diff --git a/app/composer.json b/app/composer.json index 4f2ec1d9b..f229bdbf8 100644 --- a/app/composer.json +++ b/app/composer.json @@ -5,28 +5,27 @@ "type": "project", "license": "MIT", "require": { - "php": ">=7.2", - "adodb/adodb-php": "^5.20", - "cakephp/cakephp": "4.0.*", - "cakephp/migrations": "^3.0.0", - "cakephp/plugin-installer": "^1.0", - "doctrine/dbal": "^2.9", - "josegonzalez/dotenv": "2.*", - "mobiledetect/mobiledetectlib": "2.*" + "php": ">=7.3", + "adodb/adodb-php": "^5.21", + "cakephp/cakephp": "4.2.x", + "cakephp/migrations": "^3.0", + "cakephp/plugin-installer": "^1.3", + "doctrine/dbal": "^3.1", + "mobiledetect/mobiledetectlib": "^2.8" }, "require-dev": { - "cakephp/bake": "^2.0.10", - "cakephp/cakephp-codesniffer": "^3.0", - "cakephp/debug_kit": "^4.0.6", - "phpstan/phpstan": "^0.12.18", - "phpunit/phpunit": "^8.0", + "cakephp/bake": "^2.3", + "cakephp/cakephp-codesniffer": "~4.2.0", + "cakephp/debug_kit": "^4.4", + "josegonzalez/dotenv": "^3.2", + "phpstan/phpstan": "^0.12.99", + "phpunit/phpunit": "~8.5.0 || ^9.3", "psy/psysh": "@stable" }, "suggest": { "markstory/asset_compress": "An asset compression plugin which provides file concatenation and a flexible filter system for preprocessing and minification.", "dereuromark/cakephp-ide-helper": "After baking your code, this keeps your annotations in sync with the code evolving from there on for maximum IDE and PHPStan compatibility.", - "phpunit/phpunit": "Allows automated tests to be run without system-wide install.", - "cakephp/cakephp-codesniffer": "Allows to check the code against the coding standards used in CakePHP." + "phpstan/phpstan": "PHPStan focuses on finding errors in your code without actually running it. It catches whole classes of bugs even before you write tests for the code." }, "autoload": { "psr-4": { @@ -46,8 +45,9 @@ "@test", "@cs-check" ], - "cs-check": "phpcs --colors -p --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests", - "cs-fix": "phpcbf --colors --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests", + "cs-check": "phpcs --colors -p src/ tests/", + "cs-fix": "phpcbf --colors -p src/ tests/", + "stan": "phpstan analyse src/", "test": "phpunit --colors=always" }, "prefer-stable": true, diff --git a/app/composer.lock b/app/composer.lock index 891a7509e..1f282961d 100644 --- a/app/composer.lock +++ b/app/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fd5988586d5d9d91062852addfcc0c0d", + "content-hash": "d6f8816fb1bdfe3edbc0bf4f1381a6c3", "packages": [ { "name": "adodb/adodb-php", @@ -67,72 +67,21 @@ }, "time": "2021-08-22T10:49:44+00:00" }, - { - "name": "aura/intl", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/auraphp/Aura.Intl.git", - "reference": "7fce228980b19bf4dee2d7bbd6202a69b0dde926" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/auraphp/Aura.Intl/zipball/7fce228980b19bf4dee2d7bbd6202a69b0dde926", - "reference": "7fce228980b19bf4dee2d7bbd6202a69b0dde926", - "shasum": "" - }, - "require": { - "php": "^5.6|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Aura\\Intl\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aura.Intl Contributors", - "homepage": "https://github.com/auraphp/Aura.Intl/contributors" - } - ], - "description": "The Aura Intl package provides internationalization tools, specifically message translation.", - "homepage": "https://github.com/auraphp/Aura.Intl", - "keywords": [ - "g11n", - "globalization", - "i18n", - "internationalization", - "intl", - "l10n", - "localization" - ], - "support": { - "issues": "https://github.com/auraphp/Aura.Intl/issues", - "source": "https://github.com/auraphp/Aura.Intl/tree/3.x" - }, - "time": "2017-01-20T05:00:11+00:00" - }, { "name": "cakephp/cakephp", - "version": "4.0.10", + "version": "4.2.10", "source": { "type": "git", "url": "https://github.com/cakephp/cakephp.git", - "reference": "5c6d72bc9bb1be6eec1b00b3930f9e5054ee6d76" + "reference": "ea47e927083e2f4bfaed0c404756d9852b617cad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/cakephp/zipball/5c6d72bc9bb1be6eec1b00b3930f9e5054ee6d76", - "reference": "5c6d72bc9bb1be6eec1b00b3930f9e5054ee6d76", + "url": "https://api.github.com/repos/cakephp/cakephp/zipball/ea47e927083e2f4bfaed0c404756d9852b617cad", + "reference": "ea47e927083e2f4bfaed0c404756d9852b617cad", "shasum": "" }, "require": { - "aura/intl": "^3.0.0", "cakephp/chronos": "^2.0", "composer/ca-bundle": "^1.2", "ext-intl": "*", @@ -140,6 +89,7 @@ "ext-mbstring": "*", "laminas/laminas-diactoros": "^2.2.2", "laminas/laminas-httphandlerrunner": "^1.1", + "league/container": "^3.2", "php": ">=7.2.0", "psr/http-client": "^1.0", "psr/http-server-handler": "^1.0", @@ -168,7 +118,7 @@ "cakephp/cakephp-codesniffer": "^4.0", "mikey179/vfsstream": "^1.6", "paragonie/csp-builder": "^2.3", - "phpunit/phpunit": "~8.5.0" + "phpunit/phpunit": "^8.5 || ^9.3" }, "suggest": { "ext-curl": "To enable more efficient network calls in Http\\Client.", @@ -185,6 +135,7 @@ "src/Core/functions.php", "src/Collection/functions.php", "src/I18n/functions.php", + "src/Routing/functions.php", "src/Utility/bootstrap.php" ] }, @@ -217,7 +168,7 @@ "issues": "https://github.com/cakephp/cakephp/issues", "source": "https://github.com/cakephp/cakephp" }, - "time": "2020-12-08T03:04:12+00:00" + "time": "2021-10-14T01:50:57+00:00" }, { "name": "cakephp/chronos", @@ -389,16 +340,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.2.10", + "version": "1.2.11", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "9fdb22c2e97a614657716178093cd1da90a64aa8" + "reference": "0b072d51c5a9c6f3412f7ea3ab043d6603cb2582" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/9fdb22c2e97a614657716178093cd1da90a64aa8", - "reference": "9fdb22c2e97a614657716178093cd1da90a64aa8", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/0b072d51c5a9c6f3412f7ea3ab043d6603cb2582", + "reference": "0b072d51c5a9c6f3412f7ea3ab043d6603cb2582", "shasum": "" }, "require": { @@ -410,7 +361,7 @@ "phpstan/phpstan": "^0.12.55", "psr/log": "^1.0", "symfony/phpunit-bridge": "^4.2 || ^5", - "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" + "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" }, "type": "library", "extra": { @@ -445,7 +396,80 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.2.10" + "source": "https://github.com/composer/ca-bundle/tree/1.2.11" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2021-09-25T20:32:43+00:00" + }, + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.4", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "b174585d1fe49ceed21928a945138948cb394600" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600", + "reference": "b174585d1fe49ceed21928a945138948cb394600", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4" }, "funding": [ { @@ -461,7 +485,7 @@ "type": "tidelift" } ], - "time": "2021-06-07T13:58:28+00:00" + "time": "2021-09-13T08:41:34+00:00" }, { "name": "doctrine/cache", @@ -564,34 +588,35 @@ }, { "name": "doctrine/dbal", - "version": "2.13.3", + "version": "3.1.3", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "0d7adf4cadfee6f70850e5b163e6cdd706417838" + "reference": "96b0053775a544b4a6ab47654dac0621be8b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/0d7adf4cadfee6f70850e5b163e6cdd706417838", - "reference": "0d7adf4cadfee6f70850e5b163e6cdd706417838", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/96b0053775a544b4a6ab47654dac0621be8b4cf8", + "reference": "96b0053775a544b4a6ab47654dac0621be8b4cf8", "shasum": "" }, "require": { + "composer/package-versions-deprecated": "^1.11.99", "doctrine/cache": "^1.0|^2.0", "doctrine/deprecations": "^0.5.3", "doctrine/event-manager": "^1.0", - "ext-pdo": "*", - "php": "^7.1 || ^8" + "php": "^7.3 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "9.0.0", "jetbrains/phpstorm-stubs": "2021.1", - "phpstan/phpstan": "0.12.96", - "phpunit/phpunit": "^7.5.20|^8.5|9.5.5", + "phpstan/phpstan": "0.12.99", + "phpstan/phpstan-strict-rules": "^0.12.11", + "phpunit/phpunit": "9.5.10", "psalm/plugin-phpunit": "0.16.1", "squizlabs/php_codesniffer": "3.6.0", - "symfony/cache": "^4.4", - "symfony/console": "^2.0.5|^3.0|^4.0|^5.0", + "symfony/cache": "^5.2|^6.0", + "symfony/console": "^2.0.5|^3.0|^4.0|^5.0|^6.0", "vimeo/psalm": "4.10.0" }, "suggest": { @@ -603,7 +628,7 @@ "type": "library", "autoload": { "psr-4": { - "Doctrine\\DBAL\\": "lib/Doctrine/DBAL" + "Doctrine\\DBAL\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -646,14 +671,13 @@ "queryobject", "sasql", "sql", - "sqlanywhere", "sqlite", "sqlserver", "sqlsrv" ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/2.13.3" + "source": "https://github.com/doctrine/dbal/tree/3.1.3" }, "funding": [ { @@ -669,7 +693,7 @@ "type": "tidelift" } ], - "time": "2021-09-12T19:11:48+00:00" + "time": "2021-10-02T16:15:05+00:00" }, { "name": "doctrine/deprecations", @@ -808,74 +832,18 @@ ], "time": "2020-05-29T18:28:51+00:00" }, - { - "name": "josegonzalez/dotenv", - "version": "2.1.0", - "source": { - "type": "git", - "url": "https://github.com/josegonzalez/php-dotenv.git", - "reference": "ff3461f2960737f54054dff4fef3482a2bb9682b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/josegonzalez/php-dotenv/zipball/ff3461f2960737f54054dff4fef3482a2bb9682b", - "reference": "ff3461f2960737f54054dff4fef3482a2bb9682b", - "shasum": "" - }, - "require": { - "m1/env": "2.*", - "php": ">=5.3.0" - }, - "require-dev": { - "satooshi/php-coveralls": "1.*", - "squizlabs/php_codesniffer": "2.*" - }, - "type": "library", - "autoload": { - "psr-0": { - "josegonzalez\\Dotenv": [ - "src", - "tests" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jose Diaz-Gonzalez", - "email": "dotenv@josegonzalez.com", - "homepage": "http://josediazgonzalez.com", - "role": "Maintainer" - } - ], - "description": "dotenv file parsing for PHP", - "homepage": "https://github.com/josegonzalez/php-dotenv", - "keywords": [ - "configuration", - "dotenv", - "php" - ], - "support": { - "issues": "https://github.com/josegonzalez/php-dotenv/issues", - "source": "https://github.com/josegonzalez/php-dotenv/tree/2.1.0" - }, - "time": "2017-01-03T01:04:05+00:00" - }, { "name": "laminas/laminas-diactoros", - "version": "2.7.0", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-diactoros.git", - "reference": "8b5792b7c81465efb14780c2d4787f158bd96183" + "reference": "0c26ef1d95b6d7e6e3943a243ba3dc0797227199" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/8b5792b7c81465efb14780c2d4787f158bd96183", - "reference": "8b5792b7c81465efb14780c2d4787f158bd96183", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/0c26ef1d95b6d7e6e3943a243ba3dc0797227199", + "reference": "0c26ef1d95b6d7e6e3943a243ba3dc0797227199", "shasum": "" }, "require": { @@ -961,25 +929,25 @@ "type": "community_bridge" } ], - "time": "2021-09-15T08:41:12+00:00" + "time": "2021-09-22T03:54:36+00:00" }, { "name": "laminas/laminas-httphandlerrunner", - "version": "1.4.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-httphandlerrunner.git", - "reference": "6a2dd33e4166469ade07ad1283b45924383b224b" + "reference": "5f94e55d93f756e8ad07b9049aeb3d6d84582d0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/6a2dd33e4166469ade07ad1283b45924383b224b", - "reference": "6a2dd33e4166469ade07ad1283b45924383b224b", + "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/5f94e55d93f756e8ad07b9049aeb3d6d84582d0e", + "reference": "5f94e55d93f756e8ad07b9049aeb3d6d84582d0e", "shasum": "" }, "require": { "laminas/laminas-zendframework-bridge": "^1.0", - "php": "^7.3 || ~8.0.0", + "php": "^7.3 || ~8.0.0 || ~8.1.0", "psr/http-message": "^1.0", "psr/http-message-implementation": "^1.0", "psr/http-server-handler": "^1.0" @@ -989,10 +957,10 @@ }, "require-dev": { "laminas/laminas-coding-standard": "~1.0.0", - "laminas/laminas-diactoros": "^2.1.1", - "phpunit/phpunit": "^9.3", - "psalm/plugin-phpunit": "^0.15.1", - "vimeo/psalm": "^4.6" + "laminas/laminas-diactoros": "^2.8.0", + "phpunit/phpunit": "^9.5.9", + "psalm/plugin-phpunit": "^0.16.1", + "vimeo/psalm": "^4.10.0" }, "type": "library", "extra": { @@ -1032,7 +1000,7 @@ "type": "community_bridge" } ], - "time": "2021-04-08T13:52:56+00:00" + "time": "2021-09-22T09:17:54+00:00" }, { "name": "laminas/laminas-zendframework-bridge", @@ -1097,35 +1065,47 @@ "time": "2021-09-03T17:53:30+00:00" }, { - "name": "m1/env", - "version": "2.2.0", + "name": "league/container", + "version": "3.4.1", "source": { "type": "git", - "url": "https://github.com/m1/Env.git", - "reference": "5c296e3e13450a207e12b343f3af1d7ab569f6f3" + "url": "https://github.com/thephpleague/container.git", + "reference": "84ecbc2dbecc31bd23faf759a0e329ee49abddbd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/m1/Env/zipball/5c296e3e13450a207e12b343f3af1d7ab569f6f3", - "reference": "5c296e3e13450a207e12b343f3af1d7ab569f6f3", + "url": "https://api.github.com/repos/thephpleague/container/zipball/84ecbc2dbecc31bd23faf759a0e329ee49abddbd", + "reference": "84ecbc2dbecc31bd23faf759a0e329ee49abddbd", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.0 || ^8.0", + "psr/container": "^1.0.0" }, - "require-dev": { - "phpunit/phpunit": "4.*", - "scrutinizer/ocular": "~1.1", - "squizlabs/php_codesniffer": "^2.3" + "provide": { + "psr/container-implementation": "^1.0" }, - "suggest": { - "josegonzalez/dotenv": "For loading of .env", - "m1/vars": "For loading of configs" + "replace": { + "orno/di": "~2.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0 || ^7.0", + "roave/security-advisories": "dev-latest", + "scrutinizer/ocular": "^1.8", + "squizlabs/php_codesniffer": "^3.5" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, "autoload": { "psr-4": { - "M1\\Env\\": "src" + "League\\Container\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1134,29 +1114,34 @@ ], "authors": [ { - "name": "Miles Croxford", - "email": "hello@milescroxford.com", - "homepage": "http://milescroxford.com", + "name": "Phil Bennett", + "email": "philipobenito@gmail.com", + "homepage": "http://www.philipobenito.com", "role": "Developer" } ], - "description": "Env is a lightweight library bringing .env file parser compatibility to PHP. In short - it enables you to read .env files with PHP.", - "homepage": "https://github.com/m1/Env", + "description": "A fast and intuitive dependency injection container.", + "homepage": "https://github.com/thephpleague/container", "keywords": [ - ".env", - "config", - "dotenv", - "env", - "loader", - "m1", - "parser", - "support" + "container", + "dependency", + "di", + "injection", + "league", + "provider", + "service" ], "support": { - "issues": "https://github.com/m1/Env/issues", - "source": "https://github.com/m1/Env/tree/2.2.0" + "issues": "https://github.com/thephpleague/container/issues", + "source": "https://github.com/thephpleague/container/tree/3.4.1" }, - "time": "2020-02-19T09:02:13+00:00" + "funding": [ + { + "url": "https://github.com/philipobenito", + "type": "github" + } + ], + "time": "2021-07-09T08:23:52+00:00" }, { "name": "mobiledetect/mobiledetectlib", @@ -1222,27 +1207,22 @@ }, { "name": "psr/container", - "version": "2.0.1", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "2ae37329ee82f91efadc282cc2d527fd6065a5ef" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/2ae37329ee82f91efadc282cc2d527fd6065a5ef", - "reference": "2ae37329ee82f91efadc282cc2d527fd6065a5ef", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { "php": ">=7.2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -1269,9 +1249,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.1" + "source": "https://github.com/php-fig/container/tree/1.1.1" }, - "time": "2021-03-24T13:40:57+00:00" + "time": "2021-03-05T17:36:06+00:00" }, { "name": "psr/http-client", @@ -1650,16 +1630,16 @@ }, { "name": "robmorgan/phinx", - "version": "0.12.8", + "version": "0.12.9", "source": { "type": "git", "url": "https://github.com/cakephp/phinx.git", - "reference": "d2ed1b452cc90f4cae4ea6b5976b94fb9e5ed2a2" + "reference": "5a0146a74c1bc195d1f5da86afa3b68badf7d90e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/phinx/zipball/d2ed1b452cc90f4cae4ea6b5976b94fb9e5ed2a2", - "reference": "d2ed1b452cc90f4cae4ea6b5976b94fb9e5ed2a2", + "url": "https://api.github.com/repos/cakephp/phinx/zipball/5a0146a74c1bc195d1f5da86afa3b68badf7d90e", + "reference": "5a0146a74c1bc195d1f5da86afa3b68badf7d90e", "shasum": "" }, "require": { @@ -1730,9 +1710,9 @@ ], "support": { "issues": "https://github.com/cakephp/phinx/issues", - "source": "https://github.com/cakephp/phinx/tree/0.12.8" + "source": "https://github.com/cakephp/phinx/tree/0.12.9" }, - "time": "2021-07-28T15:31:39+00:00" + "time": "2021-10-12T10:49:25+00:00" }, { "name": "symfony/config", @@ -2609,29 +2589,33 @@ }, { "name": "symfony/service-contracts", - "version": "v1.1.2", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "191afdcb5804db960d26d8566b7e9a2843cab3a0" + "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/191afdcb5804db960d26d8566b7e9a2843cab3a0", - "reference": "191afdcb5804db960d26d8566b7e9a2843cab3a0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", + "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=7.2.5", + "psr/container": "^1.1" }, "suggest": { - "psr/container": "", "symfony/service-implementation": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -2664,9 +2648,23 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v1.1.2" + "source": "https://github.com/symfony/service-contracts/tree/v2.4.0" }, - "time": "2019-05-28T07:50:59+00:00" + "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-04-01T10:43:52+00:00" }, { "name": "symfony/string", @@ -2753,28 +2751,73 @@ } ], "packages-dev": [ + { + "name": "brick/varexporter", + "version": "0.3.5", + "source": { + "type": "git", + "url": "https://github.com/brick/varexporter.git", + "reference": "05241f28dfcba2b51b11e2d750e296316ebbe518" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/varexporter/zipball/05241f28dfcba2b51b11e2d750e296316ebbe518", + "reference": "05241f28dfcba2b51b11e2d750e296316ebbe518", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.0", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^8.5 || ^9.0", + "vimeo/psalm": "4.4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\VarExporter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A powerful alternative to var_export(), which can export closures and objects without __set_state()", + "keywords": [ + "var_export" + ], + "support": { + "issues": "https://github.com/brick/varexporter/issues", + "source": "https://github.com/brick/varexporter/tree/0.3.5" + }, + "time": "2021-02-10T13:53:07+00:00" + }, { "name": "cakephp/bake", - "version": "2.2.0", + "version": "2.5.2", "source": { "type": "git", "url": "https://github.com/cakephp/bake.git", - "reference": "f1c297c4e903a15188389011b93ce46119849d01" + "reference": "bfb856afcfbc70c5cf5341669c3036a45ca15d94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/bake/zipball/f1c297c4e903a15188389011b93ce46119849d01", - "reference": "f1c297c4e903a15188389011b93ce46119849d01", + "url": "https://api.github.com/repos/cakephp/bake/zipball/bfb856afcfbc70c5cf5341669c3036a45ca15d94", + "reference": "bfb856afcfbc70c5cf5341669c3036a45ca15d94", "shasum": "" }, "require": { - "cakephp/cakephp": "^4.0", - "cakephp/twig-view": "^1.0", + "brick/varexporter": "^0.3.5", + "cakephp/cakephp": "^4.1", + "cakephp/twig-view": "^1.0.2", "php": ">=7.2" }, "require-dev": { "cakephp/cakephp-codesniffer": "^4.0", "cakephp/debug_kit": "^4.1", + "cakephp/plugin-installer": "^1.3", "phpunit/phpunit": "~8.5.0" }, "type": "cakephp-plugin", @@ -2805,28 +2848,29 @@ "issues": "https://github.com/cakephp/bake/issues", "source": "https://github.com/cakephp/bake" }, - "time": "2020-10-23T01:17:19+00:00" + "time": "2021-07-26T14:56:18+00:00" }, { "name": "cakephp/cakephp-codesniffer", - "version": "3.3.0", + "version": "4.2.4", "source": { "type": "git", "url": "https://github.com/cakephp/cakephp-codesniffer.git", - "reference": "7998a191e787fd5b68cb635d7050cb0d7b55e1a1" + "reference": "c5bb1faeebf09cd4a3604bdb0c84f7bc92dc5475" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/cakephp-codesniffer/zipball/7998a191e787fd5b68cb635d7050cb0d7b55e1a1", - "reference": "7998a191e787fd5b68cb635d7050cb0d7b55e1a1", + "url": "https://api.github.com/repos/cakephp/cakephp-codesniffer/zipball/c5bb1faeebf09cd4a3604bdb0c84f7bc92dc5475", + "reference": "c5bb1faeebf09cd4a3604bdb0c84f7bc92dc5475", "shasum": "" }, "require": { - "php": ">=5.6", - "squizlabs/php_codesniffer": "^3.0.0" + "php": ">=7.2.0", + "slevomat/coding-standard": "^6.3.6", + "squizlabs/php_codesniffer": "~3.5.5" }, "require-dev": { - "phpunit/phpunit": "<6.0" + "phpunit/phpunit": "^7.1" }, "type": "phpcodesniffer-standard", "autoload": { @@ -2856,33 +2900,33 @@ "issues": "https://github.com/cakephp/cakephp-codesniffer/issues", "source": "https://github.com/cakephp/cakephp-codesniffer" }, - "time": "2019-12-07T03:02:34+00:00" + "time": "2020-12-03T20:39:38+00:00" }, { "name": "cakephp/debug_kit", - "version": "4.2.0", + "version": "4.4.4", "source": { "type": "git", "url": "https://github.com/cakephp/debug_kit.git", - "reference": "940a0214947e85bbaa0724acfda852f64831f04f" + "reference": "10d7d9ba36945844211f1d8763e59618917e1784" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/debug_kit/zipball/940a0214947e85bbaa0724acfda852f64831f04f", - "reference": "940a0214947e85bbaa0724acfda852f64831f04f", + "url": "https://api.github.com/repos/cakephp/debug_kit/zipball/10d7d9ba36945844211f1d8763e59618917e1784", + "reference": "10d7d9ba36945844211f1d8763e59618917e1784", "shasum": "" }, "require": { - "cakephp/cakephp": "^4.0", + "cakephp/cakephp": "^4.2.0", "cakephp/chronos": "^2.0", - "composer/composer": "^1.3", + "composer/composer": "^1.3 | ^2.0", "jdorn/sql-formatter": "^1.2", "php": ">=7.2" }, "require-dev": { "cakephp/authorization": "^2.0", "cakephp/cakephp-codesniffer": "^4.0", - "phpunit/phpunit": "^8.0" + "phpunit/phpunit": "~8.5.0 | ^9.3" }, "suggest": { "ext-pdo_sqlite": "DebugKit needs to store panel data in a database. SQLite is simple and easy to use." @@ -2922,20 +2966,20 @@ "issues": "https://github.com/cakephp/debug_kit/issues", "source": "https://github.com/cakephp/debug_kit" }, - "time": "2020-06-10T01:37:18+00:00" + "time": "2021-09-12T20:06:14+00:00" }, { "name": "cakephp/twig-view", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/cakephp/twig-view.git", - "reference": "668dd6aee43dd616b1e83cb9ba166f094c10fbce" + "reference": "14df50360b809a171d0688020fbdfe513763f89b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/twig-view/zipball/668dd6aee43dd616b1e83cb9ba166f094c10fbce", - "reference": "668dd6aee43dd616b1e83cb9ba166f094c10fbce", + "url": "https://api.github.com/repos/cakephp/twig-view/zipball/14df50360b809a171d0688020fbdfe513763f89b", + "reference": "14df50360b809a171d0688020fbdfe513763f89b", "shasum": "" }, "require": { @@ -2985,43 +3029,42 @@ "issues": "https://github.com/cakephp/twig-view/issues", "source": "https://github.com/cakephp/twig-view" }, - "time": "2020-12-13T19:57:31+00:00" + "time": "2021-09-17T14:07:52+00:00" }, { "name": "composer/composer", - "version": "1.10.22", + "version": "2.1.9", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "28c9dfbe2351635961f670773e8d7b17bc5eda25" + "reference": "e558c88f28d102d497adec4852802c0dc14c7077" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/28c9dfbe2351635961f670773e8d7b17bc5eda25", - "reference": "28c9dfbe2351635961f670773e8d7b17bc5eda25", + "url": "https://api.github.com/repos/composer/composer/zipball/e558c88f28d102d497adec4852802c0dc14c7077", + "reference": "e558c88f28d102d497adec4852802c0dc14c7077", "shasum": "" }, "require": { "composer/ca-bundle": "^1.0", - "composer/semver": "^1.0", + "composer/metadata-minifier": "^1.0", + "composer/semver": "^3.0", "composer/spdx-licenses": "^1.2", - "composer/xdebug-handler": "^1.1", - "justinrainbow/json-schema": "^5.2.10", + "composer/xdebug-handler": "^2.0", + "justinrainbow/json-schema": "^5.2.11", "php": "^5.3.2 || ^7.0 || ^8.0", "psr/log": "^1.0", + "react/promise": "^1.2 || ^2.7", "seld/jsonlint": "^1.4", "seld/phar-utils": "^1.0", - "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/finder": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0" - }, - "conflict": { - "symfony/console": "2.8.38" + "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/filesystem": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0" }, "require-dev": { "phpspec/prophecy": "^1.10", - "symfony/phpunit-bridge": "^4.2" + "symfony/phpunit-bridge": "^4.2 || ^5.0 || ^6.0" }, "suggest": { "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", @@ -3034,7 +3077,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -3050,12 +3093,12 @@ { "name": "Nils Adermann", "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" + "homepage": "https://www.naderman.de" }, { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "homepage": "https://seld.be" } ], "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.", @@ -3066,9 +3109,78 @@ "package" ], "support": { - "irc": "irc://irc.freenode.org/composer", + "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/composer/issues", - "source": "https://github.com/composer/composer/tree/1.10.22" + "source": "https://github.com/composer/composer/tree/2.1.9" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2021-10-05T07:47:38+00:00" + }, + { + "name": "composer/metadata-minifier", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/composer/metadata-minifier.git", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/metadata-minifier/zipball/c549d23829536f0d0e984aaabbf02af91f443207", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "composer/composer": "^2", + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\MetadataMinifier\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Small utility library that handles metadata minification and expansion.", + "keywords": [ + "composer", + "compression" + ], + "support": { + "issues": "https://github.com/composer/metadata-minifier/issues", + "source": "https://github.com/composer/metadata-minifier/tree/1.0.0" }, "funding": [ { @@ -3084,32 +3196,33 @@ "type": "tidelift" } ], - "time": "2021-04-27T11:10:45+00:00" + "time": "2021-04-07T13:37:33+00:00" }, { "name": "composer/semver", - "version": "1.7.2", + "version": "3.2.5", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "647490bbcaf7fc4891c58f47b825eb99d19c377a" + "reference": "31f3ea725711245195f62e54ffa402d8ef2fdba9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/647490bbcaf7fc4891c58f47b825eb99d19c377a", - "reference": "647490bbcaf7fc4891c58f47b825eb99d19c377a", + "url": "https://api.github.com/repos/composer/semver/zipball/31f3ea725711245195f62e54ffa402d8ef2fdba9", + "reference": "31f3ea725711245195f62e54ffa402d8ef2fdba9", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5" + "phpstan/phpstan": "^0.12.54", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -3148,7 +3261,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/1.7.2" + "source": "https://github.com/composer/semver/tree/3.2.5" }, "funding": [ { @@ -3164,7 +3277,7 @@ "type": "tidelift" } ], - "time": "2020-12-03T15:47:16+00:00" + "time": "2021-05-24T12:41:47+00:00" }, { "name": "composer/spdx-licenses", @@ -3247,21 +3360,21 @@ }, { "name": "composer/xdebug-handler", - "version": "1.4.6", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "f27e06cd9675801df441b3656569b328e04aa37c" + "reference": "84674dd3a7575ba617f5a76d7e9e29a7d3891339" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f27e06cd9675801df441b3656569b328e04aa37c", - "reference": "f27e06cd9675801df441b3656569b328e04aa37c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/84674dd3a7575ba617f5a76d7e9e29a7d3891339", + "reference": "84674dd3a7575ba617f5a76d7e9e29a7d3891339", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1.0" + "psr/log": "^1 || ^2 || ^3" }, "require-dev": { "phpstan/phpstan": "^0.12.55", @@ -3291,7 +3404,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/1.4.6" + "source": "https://github.com/composer/xdebug-handler/tree/2.0.2" }, "funding": [ { @@ -3307,7 +3420,77 @@ "type": "tidelift" } ], - "time": "2021-03-25T17:01:18+00:00" + "time": "2021-07-31T17:03:58+00:00" + }, + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v0.7.1", + "source": { + "type": "git", + "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", + "reference": "fe390591e0241955f22eb9ba327d137e501c771c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/fe390591e0241955f22eb9ba327d137e501c771c", + "reference": "fe390591e0241955f22eb9ba327d137e501c771c", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.0 || ^3.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "phpcompatibility/php-compatibility": "^9.0", + "sensiolabs/security-checker": "^4.1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "support": { + "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + }, + "time": "2020-12-07T18:04:37+00:00" }, { "name": "doctrine/instantiator", @@ -3496,6 +3679,63 @@ }, "time": "2014-01-12T16:20:24+00:00" }, + { + "name": "josegonzalez/dotenv", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/josegonzalez/php-dotenv.git", + "reference": "f19174d9d7213a6c20e8e5e268aa7dd042d821ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/josegonzalez/php-dotenv/zipball/f19174d9d7213a6c20e8e5e268aa7dd042d821ca", + "reference": "f19174d9d7213a6c20e8e5e268aa7dd042d821ca", + "shasum": "" + }, + "require": { + "m1/env": "2.*", + "php": ">=5.5.0" + }, + "require-dev": { + "php-mock/php-mock-phpunit": "^1.1", + "satooshi/php-coveralls": "1.*", + "squizlabs/php_codesniffer": "2.*" + }, + "type": "library", + "autoload": { + "psr-0": { + "josegonzalez\\Dotenv": [ + "src", + "tests" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jose Diaz-Gonzalez", + "email": "dotenv@josegonzalez.com", + "homepage": "http://josediazgonzalez.com", + "role": "Maintainer" + } + ], + "description": "dotenv file parsing for PHP", + "homepage": "https://github.com/josegonzalez/php-dotenv", + "keywords": [ + "configuration", + "dotenv", + "php" + ], + "support": { + "issues": "https://github.com/josegonzalez/php-dotenv/issues", + "source": "https://github.com/josegonzalez/php-dotenv/tree/master" + }, + "time": "2017-09-19T15:49:58+00:00" + }, { "name": "justinrainbow/json-schema", "version": "5.2.11", @@ -3566,6 +3806,68 @@ }, "time": "2021-07-22T09:24:00+00:00" }, + { + "name": "m1/env", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/m1/Env.git", + "reference": "5c296e3e13450a207e12b343f3af1d7ab569f6f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/m1/Env/zipball/5c296e3e13450a207e12b343f3af1d7ab569f6f3", + "reference": "5c296e3e13450a207e12b343f3af1d7ab569f6f3", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "scrutinizer/ocular": "~1.1", + "squizlabs/php_codesniffer": "^2.3" + }, + "suggest": { + "josegonzalez/dotenv": "For loading of .env", + "m1/vars": "For loading of configs" + }, + "type": "library", + "autoload": { + "psr-4": { + "M1\\Env\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Miles Croxford", + "email": "hello@milescroxford.com", + "homepage": "http://milescroxford.com", + "role": "Developer" + } + ], + "description": "Env is a lightweight library bringing .env file parser compatibility to PHP. In short - it enables you to read .env files with PHP.", + "homepage": "https://github.com/m1/Env", + "keywords": [ + ".env", + "config", + "dotenv", + "env", + "loader", + "m1", + "parser", + "support" + ], + "support": { + "issues": "https://github.com/m1/Env/issues", + "source": "https://github.com/m1/Env/tree/2.2.0" + }, + "time": "2020-02-19T09:02:13+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.10.2", @@ -3626,16 +3928,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.12.0", + "version": "v4.13.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6608f01670c3cc5079e18c1dab1104e002579143" + "reference": "50953a2691a922aa1769461637869a0a2faa3f53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6608f01670c3cc5079e18c1dab1104e002579143", - "reference": "6608f01670c3cc5079e18c1dab1104e002579143", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53", + "reference": "50953a2691a922aa1769461637869a0a2faa3f53", "shasum": "" }, "require": { @@ -3676,9 +3978,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.12.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0" }, - "time": "2021-07-21T10:44:31+00:00" + "time": "2021-09-20T12:20:58+00:00" }, { "name": "phar-io/manifest", @@ -3902,16 +4204,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f" + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30f38bffc6f24293dadd1823936372dfa9e86e2f", - "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae", + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae", "shasum": "" }, "require": { @@ -3946,9 +4248,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1" }, - "time": "2021-09-17T15:28:14+00:00" + "time": "2021-10-02T14:08:47+00:00" }, { "name": "phpspec/prophecy", @@ -4017,6 +4319,59 @@ }, "time": "2021-09-10T09:02:12+00:00" }, + { + "name": "phpstan/phpdoc-parser", + "version": "0.4.9", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "98a088b17966bdf6ee25c8a4b634df313d8aa531" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/98a088b17966bdf6ee25c8a4b634df313d8aa531", + "reference": "98a088b17966bdf6ee25c8a4b634df313d8aa531", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "consistence/coding-standard": "^3.5", + "ergebnis/composer-normalize": "^2.0.2", + "jakub-onderka/php-parallel-lint": "^0.9.2", + "phing/phing": "^2.16.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.26", + "phpstan/phpstan-strict-rules": "^0.12", + "phpunit/phpunit": "^6.3", + "slevomat/coding-standard": "^4.7.2", + "symfony/process": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.4-dev" + } + }, + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/master" + }, + "time": "2020-08-03T20:32:43+00:00" + }, { "name": "phpstan/phpstan", "version": "0.12.99", @@ -4083,40 +4438,44 @@ }, { "name": "phpunit/php-code-coverage", - "version": "7.0.15", + "version": "9.2.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "819f92bba8b001d4363065928088de22f25a3a48" + "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/819f92bba8b001d4363065928088de22f25a3a48", - "reference": "819f92bba8b001d4363065928088de22f25a3a48", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218", + "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-xmlwriter": "*", - "php": ">=7.2", - "phpunit/php-file-iterator": "^2.0.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.1.3 || ^4.0", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^4.2.2", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1.3" + "nikic/php-parser": "^4.12.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^8.2.2" + "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-xdebug": "^2.7.2" + "ext-pcov": "*", + "ext-xdebug": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "7.0-dev" + "dev-master": "9.2-dev" } }, "autoload": { @@ -4144,7 +4503,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7" }, "funding": [ { @@ -4152,32 +4511,32 @@ "type": "github" } ], - "time": "2021-07-26T12:20:09+00:00" + "time": "2021-09-17T05:39:03+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "2.0.4", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "28af674ff175d0768a5a978e6de83f697d4a7f05" + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/28af674ff175d0768a5a978e6de83f697d4a7f05", - "reference": "28af674ff175d0768a5a978e6de83f697d4a7f05", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -4204,7 +4563,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.4" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5" }, "funding": [ { @@ -4212,28 +4571,40 @@ "type": "github" } ], - "time": "2021-07-19T06:46:01+00:00" + "time": "2020-09-28T05:57:25+00:00" }, { - "name": "phpunit/php-text-template", - "version": "1.2.1", + "name": "phpunit/php-invoker", + "version": "3.1.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.3" }, - "type": "library", - "autoload": { - "classmap": [ + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ "src/" ] }, @@ -4248,41 +4619,47 @@ "role": "lead" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ - "template" + "process" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" }, - "time": "2015-06-21T13:50:34+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" }, { - "name": "phpunit/php-timer", - "version": "2.1.3", + "name": "phpunit/php-text-template", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -4301,14 +4678,14 @@ "role": "lead" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "timer" + "template" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" }, "funding": [ { @@ -4316,33 +4693,32 @@ "type": "github" } ], - "time": "2020-11-30T08:20:02+00:00" + "time": "2020-10-26T05:33:50+00:00" }, { - "name": "phpunit/php-token-stream", - "version": "4.0.4", + "name": "phpunit/php-timer", + "version": "5.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/a853a0e183b9db7eed023d7933a858fa1c8d25a3", - "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": "^7.3 || ^8.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -4357,17 +4733,18 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "tokenizer" + "timer" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master" + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" }, "funding": [ { @@ -4375,21 +4752,20 @@ "type": "github" } ], - "abandoned": true, - "time": "2020-08-04T08:28:15+00:00" + "time": "2020-10-26T13:16:10+00:00" }, { "name": "phpunit/phpunit", - "version": "8.5.20", + "version": "9.5.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "9deefba183198398a09b927a6ac6bc1feb0b7b70" + "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9deefba183198398a09b927a6ac6bc1feb0b7b70", - "reference": "9deefba183198398a09b927a6ac6bc1feb0b7b70", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a", + "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a", "shasum": "" }, "require": { @@ -4400,32 +4776,35 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.0", + "myclabs/deep-copy": "^1.10.1", "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", - "php": ">=7.2", - "phpspec/prophecy": "^1.10.3", - "phpunit/php-code-coverage": "^7.0.12", - "phpunit/php-file-iterator": "^2.0.4", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1.2", - "sebastian/comparator": "^3.0.2", - "sebastian/diff": "^3.0.2", - "sebastian/environment": "^4.2.3", - "sebastian/exporter": "^3.1.2", - "sebastian/global-state": "^3.0.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0.1", - "sebastian/type": "^1.1.3", - "sebastian/version": "^2.0.1" + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.7", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^2.3.4", + "sebastian/version": "^3.0.2" }, "require-dev": { - "ext-pdo": "*" + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" }, "suggest": { "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0.0" + "ext-xdebug": "*" }, "bin": [ "phpunit" @@ -4433,12 +4812,15 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "8.5-dev" + "dev-master": "9.5-dev" } }, "autoload": { "classmap": [ "src/" + ], + "files": [ + "src/Framework/Assert/Functions.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -4461,7 +4843,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.20" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10" }, "funding": [ { @@ -4473,20 +4855,20 @@ "type": "github" } ], - "time": "2021-08-31T06:44:38+00:00" + "time": "2021-09-25T07:38:51+00:00" }, { "name": "psy/psysh", - "version": "v0.10.8", + "version": "v0.10.9", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "e4573f47750dd6c92dca5aee543fa77513cbd8d3" + "reference": "01281336c4ae557fe4a994544f30d3a1bc204375" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/e4573f47750dd6c92dca5aee543fa77513cbd8d3", - "reference": "e4573f47750dd6c92dca5aee543fa77513cbd8d3", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/01281336c4ae557fe4a994544f30d3a1bc204375", + "reference": "01281336c4ae557fe4a994544f30d3a1bc204375", "shasum": "" }, "require": { @@ -4546,34 +4928,196 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.10.8" + "source": "https://github.com/bobthecow/psysh/tree/v0.10.9" + }, + "time": "2021-10-10T13:37:39+00:00" + }, + { + "name": "react/promise", + "version": "v2.8.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4" }, - "time": "2021-04-10T16:23:39+00:00" + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4", + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v2.8.0" + }, + "time": "2020-05-12T15:16:56+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.2", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -4595,7 +5139,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" }, "funding": [ { @@ -4603,34 +5147,34 @@ "type": "github" } ], - "time": "2020-11-30T08:15:22+00:00" + "time": "2020-09-28T05:30:19+00:00" }, { "name": "sebastian/comparator", - "version": "3.0.3", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" + "reference": "55f4261989e546dc112258c7a75935a81a7ce382" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382", "shasum": "" }, "require": { - "php": ">=7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -4669,7 +5213,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" }, "funding": [ { @@ -4677,33 +5221,90 @@ "type": "github" } ], - "time": "2020-11-30T08:04:30+00:00" + "time": "2020-10-26T15:49:45+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" }, { "name": "sebastian/diff", - "version": "3.0.3", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -4735,7 +5336,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" }, "funding": [ { @@ -4743,27 +5344,27 @@ "type": "github" } ], - "time": "2020-11-30T07:59:04+00:00" + "time": "2020-10-26T13:10:38+00:00" }, { "name": "sebastian/environment", - "version": "4.2.4", + "version": "5.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" + "reference": "388b6ced16caa751030f6a69e588299fa09200ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^7.5" + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-posix": "*" @@ -4771,7 +5372,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -4798,7 +5399,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" }, "funding": [ { @@ -4806,34 +5407,34 @@ "type": "github" } ], - "time": "2020-11-30T07:53:42+00:00" + "time": "2020-09-28T05:52:38+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.3", + "version": "4.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e" + "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e", - "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65", + "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65", "shasum": "" }, "require": { - "php": ">=7.0", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -4875,7 +5476,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3" }, "funding": [ { @@ -4883,30 +5484,30 @@ "type": "github" } ], - "time": "2020-11-30T07:47:53+00:00" + "time": "2020-09-28T05:24:23+00:00" }, { "name": "sebastian/global-state", - "version": "3.0.1", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b" + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/474fb9edb7ab891665d3bfc6317f42a0a150454b", - "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49", + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49", "shasum": "" }, "require": { - "php": ">=7.2", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^8.0" + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-uopz": "*" @@ -4914,7 +5515,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -4939,7 +5540,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3" }, "funding": [ { @@ -4947,34 +5548,91 @@ "type": "github" } ], - "time": "2020-11-30T07:43:24+00:00" + "time": "2021-06-11T13:31:12+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" }, { "name": "sebastian/object-enumerator", - "version": "3.0.4", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { - "php": ">=7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -4996,7 +5654,7 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" }, "funding": [ { @@ -5004,32 +5662,32 @@ "type": "github" } ], - "time": "2020-11-30T07:40:27+00:00" + "time": "2020-10-26T13:12:34+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.2", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -5051,7 +5709,7 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" }, "funding": [ { @@ -5059,32 +5717,32 @@ "type": "github" } ], - "time": "2020-11-30T07:37:18+00:00" + "time": "2020-10-26T13:14:26+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.1", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -5114,7 +5772,7 @@ "homepage": "http://www.github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" }, "funding": [ { @@ -5122,29 +5780,32 @@ "type": "github" } ], - "time": "2020-11-30T07:34:24+00:00" + "time": "2020-10-26T13:17:30+00:00" }, { "name": "sebastian/resource-operations", - "version": "2.0.2", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -5166,7 +5827,7 @@ "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" }, "funding": [ { @@ -5174,32 +5835,32 @@ "type": "github" } ], - "time": "2020-11-30T07:30:19+00:00" + "time": "2020-09-28T06:45:17+00:00" }, { "name": "sebastian/type", - "version": "1.1.4", + "version": "2.3.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4" + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4", - "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.2" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.3-dev" } }, "autoload": { @@ -5222,7 +5883,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" + "source": "https://github.com/sebastianbergmann/type/tree/2.3.4" }, "funding": [ { @@ -5230,29 +5891,29 @@ "type": "github" } ], - "time": "2020-11-30T07:25:11+00:00" + "time": "2021-06-15T12:49:02+00:00" }, { "name": "sebastian/version", - "version": "2.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -5275,9 +5936,15 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/master" + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" }, - "time": "2016-10-03T07:35:21+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" }, { "name": "seld/jsonlint", @@ -5390,18 +6057,79 @@ }, "time": "2021-08-19T21:01:38+00:00" }, + { + "name": "slevomat/coding-standard", + "version": "6.4.1", + "source": { + "type": "git", + "url": "https://github.com/slevomat/coding-standard.git", + "reference": "696dcca217d0c9da2c40d02731526c1e25b65346" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/696dcca217d0c9da2c40d02731526c1e25b65346", + "reference": "696dcca217d0c9da2c40d02731526c1e25b65346", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7", + "php": "^7.1 || ^8.0", + "phpstan/phpdoc-parser": "0.4.5 - 0.4.9", + "squizlabs/php_codesniffer": "^3.5.6" + }, + "require-dev": { + "phing/phing": "2.16.3", + "php-parallel-lint/php-parallel-lint": "1.2.0", + "phpstan/phpstan": "0.12.48", + "phpstan/phpstan-deprecation-rules": "0.12.5", + "phpstan/phpstan-phpunit": "0.12.16", + "phpstan/phpstan-strict-rules": "0.12.5", + "phpunit/phpunit": "7.5.20|8.5.5|9.4.0" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-master": "6.x-dev" + } + }, + "autoload": { + "psr-4": { + "SlevomatCodingStandard\\": "SlevomatCodingStandard" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "support": { + "issues": "https://github.com/slevomat/coding-standard/issues", + "source": "https://github.com/slevomat/coding-standard/tree/6.4.1" + }, + "funding": [ + { + "url": "https://github.com/kukulich", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard", + "type": "tidelift" + } + ], + "time": "2020-10-05T12:39:37+00:00" + }, { "name": "squizlabs/php_codesniffer", - "version": "3.6.0", + "version": "3.5.8", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ffced0d2c8fa8e6cdc4d695a743271fab6c38625" + "reference": "9d583721a7157ee997f235f327de038e7ea6dac4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ffced0d2c8fa8e6cdc4d695a743271fab6c38625", - "reference": "ffced0d2c8fa8e6cdc4d695a743271fab6c38625", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4", + "reference": "9d583721a7157ee997f235f327de038e7ea6dac4", "shasum": "" }, "require": { @@ -5444,7 +6172,7 @@ "source": "https://github.com/squizlabs/PHP_CodeSniffer", "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" }, - "time": "2021-04-09T00:54:41+00:00" + "time": "2020-10-23T02:01:07+00:00" }, { "name": "symfony/finder", @@ -5572,16 +6300,16 @@ }, { "name": "symfony/var-dumper", - "version": "v5.3.7", + "version": "v5.3.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "3ad5af4aed07d0a0201bbcfc42658fe6c5b2fb8f" + "reference": "eaaea4098be1c90c8285543e1356a09c8aa5c8da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/3ad5af4aed07d0a0201bbcfc42658fe6c5b2fb8f", - "reference": "3ad5af4aed07d0a0201bbcfc42658fe6c5b2fb8f", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/eaaea4098be1c90c8285543e1356a09c8aa5c8da", + "reference": "eaaea4098be1c90c8285543e1356a09c8aa5c8da", "shasum": "" }, "require": { @@ -5640,7 +6368,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.3.7" + "source": "https://github.com/symfony/var-dumper/tree/v5.3.8" }, "funding": [ { @@ -5656,7 +6384,7 @@ "type": "tidelift" } ], - "time": "2021-08-04T23:19:25+00:00" + "time": "2021-09-24T15:59:58+00:00" }, { "name": "theseer/tokenizer", @@ -5924,7 +6652,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=7.2" + "php": ">=7.3" }, "platform-dev": [], "plugin-api-version": "2.1.0" diff --git a/app/config/app.php b/app/config/app.php index 9f35ce8bd..7fad48a61 100644 --- a/app/config/app.php +++ b/app/config/app.php @@ -42,7 +42,8 @@ * Development Mode: * true: Errors and warnings shown. */ - 'debug' => filter_var(env('DEBUG', false), FILTER_VALIDATE_BOOLEAN), +// 'debug' => filter_var(env('DEBUG', false), FILTER_VALIDATE_BOOLEAN), + 'debug' => true, /* * Configure basic information about the application. diff --git a/app/config/bootstrap.php b/app/config/bootstrap.php index b47318334..5709bada3 100644 --- a/app/config/bootstrap.php +++ b/app/config/bootstrap.php @@ -257,6 +257,6 @@ * Debug Kit should not be installed on a production system */ //if (Configure::read('debug')) { -// This mucks with CSS, maybe just remove it entirely? +//// This mucks with CSS, maybe just remove it entirely? // Plugin::load('DebugKit', ['bootstrap' => true]); //} \ No newline at end of file diff --git a/app/src/Application.php b/app/src/Application.php index 987dc7754..3e3c914df 100644 --- a/app/src/Application.php +++ b/app/src/Application.php @@ -14,13 +14,12 @@ */ namespace App; -use Cake\Core\Configure; use Cake\Error\Middleware\ErrorHandlerMiddleware; use Cake\Http\BaseApplication; use Cake\Http\Middleware\CsrfProtectionMiddleware; +use Cake\Http\Middleware\BodyParserMiddleware; use Cake\Routing\Middleware\AssetMiddleware; use Cake\Routing\Middleware\RoutingMiddleware; -use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; /** @@ -37,17 +36,17 @@ class Application extends BaseApplication { * @return \Cake\Http\MiddlewareQueue The updated middleware queue. */ - public function middleware($middlewareQueue): \Cake\Http\MiddlewareQueue { + public function middleware(\Cake\Http\MiddlewareQueue $middlewareQueue): \Cake\Http\MiddlewareQueue { // loading csrf Middleware $csrf = new CsrfProtectionMiddleware(['cookieName' => 'matchCsrfToken']); - $csrf->whitelistCallback(function (ServerRequestInterface $request) { + $csrf->skipCheckCallback(function (ServerRequestInterface $request) { // skip controllers $skipedControllers = ['TierApi']; // Controllers list if(in_array($request->getParam('controller'), $skipedControllers, true)) { return true; } // skip debugkit - if($request->getParam('plugin') == 'DebugKit') { + if($request->getParam('plugin') === 'DebugKit') { return true; } return $request; @@ -63,6 +62,12 @@ public function middleware($middlewareQueue): \Cake\Http\MiddlewareQueue { // Add routing middleware. ->add(new RoutingMiddleware($this)) + + // https://book.cakephp.org/4/en/controllers/middleware.html#body-parser-middleware + // Enable requests decoding into an array that is available via $request->getParsedData() + // and $request->getData() + // // only JSON will be parsed + ->add(new BodyParserMiddleware()) // Enable CSRF protection using Cake v3.5+ approach. // Initially, we use the default options, except a different CSRF cookie diff --git a/app/src/Auth/EnvAuthenticate.php b/app/src/Auth/EnvAuthenticate.php index 3640b4599..fc872719b 100644 --- a/app/src/Auth/EnvAuthenticate.php +++ b/app/src/Auth/EnvAuthenticate.php @@ -33,7 +33,7 @@ use Cake\Core\InstanceConfigTrait; use Cake\Http\ServerRequest; use Cake\Http\Response; -use Cake\ORM\TableRegistry; +use Cake\Datasource\FactoryLocator; use Cake\Log\Log; diff --git a/app/src/Command/BulkLoadCommand.php b/app/src/Command/BulkLoadCommand.php index ec29df1b3..b7bbcb7b8 100644 --- a/app/src/Command/BulkLoadCommand.php +++ b/app/src/Command/BulkLoadCommand.php @@ -33,10 +33,10 @@ use Cake\Console\Arguments; use Cake\Console\Command; use Cake\Console\CommandRunner; +use Cake\ORM\Locator\LocatorAwareTrait; use Cake\Console\ConsoleIo; use Cake\Console\ConsoleOptionParser; use Cake\Datasource\Exception\RecordNotFoundException; -use Cake\ORM\TableRegistry; use Cake\Utility\Hash; use Cake\Utility\Security; @@ -74,7 +74,7 @@ public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionPar public function execute(Arguments $args, ConsoleIo $io) { // Verify that we have a valid matchgrid - $Matchgrids = TableRegistry::get('Matchgrids'); + $Matchgrids = $this->getTableLocator()->get('Matchgrids'); try { $mg = $Matchgrids->getMatchgridConfig($args->getOption('matchgrid-id')); @@ -85,7 +85,7 @@ public function execute(Arguments $args, ConsoleIo $io) { } // Pull the list of Systems of Record so we can validate the inbound records - $SoR = TableRegistry::get('SystemsOfRecord'); + $SoR = $this->getTableLocator()->get('SystemsOfRecord'); $sors = $SoR->find('list', ['keyField' => 'label', 'valueField' => 'id']) ->where(['matchgrid_id' => $mg->id]) diff --git a/app/src/Command/DatabaseCommand.php b/app/src/Command/DatabaseCommand.php index c72238f6e..89d8f4570 100644 --- a/app/src/Command/DatabaseCommand.php +++ b/app/src/Command/DatabaseCommand.php @@ -221,7 +221,7 @@ public function execute(Arguments $args, ConsoleIo $io) { $toSql = $schema->toSql($conn->getDatabasePlatform()); // SchemaManager provides info about the database - $sm = $conn->getSchemaManager(); + $sm = $conn->createSchemaManager(); // The is the current database representation $curSchema = $sm->createSchema(); @@ -258,7 +258,7 @@ public function execute(Arguments $args, ConsoleIo $io) { $io->out("Skipping sequence drop"); } else { if($doSQL) { - $stmt = $conn->query($sql); + $stmt = $conn->executeQuery($sql); // $stmt just returns the query string so we don't bother examining it } } diff --git a/app/src/Command/SetupCommand.php b/app/src/Command/SetupCommand.php index e40d3dfc9..ecf59c7d2 100644 --- a/app/src/Command/SetupCommand.php +++ b/app/src/Command/SetupCommand.php @@ -35,7 +35,6 @@ use Cake\Console\CommandRunner; use Cake\Console\ConsoleIo; use Cake\Console\ConsoleOptionParser; -use Cake\ORM\TableRegistry; use Cake\Utility\Security; use \App\Lib\Enum\PermissionEnum; @@ -105,7 +104,7 @@ public function execute(Arguments $args, ConsoleIo $io) { // Create the initial admin permission $io->out(__('match.cmd.se.admin')); - $permissionsTable = TableRegistry::get('Permissions'); + $permissionsTable = $this->getTableLocator()->get('Permissions'); $permission = $permissionsTable->newEmptyEntity(); $permission->username = $user; @@ -122,7 +121,7 @@ public function execute(Arguments $args, ConsoleIo $io) { $targetVersion = rtrim(file_get_contents($versionFile)); - $metaTable = TableRegistry::get('Meta'); + $metaTable = $this->getTableLocator()->get('Meta'); $metaTable->setUpgradeVersion($targetVersion, true); // Write out the salt file diff --git a/app/src/Controller/AppController.php b/app/src/Controller/AppController.php index 22127ad8b..d42740fc4 100644 --- a/app/src/Controller/AppController.php +++ b/app/src/Controller/AppController.php @@ -33,7 +33,6 @@ use Cake\Datasource\Exception; use Cake\Datasource\Exception\RecordNotFoundException; use Cake\Event\EventInterface; -use Cake\ORM\TableRegistry; use InvalidArgumentException; class AppController extends Controller { @@ -217,7 +216,7 @@ protected function getPrimaryLink(bool $lookup=false) { // Look up the link value to find the related entity $linkModelName = $this->$modelsName->getPrimaryLinkTableName(); - $linkModel = TableRegistry::get($linkModelName); + $linkModel = $this->getTableLocator()->get($linkModelName); $this->set('vv_primary_link_model', $linkModelName); $this->set('vv_primary_link_obj', $linkModel->findById($this->cur_pl['linkvalue'])->firstOrFail()); diff --git a/app/src/Controller/Component/AuthorizationComponent.php b/app/src/Controller/Component/AuthorizationComponent.php index 21e915b0c..5ba649930 100644 --- a/app/src/Controller/Component/AuthorizationComponent.php +++ b/app/src/Controller/Component/AuthorizationComponent.php @@ -30,13 +30,13 @@ namespace App\Controller\Component; use Cake\Controller\Component; -use Cake\ORM\TableRegistry; +use Cake\Datasource\FactoryLocator; use \App\Lib\Enum\PermissionEnum; class AuthorizationComponent extends Component { // Cached copy of permissions, by username protected $userPermissions = null; - + /** * Class initializations. * @@ -45,8 +45,6 @@ class AuthorizationComponent extends Component { public function initialize(array $config): void { parent::initialize($config); - - $this->Permissions = TableRegistry::get('Permissions'); } /** @@ -70,6 +68,7 @@ protected function getPermissions($username) { ]; // Pull the permissions from the database + $this->Permissions = FactoryLocator::get('Table')->get('Permissions'); $perms = $this->Permissions->findForUser($username); foreach($perms as $mgid => $p) { diff --git a/app/src/Controller/MatchgridRecordsController.php b/app/src/Controller/MatchgridRecordsController.php index 28db151d4..72c04d996 100644 --- a/app/src/Controller/MatchgridRecordsController.php +++ b/app/src/Controller/MatchgridRecordsController.php @@ -30,7 +30,6 @@ namespace App\Controller; use Cake\Log\Log; -use Cake\ORM\TableRegistry; use Cake\Utility\Hash; use Cake\Event\EventInterface; @@ -61,8 +60,7 @@ public function initialize(): void { // the table. We also can't manually call setMatchgrid() (or really anything) // since that will trigger the instantiation of the model. - $Matchgrids = TableRegistry::get('Matchgrids'); - $obj = null; + $Matchgrids = $this->getTableLocator()->get('Matchgrids'); // Note we allow matchgrid_id to be asserted by *all* actions, since the // record ID is specific to the matchgrid. @@ -73,7 +71,7 @@ public function initialize(): void { } // Set the table name and Matchgrid ID for MatchgridRecordsTable - TableRegistry::getTableLocator()->setConfig('MatchgridRecords', [ + $this->getTableLocator()->setConfig('MatchgridRecords', [ 'table' => $obj->prefixed_table_name, 'matchgrid_id' => $this->request->getQuery('matchgrid_id') ]); @@ -166,7 +164,7 @@ public function beforeRender(EventInterface $event) { // Pull the Matchgrid configuration in order to pass the attribute configuration. // We can almost, but not quite, use autoViewVars for this, since we need the // attribute grouping to create the full api name. - $Matchgrids = TableRegistry::get('Matchgrids'); + $Matchgrids = $this->getTableLocator()->get('Matchgrids'); try { $mg = $Matchgrids->getMatchgridConfig($this->cur_mg->id); diff --git a/app/src/Controller/RuleAttributesController.php b/app/src/Controller/RuleAttributesController.php index cbe510e9f..9fb67e988 100644 --- a/app/src/Controller/RuleAttributesController.php +++ b/app/src/Controller/RuleAttributesController.php @@ -34,9 +34,7 @@ class RuleAttributesController extends StandardController { 'order' => [ 'Attributes.name' => 'asc' ], -// 'sortWhitelist' is renamed 'sortableFields' in Cake 4.1 -// 'sortableFields' => [ - 'sortWhitelist' => [ + 'sortableFields' => [ 'Attributes.name' ] ]; diff --git a/app/src/Controller/TierApiController.php b/app/src/Controller/TierApiController.php index 1d1b80f36..668f48e24 100644 --- a/app/src/Controller/TierApiController.php +++ b/app/src/Controller/TierApiController.php @@ -29,8 +29,7 @@ namespace App\Controller; -use Cake\Mailer\Email; -use Cake\ORM\TableRegistry; +use Cake\Mailer\Mailer; use Cake\Log\Log; use Cake\Routing\Router; @@ -179,8 +178,8 @@ protected function doMatchRequest(bool $searchOnly=false) { $sor = $this->request->getParam('sor'); $sorid = $this->request->getParam('sorid'); // Request attributes are here (json body) - $json = $this->request->input('json_decode'); - + $json = $this->request->getParsedBody(); + Log::write('debug', $sor . "/" . $sorid . ($searchOnly ? " Search" : " Match") . " request received for matchgrid " . $matchgridId); try { @@ -336,7 +335,7 @@ protected function doMatchRequest(bool $searchOnly=false) { // Pull the SOR configuration - $SoR = TableRegistry::get('SystemsOfRecord'); + $SoR = $this->getTableLocator()->get('SystemsOfRecord'); $sorobj = $SoR->find('all')->where(['matchgrid_id' => $matchgridId, 'label' => $sor])->firstOrFail(); @@ -353,7 +352,7 @@ protected function doMatchRequest(bool $searchOnly=false) { if(!empty($sorobj->notification_email)) { $notify = $sorobj->notification_email; } else { - $MatchgridSettings = TableRegistry::get('MatchgridSettings'); + $MatchgridSettings = $this->getTableLocator()->get('MatchgridSettings'); $notify = $MatchgridSettings->getNotificationEmail($matchgridId); } @@ -364,7 +363,7 @@ protected function doMatchRequest(bool $searchOnly=false) { $url = "/matchgrids/reconcile/" . $this->cur_mg->id . "?rowid=" . $matchRequest; - $email = new Email('default'); + $email = new Mailer('default'); $email->setViewVars([ 'vv_matchgrid_id' => $this->cur_mg->id, 'vv_pending_url' => Router::url($url, true) @@ -372,7 +371,7 @@ protected function doMatchRequest(bool $searchOnly=false) { $email->viewBuilder()->setTemplate('potential_match'); $email->setEmailFormat('text'); $email->setTo($notify); - $email->send(); + $email->deliver(); Log::write('debug', $sor . "/" . $sorid . " Potential match requiring resolution, notification sent to " . $notify); } @@ -426,8 +425,8 @@ protected function doMerge(\App\Lib\Match\MatchService $MatchService) { // Reference ID we want to keep / merge to $targetId = $this->request->getParam('id'); - $json = $this->request->input('json_decode'); - + $json = $this->request->getParsedBody(); + if($targetId && !empty($json->referenceIds)) { $MatchService->merge($targetId, $json->referenceIds); diff --git a/app/src/Lib/Identifier/Sequence.php b/app/src/Lib/Identifier/Sequence.php index 35c80d3da..1fc48e3b4 100644 --- a/app/src/Lib/Identifier/Sequence.php +++ b/app/src/Lib/Identifier/Sequence.php @@ -41,7 +41,7 @@ class Sequence extends ReferenceIdService { */ public function generate(\App\Model\Entity\Matchgrid $mgConfig, \ADOConnection $dbc) { - $MatchgridSettings = \Cake\ORM\TableRegistry::get('MatchgridSettings'); + $MatchgridSettings = Cake\Datasource\FactoryLocator::get('MatchgridSettings'); // We'll use the matchgrid ID rather than name in the sequence name to avoid // issues if the matchgrid is renamed for some reason. diff --git a/app/src/Lib/Match/MatchService.php b/app/src/Lib/Match/MatchService.php index 41dc18157..c2e23f5eb 100644 --- a/app/src/Lib/Match/MatchService.php +++ b/app/src/Lib/Match/MatchService.php @@ -30,7 +30,7 @@ namespace App\Lib\Match; use Cake\Log\Log; -use Cake\ORM\TableRegistry; +use Cake\Datasource\FactoryLocator; use \App\Lib\Enum\ConfidenceModeEnum; use \App\Lib\Enum\ReferenceIdEnum; @@ -104,7 +104,7 @@ public function attachReferenceId(string $sor, string $sorid, AttributeManager $ */ protected function generateReferenceId() { - $MatchgridSettings = TableRegistry::get('MatchgridSettings'); + $MatchgridSettings = FactoryLocator::get('MatchgridSettings'); if($MatchgridSettings->getReferenceIdMethod($this->mgConfig->id) == ReferenceIdEnum::Sequence) { $IdService = new \App\Lib\Identifier\Sequence; @@ -553,7 +553,6 @@ protected function search(string $mode, $attribute = $ruleattr->crosscheck_attribute ?: $ruleattr->attribute; $attrclause = ""; - $colclause = ""; // The column name $colclause = $attribute->name; @@ -774,7 +773,7 @@ public function searchReferenceId(string $sor, string $sorid, AttributeManager $ */ public function setConfig(int $matchgridId) { - $Matchgrids = TableRegistry::get('Matchgrids'); + $Matchgrids = FactoryLocator::get('Table')->get('Matchgrids'); $this->mgConfig = $Matchgrids->findById($matchgridId) ->where(['status' => StatusEnum::Active]) diff --git a/app/src/Lib/Traits/MatchgridLinkTrait.php b/app/src/Lib/Traits/MatchgridLinkTrait.php index fe6f61231..4de262431 100644 --- a/app/src/Lib/Traits/MatchgridLinkTrait.php +++ b/app/src/Lib/Traits/MatchgridLinkTrait.php @@ -29,7 +29,7 @@ namespace App\Lib\Traits; -use Cake\ORM\TableRegistry; +use Cake\Datasource\FactoryLocator; trait MatchgridLinkTrait { // Does the associated model require a matchgrid ID? @@ -79,7 +79,7 @@ public function calculateMatchgridId(int $id) { // Look up $linkAttr to get its Matchgrid. We currently assume only // one level of nesting. - $linkTable = TableRegistry::get($this->getPrimaryLinkTableName()); + $linkTable = FactoryLocator::get($this->getPrimaryLinkTableName()); return $linkTable->calculateMatchgridId($obj->$linkAttr); } diff --git a/app/src/Model/Table/ApiUsersTable.php b/app/src/Model/Table/ApiUsersTable.php index ab9200305..e6400d345 100644 --- a/app/src/Model/Table/ApiUsersTable.php +++ b/app/src/Model/Table/ApiUsersTable.php @@ -31,7 +31,7 @@ use Cake\ORM\RulesChecker; use Cake\ORM\Table; -use Cake\ORM\TableRegistry; +use Cake\Datasource\FactoryLocator; use Cake\Validation\Validator; use \App\Lib\Enum\ResolutionModeEnum; @@ -122,7 +122,7 @@ public function checkUsername(\App\Model\Entity\ApiUser $entity, array $options) if($entity->matchgrid_id) { // This is a Matchgrid API user, so make sure it is prefixed with the matchgrid name - $matchgrids = TableRegistry::get('Matchgrids'); + $matchgrids = FactoryLocator::get('Table')->get('Matchgrids'); // This throws Cake\Datasource\Exception\RecordNotFoundException if not found $matchgrid = $matchgrids->get($entity->matchgrid_id); diff --git a/app/src/Model/Table/MatchgridRecordsTable.php b/app/src/Model/Table/MatchgridRecordsTable.php index 7f82b81f7..898e3ba51 100644 --- a/app/src/Model/Table/MatchgridRecordsTable.php +++ b/app/src/Model/Table/MatchgridRecordsTable.php @@ -30,7 +30,7 @@ namespace App\Model\Table; use Cake\ORM\Table; -use Cake\ORM\TableRegistry; +use Cake\Datasource\FactoryLocator; class MatchgridRecordsTable extends Table { use \App\Lib\Traits\AutoViewVarsTrait; @@ -67,7 +67,7 @@ public function initialize(array $config): void { // Use the matchgrid ID to figure out the attribute configuration for // search filter purposes - $Matchgrid = TableRegistry::get('Matchgrids'); + $Matchgrid = FactoryLocator::get('Table')->get('Matchgrids'); $mgconfig = $Matchgrid->getMatchgridConfig($config['matchgrid_id']); diff --git a/app/vendor/aura/intl/.gitignore b/app/vendor/aura/intl/.gitignore deleted file mode 100644 index 75d706bcb..000000000 --- a/app/vendor/aura/intl/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/tests/tmp -/vendor -/composer.lock diff --git a/app/vendor/aura/intl/.scrutinizer.yml b/app/vendor/aura/intl/.scrutinizer.yml deleted file mode 100644 index 7c7c84543..000000000 --- a/app/vendor/aura/intl/.scrutinizer.yml +++ /dev/null @@ -1,10 +0,0 @@ -filter: - paths: ["src/*"] -tools: - external_code_coverage: true - php_code_coverage: true - php_sim: true - php_mess_detector: true - php_pdepend: true - php_analyzer: true - php_cpd: true diff --git a/app/vendor/aura/intl/.travis.yml b/app/vendor/aura/intl/.travis.yml deleted file mode 100644 index 823c70497..000000000 --- a/app/vendor/aura/intl/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -sudo: false -language: php -php: - - 5.6 - - hhvm - - 7 -before_script: - - composer self-update - - composer install -script: - - phpunit --coverage-clover=coverage.clover -after_script: - - wget https://scrutinizer-ci.com/ocular.phar - - php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/app/vendor/aura/intl/CHANGELOG.md b/app/vendor/aura/intl/CHANGELOG.md deleted file mode 100644 index 12e89902c..000000000 --- a/app/vendor/aura/intl/CHANGELOG.md +++ /dev/null @@ -1,66 +0,0 @@ -# CHANGELOG - -## 3.0.0 - -- Final release. No changes after first beta. - -## 3.0.0-beta1 - -- Removed `aura/installer-default` from composer.json which was used by aura framework version 1 to install in package folder. -- Fixes [issue 17](https://github.com/auraphp/Aura.Intl/issues/17) by removing Aura.Di wiring trait tests. -- Changed directory structure from PSR-0 to PSR-4. -- Supported PHP version : 5.6+. -- There is no other BC breaks from 1.x version - - -## 1.1.1 - -Hygiene release: Composer update. - -## 1.1.0 - -- Merge pull request #11 from lorenzo/feature/format-key. If Translator::translate() cannot translate, it now returns the incoming translation key as the translated message. This makes it easier to use the key as a human readable string, which can contain formatting placeholders. With this feature the developer will be able to see immediate feedback of their translation messages without having to translate to a specific locale beforehand. - -## 1.0.1 - -Hygiene release. - -- Merge pull request #10 from harikt/fixdoc; fixes #9, add a test for failure. The expected exception need to be fixed. - -- Merge pull request #8 from harikt/v2config, adding v2 config files. - -- Merge pull request #7 from harikt/issue6, add a test to show it works. Thank you @samdark. - -- Point travis status badge to develop branch - -## 1.0.0 - -- [CHG] When interpolating array values into strings, now converts the array - to comma-separated values instead of printing 'Array'. - -- [DOC] PHP 5.5 appears to be able to format strings with missing token - replacements; updated tests to account for this. - -## 1.0.0-beta2 - -- [FIX] Multiple typo fixes; thanks, @pborreli. - -- [CHG] Use {foo} instead of {:foo} in line with PSR-3 placeholders. - -- [NEW] Add a PackageFactory - -- [NEW] Add more-detailed exception classes - -- [ADD] In config, add 'intl_package_factory' as a service - -- [CHG] Throw IcuVersionTooLow Exception if IntlFormatter is instantiated with - ICU Version lower than 4.8; Skip all IntlFormatter tests if the intl - extension is not loaded. Thanks, @mapthegod. - -- [CHG] In the FormatterLocator, registry entries *must* be wrapped in a - callable from now on. - - -## 1.0.0-beta1 - -Initial release. diff --git a/app/vendor/aura/intl/CONTRIBUTING.md b/app/vendor/aura/intl/CONTRIBUTING.md deleted file mode 100644 index b68f9b85c..000000000 --- a/app/vendor/aura/intl/CONTRIBUTING.md +++ /dev/null @@ -1,7 +0,0 @@ -# Contributing - -We are happy to review any contributions you want to make. When contributing, please follow the rules outlined at . - -The time between submitting a contribution and its review one may be extensive; do not be discouraged if there is not immediate feedback. - -Thanks! diff --git a/app/vendor/aura/intl/LICENSE b/app/vendor/aura/intl/LICENSE deleted file mode 100644 index 5f5f3b61c..000000000 --- a/app/vendor/aura/intl/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017, Aura for PHP - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/app/vendor/aura/intl/README.md b/app/vendor/aura/intl/README.md deleted file mode 100644 index 0537d4721..000000000 --- a/app/vendor/aura/intl/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Aura.Intl - -The Aura.Intl package provides internationalization (I18N) tools, specifically -package-oriented per-locale message translation. - -## Installation and Autoloading - -This package is installable and PSR-4 autoloadable via Composer as -[aura/intl][]. - -Alternatively, [download a release][], or clone this repository, then map the -`Aura\Intl\` namespace to the package `src/` directory. - -## Dependencies - -This package requires PHP 5.6 or later; it has been tested on PHP 5.6, 7.0 -and HHVM. We recommend using the latest available version of PHP as a matter of -principle. - -Aura library packages may sometimes depend on external interfaces, but never on -external implementations. This allows compliance with community standards -without compromising flexibility. For specifics, please examine the package -[composer.json][] file. - -## Quality - -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/auraphp/Aura.Intl/badges/quality-score.png?b=3.x)](https://scrutinizer-ci.com/g/auraphp/Aura.Intl/) -[![Code Coverage](https://scrutinizer-ci.com/g/auraphp/Aura.Intl/badges/coverage.png?b=3.x)](https://scrutinizer-ci.com/g/auraphp/Aura.Intl/) -[![Build Status](https://travis-ci.org/auraphp/Aura.Intl.png?branch=3.x)](https://travis-ci.org/auraphp/Aura.Intl) - -This project adheres to [Semantic Versioning](http://semver.org/). - -To run the unit tests at the command line, issue `composer install` and then -`phpunit` at the package root. This requires [Composer][] to be available as -`composer`, and [PHPUnit][] to be available as `phpunit`. - -This package attempts to comply with [PSR-1][], [PSR-2][], and [PSR-4][]. If -you notice compliance oversights, please send a patch via pull request. - -## Community - -To ask questions, provide feedback, or otherwise communicate with other Aura -users, please join our [Google Group][], follow [@auraphp][], or chat with us -on Freenode in the #auraphp channel. - -## Documentation - -This package is fully documented [here](./docs/index.md). - -[PSR-1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md -[PSR-2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md -[PSR-4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md -[Composer]: http://getcomposer.org/ -[PHPUnit]: http://phpunit.de/ -[Google Group]: http://groups.google.com/group/auraphp -[@auraphp]: http://twitter.com/auraphp -[download a release]: https://github.com/auraphp/Aura.Intl/releases -[aura/intl]: https://packagist.org/packages/aura/intl -[composer.json]: ./composer.json diff --git a/app/vendor/aura/intl/composer.json b/app/vendor/aura/intl/composer.json deleted file mode 100644 index 6366d0ab7..000000000 --- a/app/vendor/aura/intl/composer.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "aura/intl", - "type": "library", - "description": "The Aura Intl package provides internationalization tools, specifically message translation.", - "keywords": [ - "intl", - "internationalization", - "i18n", - "l10n", - "localization", - "globalization", - "g11n" - ], - "homepage": "https://github.com/auraphp/Aura.Intl", - "license": "MIT", - "authors": [ - { - "name": "Aura.Intl Contributors", - "homepage": "https://github.com/auraphp/Aura.Intl/contributors" - } - ], - "require": { - "php": "^5.6|^7.0" - }, - "autoload": { - "psr-4": { - "Aura\\Intl\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "Aura\\Intl\\": "tests/" - } - } -} diff --git a/app/vendor/aura/intl/docs/index.md b/app/vendor/aura/intl/docs/index.md deleted file mode 100644 index f097c5fac..000000000 --- a/app/vendor/aura/intl/docs/index.md +++ /dev/null @@ -1,218 +0,0 @@ -# Aura.Intl - -The Aura.Intl package provides internationalization (I18N) tools, specifically -package-oriented per-locale message translation. - -## Getting Started - -You can instantiate _TranslatorLocator_ object from _TranslatorLocatorFactory_ -as below - -```php -newInstance(); -?> -``` - -Alternatively, we can add the Aura.Intl package `/path/to/Aura.Intl/src` to -our autoloader and build a translator locator manually: - -```php - function () { return new \Aura\Intl\BasicFormatter; }, - 'intl' => function () { return new \Aura\Intl\IntlFormatter; }, - ]), - new TranslatorFactory, - 'en_US' -); -?> -``` - -## Setting Localized Messages For A Package - -We can set localized messages for a package through the `PackageLocator` object -from the translator locator. We create a new `Package` with messages and place -it into the locator as a callable. The messages take the form of a message key and -and message string. - -```php -getPackages(); - -// place into the locator for Vendor.Package -$packages->set('Vendor.Package', 'en_US', function() { - // create a US English message set - $package = new Package; - $package->setMessages([ - 'FOO' => 'The text for "foo."', - 'BAR' => 'The text for "bar."', - ]); - return $package; -}); - -// place into the locator for a Vendor.Package -$packages->set('Vendor.Package', 'pt_BR', function() { - // a Brazilian Portuguese message set - $package = new Package; - $package->setMessages([ - 'FOO' => 'O texto de "foo".', - 'BAR' => 'O texto de "bar".', - ]); - return $package; -}); -?> -``` - - -## Setting The Default Locale - -We can set the default locale for translations using the `setLocale()` method: - -```php -setLocale('pt_BR'); -?> -``` - -## Getting A Localized Message - -Now that the translator locator has messages and a default locale, we can get -an individual package translator. The package translator is suitable for -injection into another class, or for standalone use. - -```php -get('Vendor.Package'); -echo $translator->translate('FOO'); // 'O texto de "foo".' -?> -``` - -You can get a translator for a non-default locale as well: - -```php -get('Vendor.Package', 'en_US'); -echo $translator->translate('FOO'); // 'The text for "foo."' -?> -``` - - -## Replacing Message Tokens With Values - -We often need to use dynamic values in translated messages. First, the -message string needs to have a token placeholder for the dynamic value: - -```php -getPackages(); - -$packages->set('Vendor.Dynamic', 'en_US', function() { - - // US English messages - $package = new Package; - $package->setMessages([ - 'PAGE' => 'Page {page} of {pages} pages.', - ]); - return $package; -}); - -$packages->set('Vendor.Dynamic', 'pt_BR', function() { - // Brazilian Portuguese messages - $package = new Package; - $package->setMessages([ - 'PAGE' => 'Página {page} de {pages} páginas.', - ]); - return $package; -}); -?> -``` - -Then, when we translate the message, we provide an array of tokens and -replacement values. These will be interpolated into the message string. - -```php -get('Vendor.Dynamic'); -echo $translator->translate('PAGE', [ - 'page' => 1, - 'pages' => 1, -]); // 'Página 1 de 1 páginas.' -?> -``` - -## Pluralized Messages - -Usually, we need to use different messages when a value is singular or plural. -The `BasicFormatter` is not capable of presenting different messages based on -different token values. The `IntlFormatter` *is* capable, but the PHP -[`intl`](http://php.net/intl) extension must be loaded to take advantage of -it, and we must specify the `'intl'` formatter for the package in the catalog. - -When using the `IntlFormatter`, we can build our message strings to present -singular or plural messages, as in the following example: - -```php -getCatalog(); - -// get the Vendor.Dynamic package en_US locale and set -// US English messages with pluralization. note the use -// of # instead of {pages} herein; using the placeholder -// "inside itself" with the Intl formatter causes trouble. -$package->setMessages([ - 'PAGE' => '{pages,plural,' - . '=0{No pages.}' - . '=1{One page only.}' - . 'other{Page {page} of # pages.}' - . '}' -]); - -// use the 'intl' formatter for this package and locale -$package->setFormatter('intl'); - -// now that we have added the pluralizable messages, -// get the US English translator for the package -$translator = $translators->get('Vendor.Dynamic', 'en_US'); - -// zero translation -echo $translator->translate('PAGE', [ - 'page' => 0, - 'pages' => 0, -]); // 'No pages.' - -// singular translation -echo $translator->translate('PAGE', [ - 'page' => 1, - 'pages' => 1, -]); // 'One page only.' - -// plural translation -echo $translator->translate('PAGE', [ - 'page' => 3, - 'pages' => 10, -]); // 'Page 3 of 10 pages.' -?> -``` - -Note that you can use other tokens within a pluralized token string to build -more complex messages. For more information, see the following: - - diff --git a/app/vendor/aura/intl/phpunit.php b/app/vendor/aura/intl/phpunit.php deleted file mode 100644 index ceaada7da..000000000 --- a/app/vendor/aura/intl/phpunit.php +++ /dev/null @@ -1,9 +0,0 @@ - - - - ./tests - - - - - ./src/ - - - diff --git a/app/vendor/aura/intl/src/BasicFormatter.php b/app/vendor/aura/intl/src/BasicFormatter.php deleted file mode 100644 index 2d02a778f..000000000 --- a/app/vendor/aura/intl/src/BasicFormatter.php +++ /dev/null @@ -1,47 +0,0 @@ - $value) { - // convert an array to a CSV string - if (is_array($value)) { - $value = '"' . implode('", "', $value) . '"'; - } - $replace['{' . $token . '}'] = $value; - } - return strtr($string, $replace); - } -} diff --git a/app/vendor/aura/intl/src/Exception.php b/app/vendor/aura/intl/src/Exception.php deleted file mode 100644 index c86980522..000000000 --- a/app/vendor/aura/intl/src/Exception.php +++ /dev/null @@ -1,22 +0,0 @@ - $spec) { - $this->set($name, $spec); - } - } - - /** - * - * Sets a formatter into the registry by name. - * - * @param string $name The formatter name. - * - * @param callable $spec A callable that returns a formatter object. - * - * @return void - * - */ - public function set($name, $spec) - { - $this->registry[$name] = $spec; - $this->converted[$name] = false; - } - - /** - * - * Gets a formatter from the registry by name. - * - * @param string $name The formatter to retrieve. - * - * @return FormatterInterface A formatter object. - * - * @throws Exception\FormatterNotMapped - * - */ - public function get($name) - { - if (! isset($this->registry[$name])) { - throw new Exception\FormatterNotMapped($name); - } - - if (! $this->converted[$name]) { - $func = $this->registry[$name]; - $this->registry[$name] = $func(); - $this->converted[$name] = true; - } - - return $this->registry[$name]; - } -} diff --git a/app/vendor/aura/intl/src/IntlFormatter.php b/app/vendor/aura/intl/src/IntlFormatter.php deleted file mode 100644 index 719d6f94d..000000000 --- a/app/vendor/aura/intl/src/IntlFormatter.php +++ /dev/null @@ -1,98 +0,0 @@ - $value) { - // convert an array to a CSV string - if (is_array($value)) { - $value = '"' . implode('", "', $value) . '"'; - } - - $values[$token] = $value; - } - - try { - $formatter = new MessageFormatter($locale, $string); - if (! $formatter) { - $this->throwCannotInstantiateFormatter(); - } - } catch (\Exception $e) { - $this->throwCannotInstantiateFormatter(); - } - - $result = $formatter->format($values); - if ($result === false) { - throw new Exception\CannotFormat( - $formatter->getErrorMessage(), - $formatter->getErrorCode() - ); - } - - return $result; - } - - /** - * - * Throws exception - * - * @throws Exception\CannotInstantiateFormatter - */ - protected function throwCannotInstantiateFormatter() - { - throw new Exception\CannotInstantiateFormatter( - intl_get_error_message(), - intl_get_error_code() - ); - } -} diff --git a/app/vendor/aura/intl/src/Package.php b/app/vendor/aura/intl/src/Package.php deleted file mode 100644 index b714e8325..000000000 --- a/app/vendor/aura/intl/src/Package.php +++ /dev/null @@ -1,197 +0,0 @@ -formatter = $formatter; - $this->fallback = $fallback; - $this->messages = $messages; - } - - /** - * - * Sets the messages for this package. - * - * @param array $messages The messages for this package. - * - * @return void - * - */ - public function setMessages(array $messages) - { - $this->messages = $messages; - } - - /** - * - * Adds one message for this package. - * - * @param string $key the key of the message - * - * @param string $message the actual message - * - * @return void - * - */ - public function addMessage($key, $message) - { - $this->messages[$key] = $message; - } - - /** - * - * Adds new messages for this package. - * - * @param array $messages The messages to add in this package. - * - * @return void - * - */ - public function addMessages($messages) - { - $this->messages = array_merge($this->messages, $messages); - } - - /** - * - * Gets the messages for this package. - * - * @return array - * - */ - public function getMessages() - { - return $this->messages; - } - - - /** - * - * Gets the message of the given key for this package. - * - * @param string $key the key of the message to return - * - * @return mixed The message translation string, or false if not found. - * - */ - public function getMessage($key) - { - if (isset($this->messages[$key])) { - return $this->messages[$key]; - } - - return false; - } - - /** - * - * Sets the formatter name for this package. - * - * @param string $formatter The formatter name for this package. - * - * @return void - * - */ - public function setFormatter($formatter) - { - $this->formatter = $formatter; - } - - /** - * - * Gets the formatter name for this package. - * - * @return string - * - */ - public function getFormatter() - { - return $this->formatter; - } - - /** - * - * Sets the fallback package name. - * - * @param string $fallback The fallback package name. - * - * @return void - * - */ - public function setFallback($fallback) - { - $this->fallback = $fallback; - } - - /** - * - * Gets the fallback package name. - * - * @return string - * - */ - public function getFallback() - { - return $this->fallback; - } -} diff --git a/app/vendor/aura/intl/src/PackageFactory.php b/app/vendor/aura/intl/src/PackageFactory.php deleted file mode 100644 index a7a4a27aa..000000000 --- a/app/vendor/aura/intl/src/PackageFactory.php +++ /dev/null @@ -1,47 +0,0 @@ -setFallback($info['fallback']); - } - if (isset($info['formatter'])) { - $package->setFormatter($info['formatter']); - } - if (isset($info['messages'])) { - $package->setMessages($info['messages']); - } - return $package; - } -} diff --git a/app/vendor/aura/intl/src/PackageLocator.php b/app/vendor/aura/intl/src/PackageLocator.php deleted file mode 100644 index d719c3743..000000000 --- a/app/vendor/aura/intl/src/PackageLocator.php +++ /dev/null @@ -1,107 +0,0 @@ - $locales) { - foreach ($locales as $locale => $spec) { - $this->set($name, $locale, $spec); - } - } - } - - /** - * - * Sets a Package object. - * - * @param string $name The package name. - * - * @param string $locale The locale for the package. - * - * @param callable $spec A callable that returns a package. - * - * @return void - * - */ - public function set($name, $locale, callable $spec) - { - $this->registry[$name][$locale] = $spec; - $this->converted[$name][$locale] = false; - } - - /** - * - * Gets a Package object. - * - * @param string $name The package name. - * - * @param string $locale The locale for the package. - * - * @return Package - * - */ - public function get($name, $locale) - { - if (! isset($this->registry[$name][$locale])) { - throw new Exception("Package '$name' with locale '$locale' is not registered."); - } - - if (! $this->converted[$name][$locale]) { - $func = $this->registry[$name][$locale]; - $this->registry[$name][$locale] = $func(); - $this->converted[$name][$locale] = true; - } - - return $this->registry[$name][$locale]; - } -} diff --git a/app/vendor/aura/intl/src/PackageLocatorInterface.php b/app/vendor/aura/intl/src/PackageLocatorInterface.php deleted file mode 100644 index e4b8532fb..000000000 --- a/app/vendor/aura/intl/src/PackageLocatorInterface.php +++ /dev/null @@ -1,49 +0,0 @@ -locale = $locale; - $this->package = $package; - $this->formatter = $formatter; - $this->fallback = $fallback; - } - - /** - * - * Gets the message translation by its key. - * - * @param string $key The message key. - * - * @return mixed The message translation string, or false if not found. - * - */ - protected function getMessage($key) - { - $message = $this->package->getMessage($key); - if ($message) { - return $message; - } - - if ($this->fallback) { - // get the message from the fallback translator - $message = $this->fallback->getMessage($key); - if ($message) { - // speed optimization: retain locally - $this->package->addMessage($key, $message); - // done! - return $message; - } - } - - // no local message, no fallback - return false; - } - - /** - * - * Translates the message indicated by they key, replacing token values - * along the way. - * - * @param string $key The message key. - * - * @param array $tokens_values Token values to interpolate into the - * message. - * - * @return string The translated message with tokens replaced. - * - */ - public function translate($key, array $tokens_values = []) - { - // get the message string - $message = $this->getMessage($key); - - // do we have a message string? - if (! $message) { - // no, return the message key as-is - $message = $key; - } - - // are there token replacement values? - if (empty($tokens_values)) { - // no, return the message string as-is - return $message; - } - - // run message string through formatter to replace tokens with values - return $this->formatter->format($this->locale, $message, $tokens_values); - } - - /** - * - * An object of type Package - * - * @return Package - * - */ - public function getPackage() - { - return $this->package; - } -} diff --git a/app/vendor/aura/intl/src/TranslatorFactory.php b/app/vendor/aura/intl/src/TranslatorFactory.php deleted file mode 100644 index 8cc543888..000000000 --- a/app/vendor/aura/intl/src/TranslatorFactory.php +++ /dev/null @@ -1,57 +0,0 @@ -class; - return new $class($locale, $package, $formatter, $fallback); - } -} diff --git a/app/vendor/aura/intl/src/TranslatorInterface.php b/app/vendor/aura/intl/src/TranslatorInterface.php deleted file mode 100644 index 8367a4c89..000000000 --- a/app/vendor/aura/intl/src/TranslatorInterface.php +++ /dev/null @@ -1,32 +0,0 @@ -packages = $packages; - $this->factory = $factory; - $this->formatters = $formatters; - $this->setLocale($locale); - } - - /** - * - * Sets the default locale code. - * - * @param string $locale The new locale code. - * - * @return void - * - */ - public function setLocale($locale) - { - $this->locale = $locale; - } - - /** - * - * Returns the default locale code. - * - * @return string - * - */ - public function getLocale() - { - return $this->locale; - } - - /** - * - * The TranslatorFactory object - * - * @return TranslatorFactory - */ - public function getFactory() - { - return $this->factory; - } - - /** - * - * An object of type PackageLocator - * - * @return PackageLocator - * - */ - public function getPackages() - { - return $this->packages; - } - - /** - * - * An object of type FormatterLocator - * - * @return FormatterLocator - * - */ - public function getFormatters() - { - return $this->formatters; - } - - /** - * - * Gets a translator from the registry by package for a locale. - * - * @param string $name The translator package to retrieve. - * - * @param string $locale The locale to use; if empty, uses the default - * locale. - * - * @return TranslatorInterface A translator object. - * - */ - public function get($name, $locale = null) - { - if (! $name) { - return null; - } - - if (! $locale) { - $locale = $this->getLocale(); - } - - if (! isset($this->registry[$name][$locale])) { - - // get the package descriptor - $package = $this->packages->get($name, $locale); - - // build a translator; note the recursive nature of the - // 'fallback' param at the very end. - $translator = $this->factory->newInstance( - $locale, - $package, - $this->formatters->get($package->getFormatter()), - $this->get($package->getFallback(), $locale) - ); - - // retain in the registry - $this->registry[$name][$locale] = $translator; - } - - return $this->registry[$name][$locale]; - } -} diff --git a/app/vendor/aura/intl/src/TranslatorLocatorFactory.php b/app/vendor/aura/intl/src/TranslatorLocatorFactory.php deleted file mode 100644 index 907e60c38..000000000 --- a/app/vendor/aura/intl/src/TranslatorLocatorFactory.php +++ /dev/null @@ -1,45 +0,0 @@ - function () { - return new \Aura\Intl\BasicFormatter; - }, - 'intl' => function () { - return new \Aura\Intl\IntlFormatter; - }, - ]), - new TranslatorFactory, - 'en_US' - ); - } -} diff --git a/app/vendor/aura/intl/tests/BasicFormatterTest.php b/app/vendor/aura/intl/tests/BasicFormatterTest.php deleted file mode 100644 index b70781b56..000000000 --- a/app/vendor/aura/intl/tests/BasicFormatterTest.php +++ /dev/null @@ -1,29 +0,0 @@ -newFormatter(); - - $locale = 'en_US'; - $expect = 'Hello world 88!'; - $tokens_values = ['foo' => 'world', 'bar' => '88', 'baz' => '!']; - - $string = 'Hello {foo} {bar}{baz}'; - $actual = $formatter->format($locale, $string, $tokens_values); - $this->assertSame($expect, $actual); - - $tokens_values = ['array' => ['foo', 'bar', 'baz']]; - $string = 'Array {array}'; - $expect = 'Array "foo", "bar", "baz"'; - $actual = $formatter->format($locale, $string, $tokens_values); - $this->assertSame($expect, $actual); - } -} diff --git a/app/vendor/aura/intl/tests/FormatterLocatorTest.php b/app/vendor/aura/intl/tests/FormatterLocatorTest.php deleted file mode 100644 index c1286792c..000000000 --- a/app/vendor/aura/intl/tests/FormatterLocatorTest.php +++ /dev/null @@ -1,37 +0,0 @@ - function () { - return new \Aura\Intl\MockFormatter; - }, - ]); - - $expect = 'Aura\Intl\MockFormatter'; - $actual = $formatters->get('mock'); - $this->assertInstanceOf($expect, $actual); - } - - public function testSetAndGet() - { - $formatters = new FormatterLocator; - $formatters->set('mock', function () { - return new \Aura\Intl\MockFormatter; - }); - - $expect = 'Aura\Intl\MockFormatter'; - $actual = $formatters->get('mock'); - $this->assertInstanceOf($expect, $actual); - } - - public function testGet_noSuchFormatter() - { - $formatters = new FormatterLocator; - $this->setExpectedException('Aura\Intl\Exception\FormatterNotMapped'); - $formatters->get('noSuchFormatter'); - } -} diff --git a/app/vendor/aura/intl/tests/IntlFormatterTest.php b/app/vendor/aura/intl/tests/IntlFormatterTest.php deleted file mode 100644 index ea8399c74..000000000 --- a/app/vendor/aura/intl/tests/IntlFormatterTest.php +++ /dev/null @@ -1,195 +0,0 @@ -markTestSkipped('This test is skipped if the Intl Extension is not loaded.'); - } - } - - public function testIntlVersion() - { - $this->setExpectedException('Aura\Intl\Exception\IcuVersionTooLow'); - $formatter = new IntlFormatter('4.7'); - } - - /** - * This test fails on PHP 5.4.4 - * The return value expected is No pages , but returns false - * - * var_dump( msgfmt_get_error_message($fmt) ); - * U_ZERO_ERROR - * - * It seems $fmt = msgfmt_create($locale, $string); is not creating with - * this string , so the msgfmt_format() throws expects parameter 1 to be - * MessageFormatter, null given error. - */ - public function testFormat_plural() - { - $formatter = $this->newFormatter(); - $locale = 'en_US'; - $string = '{pages,plural,' - . '=0{No pages.}' - . '=1{One page only.}' - . 'other{Page {page} of # pages.}' - . '}'; - - $tokens_values = ['page' => 0, 'pages' => 0]; - $expect = 'No pages.'; - $actual = $formatter->format($locale, $string, $tokens_values); - $this->assertSame($expect, $actual); - - $tokens_values = ['page' => 1, 'pages' => 1]; - $expect = 'One page only.'; - $actual = $formatter->format($locale, $string, $tokens_values); - $this->assertSame($expect, $actual); - - $tokens_values = ['page' => 1, 'pages' => 2]; - $expect = 'Page 1 of 2 pages.'; - $actual = $formatter->format($locale, $string, $tokens_values); - $this->assertSame($expect, $actual); - } - - /** - * @dataProvider provide_testFormat_select - */ - public function testFormat_select($tokens_values, $expect) - { - $locale = 'en_US'; - $string = " - {gender, select, - female { - {count, plural, offset:1 - =0 {{from} does not give a party.} - =1 {{from} invites {to} to her party.} - =2 {{from} invites {to} and one other person to her party.} - other {{from} invites {to} as one of the # people invited to her party.} - } - } - male { - {count, plural, offset:1 - =0 {{from} does not give a party.} - =1 {{from} invites {to} to his party.} - =2 {{from} invites {to} and one other person to his party.} - other {{from} invites {to} as one of the # other people invited to his party.} - } - } - other { - {count, plural, offset:1 - =0 {{from} does not give a party.} - =1 {{from} invites {to} to their party.} - =2 {{from} invites {to} and one other person to their party.} - other {{from} invites {to} as one of the # other people invited to their party.} - } - } - }"; - - $formatter = $this->newFormatter(); - $actual = $formatter->format($locale, $string, $tokens_values); - $this->assertSame(trim($expect), trim($actual)); - } - - public function provide_testFormat_select() - { - return [ - [array('gender' => 'female', 'count' => 0, 'from' => 'Alice', 'to' => 'Bob'), 'Alice does not give a party.'], - [array('gender' => 'male', 'count' => 1, 'from' => 'Bob', 'to' => 'Alice'), 'Bob invites Alice to his party.'], - [array('gender' => 'none', 'count' => 2, 'from' => 'Alice', 'to' => 'Bob'), 'Alice invites Bob and one other person to their party.'], - [array('gender' => 'female', 'count' => 27, 'from' => 'Alice', 'to' => 'Bob'), 'Alice invites Bob as one of the 26 people invited to her party.'], - ]; - } - - public function testFormat_cannotInstantiateFormatter() - { - $locale = 'en_US'; - // uses {count} instead of #, which should fail - $string = " - {gender, select, - female { - {count, plural, offset:1 - =0 {{from} does not give a party.} - =1 {{from} invites {to} to her party.} - =2 {{from} invites {to} and one other person to her party.} - other {{from} invites {to} as one of the {count} people invited to her party.} - } - } - male { - {count, plural, offset:1 - =0 {{from} does not give a party.} - =1 {{from} invites {to} to his party.} - =2 {{from} invites {to} and one other person to his party.} - other {{from} invites {to} as one of the {count} other people invited to his party.} - } - } - other { - {count, plural, offset:1 - =0 {{from} does not give a party.} - =1 {{from} invites {to} to their party.} - =2 {{from} invites {to} and one other person to their party.} - other {{from} invites {to} as one of the {count} other people invited to their party.} - } - } - }"; - $formatter = $this->newFormatter(); - $this->setExpectedException('Aura\Intl\Exception\CannotFormat'); - $actual = $formatter->format($locale, $string, array('gender' => 'female', 'count' => 5, 'from' => 'Alice', 'to' => 'Bob')); - } - - // @todo MAKE IT SO THAT WE CHECK FOR TOKENS IN THE ARRAY - public function testFormat_cannotFormat() - { - $locale = 'en_US'; - $string = 'Hello {foo}'; - $tokens_values = ['bar' => 'baz']; // no 'foo' token - $formatter = $this->newFormatter(); - - $expect = 'Hello {foo}'; - $actual = $formatter->format($locale, $string, $tokens_values); - $this->assertSame($expect, $actual); - } - - /** - * @dataProvider provide_testIssue6 - */ - public function testIssue6($tokens_values, $expect) - { - $locale = 'en_US'; - $string = '{gender, select, female{{name} is {gender} and she report bugs!} male{{name} is {gender} and he report bugs!} other{{name} report bugs!}}'; - $formatter = $this->newFormatter(); - $actual = $formatter->format($locale, $string, $tokens_values); - $this->assertSame($expect, $actual); - } - - public function testEmptyStringThrowsException() - { - $locale = 'en_US'; - $string = ''; - $formatter = $this->newFormatter(); - if (! defined('HHVM_VERSION')) { - $this->setExpectedException('Aura\Intl\Exception\CannotInstantiateFormatter'); - } - $actual = $formatter->format($locale, $string, []); - if (defined('HHVM_VERSION')) { - // HHVM will instantiate Formatter even if empty string is passed. - $expect = ''; - $this->assertSame($expect, $actual); - } - } - - public function provide_testIssue6() - { - return [ - [array('gender' => 'female', 'name' => 'Alice'), 'Alice is female and she report bugs!'], - [array('gender' => 'male', 'name' => 'Alexander' ), 'Alexander is male and he report bugs!'], - [array('gender' => '', 'name' => 'Unknown'), 'Unknown report bugs!'], - ]; - } -} diff --git a/app/vendor/aura/intl/tests/MockFormatter.php b/app/vendor/aura/intl/tests/MockFormatter.php deleted file mode 100644 index 8905b1dca..000000000 --- a/app/vendor/aura/intl/tests/MockFormatter.php +++ /dev/null @@ -1,10 +0,0 @@ -packages = new PackageLocator([ - 'Vendor.Foo' => [ - 'en_US' => function () { - return new \Aura\Intl\Package; - }, - ], - ]); - } - - public function testGet() - { - // get once to create it the first time - $first = $this->packages->get('Vendor.Foo', 'en_US'); - $this->assertInstanceOf('Aura\Intl\Package', $first); - - // get again to make sure it's the same object - $again = $this->packages->get('Vendor.Foo', 'en_US'); - $this->assertSame($first, $again); - - // try for an unregistered package - $this->setExpectedException('Aura\Intl\Exception'); - $this->packages->get('Vendor.Bar', 'en_US'); - } -} diff --git a/app/vendor/aura/intl/tests/PackageTest.php b/app/vendor/aura/intl/tests/PackageTest.php deleted file mode 100644 index 9ca034c79..000000000 --- a/app/vendor/aura/intl/tests/PackageTest.php +++ /dev/null @@ -1,42 +0,0 @@ -package = $factory->newInstance([ - 'fallback' => 'Vendor.Fallback', - 'formatter' => 'intl', - 'messages' => [ - 'ERR_NO_SUCH_OPTION' => "The option {option} is not recognized.", - ], - ]); - } - - public function testGet() - { - $expect = 'Vendor.Fallback'; - $actual = $this->package->getFallback(); - $this->assertSame($expect, $actual); - - $expect = 'intl'; - $actual = $this->package->getFormatter(); - $this->assertSame($expect, $actual); - - $expect = [ - 'ERR_NO_SUCH_OPTION' => "The option {option} is not recognized.", - ]; - $actual = $this->package->getMessages(); - $this->assertSame($expect, $actual); - - } -} diff --git a/app/vendor/aura/intl/tests/TranslatorLocatorFactoryTest.php b/app/vendor/aura/intl/tests/TranslatorLocatorFactoryTest.php deleted file mode 100644 index 5d7f04c0f..000000000 --- a/app/vendor/aura/intl/tests/TranslatorLocatorFactoryTest.php +++ /dev/null @@ -1,11 +0,0 @@ -assertInstanceOf('Aura\Intl\TranslatorLocator', $factory->newInstance()); - } -} diff --git a/app/vendor/aura/intl/tests/TranslatorLocatorTest.php b/app/vendor/aura/intl/tests/TranslatorLocatorTest.php deleted file mode 100644 index 7c6ed130f..000000000 --- a/app/vendor/aura/intl/tests/TranslatorLocatorTest.php +++ /dev/null @@ -1,113 +0,0 @@ - "The option {option} is not recognized.", - ] - ); - }; - - $registry['Vendor.Package']['pt_BR'] = function () { - return new \Aura\Intl\Package( - 'mock', - null, - [ - 'ERR_NO_SUCH_OPTION' => "O {option} opção não é reconhecido.", - ] - ); - }; - - $this->packages = new PackageLocator($registry); - - $this->formatters = new FormatterLocator([ - 'mock' => function () { - return new MockFormatter; - }, - ]); - - $this->factory = new TranslatorFactory; - - $this->translators = new TranslatorLocator( - $this->packages, - $this->formatters, - $this->factory, - 'en_US' - ); - } - - public function testSetAndGetLocale() - { - $expect = 'pt_BR'; - $this->translators->setLocale($expect); - $actual = $this->translators->getLocale(); - $this->assertSame($expect, $actual); - } - - public function testGetFactory() - { - $actual = $this->translators->getFactory(); - $this->assertSame($this->factory, $actual); - } - - public function testGet() - { - $actual = $this->translators->get('Vendor.Package'); - $this->assertInstanceOf('Aura\Intl\Translator', $actual); - } - - public function testGetPackages() - { - $actual = $this->translators->getPackages(); - $this->assertSame($this->packages, $actual); - } - - public function testGetFormatterLocator() - { - $actual = $this->translators->getFormatters(); - $this->assertSame($this->formatters, $actual); - } - - public function testIssue9() - { - $this->packages->set('Vendor.Package', 'en_UK', function () { - $package = new Package('mock'); - $package->setMessages([ - 'FOO' => 'The text for "foo."', - 'BAR' => 'The text for "bar."', - ]); - return $package; - }); - - $translator = $this->translators->get('Vendor.Package', 'en_UK'); - $expect = 'The text for "foo."'; - $this->assertSame($translator->translate('FOO'), $expect); - } - - public function testIssue9Failure() - { - $package = new Package; - $package->setMessages([ - 'FOO' => 'The text for "foo."', - 'BAR' => 'The text for "bar."', - ]); - // $this->packages->set('Vendor.Package', 'en_UK', $package); - // $this->setExpectedException('Exception'); - // $translator = $this->translators->get('Vendor.Package', 'en_UK'); - } -} diff --git a/app/vendor/aura/intl/tests/TranslatorTest.php b/app/vendor/aura/intl/tests/TranslatorTest.php deleted file mode 100644 index e7e8c58d7..000000000 --- a/app/vendor/aura/intl/tests/TranslatorTest.php +++ /dev/null @@ -1,101 +0,0 @@ - 'Foo text', - 'TEXT_BAR' => 'Bar text', - ]; - - protected $formatter; - - protected function newTranslator(TranslatorInterface $fallback = null) - { - return $this->factory->newInstance( - 'en_US', - $this->package, - $this->formatter, - $fallback - ); - } - - protected function setUp() - { - $this->factory = new TranslatorFactory; - $this->formatter = new MockFormatter; - $this->package = new Package; - $this->package->setMessages($this->messages); - $this->translator = $this->newTranslator(); - } - - public function testTranslate() - { - // key exists - $expect = 'Foo text'; - $actual = $this->translator->translate('TEXT_FOO'); - $this->assertSame($expect, $actual); - - // key exists, with tokens passed - $expect = 'Foo text'; - $actual = $this->translator->translate('TEXT_FOO', ['foo' => 'bar']); - $this->assertSame($expect, $actual); - - // key does not exist - $expect = 'TEXT_NONE'; - $actual = $this->translator->translate('TEXT_NONE'); - $this->assertSame($expect, $actual); - } - - public function testTranslate_fallback() - { - $package = new Package; - $package->setMessages([ - 'TEXT_NONE' => 'Fallback text', - ]); - // create fallback translator - $fallback = new Translator( - 'en_US', - $package, - $this->formatter - ); - - // create primary translator with fallback - $translator = $this->newTranslator($fallback); - - // key does not exist in primary, but exists in fallback - $expect = 'Fallback text'; - $actual = $translator->translate('TEXT_NONE'); - $this->assertSame($expect, $actual); - } - - public function testTranslateMissingKey() - { - // using getMockBuilder so we can use with phpunit 4.8 or 5+ without warnings - $formatter = $this->getMockBuilder(get_class($this->formatter)) - ->disableOriginalConstructor() - ->disableOriginalClone() - ->disableArgumentCloning() - ->getMock(); - // create fallback translator - $translator = new Translator('en_US', new Package, $formatter); - - $formatter->expects($this->once()) - ->method('format') - ->with('en_US', 'TEXT', ['var' => 'SOME']) - ->will($this->returnValue('FORMATTED')); - - // key does not exist, with tokens passed - $expect = 'FORMATTED'; - $actual = $translator->translate('TEXT', ['var' => 'SOME']); - $this->assertEquals($expect, $actual); - } -} diff --git a/app/vendor/brick/varexporter/LICENSE b/app/vendor/brick/varexporter/LICENSE new file mode 100644 index 000000000..8f97e0be1 --- /dev/null +++ b/app/vendor/brick/varexporter/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2019-present Benjamin Morel + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/app/vendor/brick/varexporter/composer.json b/app/vendor/brick/varexporter/composer.json new file mode 100644 index 000000000..f627551c0 --- /dev/null +++ b/app/vendor/brick/varexporter/composer.json @@ -0,0 +1,28 @@ +{ + "name": "brick/varexporter", + "description": "A powerful alternative to var_export(), which can export closures and objects without __set_state()", + "type": "library", + "keywords": [ + "var_export" + ], + "license": "MIT", + "require": { + "php": "^7.2 || ^8.0", + "nikic/php-parser": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.0", + "php-coveralls/php-coveralls": "^2.2", + "vimeo/psalm": "4.4.1" + }, + "autoload": { + "psr-4": { + "Brick\\VarExporter\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Brick\\VarExporter\\Tests\\": "tests/" + } + } +} diff --git a/app/vendor/brick/varexporter/src/ExportException.php b/app/vendor/brick/varexporter/src/ExportException.php new file mode 100644 index 000000000..888cf9acd --- /dev/null +++ b/app/vendor/brick/varexporter/src/ExportException.php @@ -0,0 +1,36 @@ + child object id => path where the object first appeared. + * + * @var array> + */ + private $visitedObjects = []; + + /** + * @psalm-readonly + * + * @var bool + */ + public $addTypeHints; + + /** + * @psalm-readonly + * + * @var bool + */ + public $skipDynamicProperties; + + /** + * @psalm-readonly + * + * @var bool + */ + public $inlineNumericScalarArray; + + /** + * @psalm-readonly + * + * @var bool + */ + public $closureSnapshotUses; + + /** + * @psalm-readonly + * + * @var bool + */ + public $trailingCommaInArray; + + /** + * @psalm-readonly + * + * @var int + */ + public $indentLevel; + + /** + * @param int $options + * @param int Indentation level + */ + public function __construct(int $options, int $indentLevel = 0) + { + $this->objectExporters[] = new ObjectExporter\StdClassExporter($this); + + if (! ($options & VarExporter::NO_CLOSURES)) { + $this->objectExporters[] = new ObjectExporter\ClosureExporter($this); + } + + if (! ($options & VarExporter::NO_SET_STATE)) { + $this->objectExporters[] = new ObjectExporter\SetStateExporter($this); + } + + $this->objectExporters[] = new ObjectExporter\InternalClassExporter($this); + + if (! ($options & VarExporter::NO_SERIALIZE)) { + $this->objectExporters[] = new ObjectExporter\SerializeExporter($this); + } + + if (! ($options & VarExporter::NOT_ANY_OBJECT)) { + $this->objectExporters[] = new ObjectExporter\AnyObjectExporter($this); + } + + $this->addTypeHints = (bool) ($options & VarExporter::ADD_TYPE_HINTS); + $this->skipDynamicProperties = (bool) ($options & VarExporter::SKIP_DYNAMIC_PROPERTIES); + $this->inlineNumericScalarArray = (bool) ($options & VarExporter::INLINE_NUMERIC_SCALAR_ARRAY); + $this->closureSnapshotUses = (bool) ($options & VarExporter::CLOSURE_SNAPSHOT_USES); + $this->trailingCommaInArray = (bool) ($options & VarExporter::TRAILING_COMMA_IN_ARRAY); + + $this->indentLevel = $indentLevel; + } + + /** + * @param mixed $var The variable to export. + * @param string[] $path The path to the current variable in the array/object graph. + * @param int[] $parentIds The ids of all objects higher in the graph. + * + * @return string[] The lines of code. + * + * @throws ExportException + */ + public function export($var, array $path, array $parentIds) : array + { + switch ($type = gettype($var)) { + case 'boolean': + case 'integer': + case 'double': + case 'string': + return [var_export($var, true)]; + + case 'NULL': + // lowercase null + return ['null']; + + case 'array': + /** @var array $var */ + return $this->exportArray($var, $path, $parentIds); + + case 'object': + /** @var object $var */ + return $this->exportObject($var, $path, $parentIds); + + default: + // resources + throw new ExportException(sprintf('Type "%s" is not supported.', $type), $path); + } + } + + /** + * @psalm-suppress MixedAssignment + * + * @param array $array The array to export. + * @param string[] $path The path to the current array in the array/object graph. + * @param int[] $parentIds The ids of all objects higher in the graph. + * + * @return string[] The lines of code. + * + * @throws ExportException + */ + public function exportArray(array $array, array $path, array $parentIds) : array + { + if (! $array) { + return ['[]']; + } + + $result = []; + + $count = count($array); + $isNumeric = array_keys($array) === range(0, $count - 1); + + $current = 0; + + $inline = ($this->inlineNumericScalarArray && $isNumeric && $this->isScalarArray($array)); + + foreach ($array as $key => $value) { + $isLast = (++$current === $count); + + $newPath = $path; + $newPath[] = (string) $key; + + $exported = $this->export($value, $newPath, $parentIds); + + if ($inline) { + $result[] = $exported[0]; + } else { + $prepend = ''; + $append = ''; + + if (! $isNumeric) { + $prepend = var_export($key, true) . ' => '; + } + + if (! $isLast || $this->trailingCommaInArray) { + $append = ','; + } + + $exported = $this->wrap($exported, $prepend, $append); + $exported = $this->indent($exported); + + $result = array_merge($result, $exported); + } + } + + if ($inline) { + return ['[' . implode(', ', $result) . ']']; + } + + array_unshift($result, '['); + $result[] = ']'; + + return $result; + } + + /** + * Returns whether the given array only contains scalar values. + * + * Types considered scalar here are int, bool, float, string and null. + * If the array is empty, this method returns true. + * + * @param array $array + * + * @return bool + */ + private function isScalarArray(array $array) : bool + { + foreach ($array as $value) { + if ($value !== null && ! is_scalar($value)) { + return false; + } + } + + return true; + } + + /** + * @param object $object The object to export. + * @param string[] $path The path to the current object in the array/object graph. + * @param int[] $parentIds The ids of all objects higher in the graph. + * + * @return string[] The lines of code. + * + * @throws ExportException + */ + public function exportObject(object $object, array $path, array $parentIds) : array + { + $id = spl_object_id($object); + + foreach ($parentIds as $parentId) { + if (isset($this->visitedObjects[$parentId][$id])) { + throw new ExportException(sprintf( + 'Object of class "%s" has a circular reference at %s. ' . + 'Circular references are currently not supported.', + get_class($object), + ExportException::pathToString($this->visitedObjects[$parentId][$id]) + ), $path); + } + + $this->visitedObjects[$parentId][$id] = $path; + } + + $reflectionObject = new \ReflectionObject($object); + + foreach ($this->objectExporters as $objectExporter) { + if ($objectExporter->supports($reflectionObject)) { + return $objectExporter->export($object, $reflectionObject, $path, $parentIds); + } + } + + // This may only happen when an option is given to disallow specific export methods. + + $className = $reflectionObject->getName(); + + throw new ExportException('Class "' . $className . '" cannot be exported using the current options.', $path); + } + + /** + * Indents every non-empty line. + * + * @param string[] $lines The lines of code. + * + * @return string[] The indented lines of code. + */ + public function indent(array $lines) : array + { + foreach ($lines as & $value) { + if ($value !== '') { + $value = ' ' . $value; + } + } + + return $lines; + } + + /** + * @param string[] $lines The lines of code. + * @param string $prepend The string to prepend to the first line. + * @param string $append The string to append to the last line. + * + * @return string[] + */ + public function wrap(array $lines, string $prepend, string $append) : array + { + $lines[0] = $prepend . $lines[0]; + $lines[count($lines) - 1] .= $append; + + return $lines; + } +} diff --git a/app/vendor/brick/varexporter/src/Internal/ObjectExporter.php b/app/vendor/brick/varexporter/src/Internal/ObjectExporter.php new file mode 100644 index 000000000..9b34614ee --- /dev/null +++ b/app/vendor/brick/varexporter/src/Internal/ObjectExporter.php @@ -0,0 +1,96 @@ +exporter = $exporter; + } + + /** + * Returns whether this exporter supports the given object. + * + * @param \ReflectionObject $reflectionObject A reflection of the object. + * + * @return bool + */ + abstract public function supports(\ReflectionObject $reflectionObject) : bool; + + /** + * Exports the given object. + * + * @param object $object The object to export. + * @param \ReflectionObject $reflectionObject A reflection of the object. + * @param string[] $path The path to the current object in the array/object graph. + * @param int[] $parentIds The ids of all objects higher in the graph. + * + * @return string[] The lines of code. + * + * @throws ExportException + */ + abstract public function export(object $object, \ReflectionObject $reflectionObject, array $path, array $parentIds) : array; + + /** + * Returns the code to create a new object of the given class. + * + * If the class has a constructor, reflection will be used to bypass it. + * + * @param \ReflectionClass $class + * + * @return string[] The lines of code. + */ + final protected function getCreateObjectCode(\ReflectionClass $class) : array + { + $className = '\\' . $class->getName(); + + if ($class->getConstructor() === null) { + return ['$object = new ' . $className . ';']; + } + + $lines = ['$class = new \ReflectionClass(' . $className . '::class);']; + + if ($this->exporter->addTypeHints) { + $lines[] = ''; + $lines[] = '/** @var ' . $className . ' $object */'; + } + + $lines[] = '$object = $class->newInstanceWithoutConstructor();'; + + return $lines; + } + + /** + * Wraps the given PHP code in a static closure. + * + * @param string[] $code The lines of code. + * + * @return string[] The lines of code, wrapped in a closure. + */ + final protected function wrapInClosure(array $code) : array + { + return array_merge( + ['(static function() {'], + $this->exporter->indent($code), + ['})()'] + ); + } +} diff --git a/app/vendor/brick/varexporter/src/Internal/ObjectExporter/AnyObjectExporter.php b/app/vendor/brick/varexporter/src/Internal/ObjectExporter/AnyObjectExporter.php new file mode 100644 index 000000000..5449e71ab --- /dev/null +++ b/app/vendor/brick/varexporter/src/Internal/ObjectExporter/AnyObjectExporter.php @@ -0,0 +1,191 @@ +getCreateObjectCode($reflectionObject); + + $objectAsArray = (array) $object; + + $current = $this->exporter->skipDynamicProperties + ? new \ReflectionClass($object) // properties from class definition only + : $reflectionObject; // properties from class definition + dynamic properties + + $isParentClass = false; + + $returnNewObject = ($reflectionObject->getConstructor() === null); + + while ($current) { + $publicProperties = []; + $nonPublicProperties = []; + $unsetPublicProperties = []; + $unsetNonPublicProperties = []; + + foreach ($current->getProperties() as $property) { + if ($property->isStatic()) { + continue; + } + + if ($isParentClass && ! $property->isPrivate()) { + // property already handled in the child class. + continue; + } + + $name = $property->getName(); + + // getting the property value through the object to array cast, and not through reflection, as this is + // currently the only way to know whether a declared property has been unset - at least before PHP 7.4, + // which will bring ReflectionProperty::isInitialized(). + + $key = $this->getPropertyKey($property); + + if (array_key_exists($key, $objectAsArray)) { + $value = $objectAsArray[$key]; + + if ($property->isPublic()) { + $publicProperties[$name] = $value; + } else { + $nonPublicProperties[$name] = $value; + } + } else { + if ($property->isPublic()) { + $unsetPublicProperties[] = $name; + } else { + $unsetNonPublicProperties[] = $name; + } + } + + $returnNewObject = false; + } + + if ($publicProperties || $unsetPublicProperties) { + $lines[] = ''; + + foreach ($publicProperties as $name => $value) { + /** @psalm-suppress RedundantCast See: https://github.com/vimeo/psalm/issues/4891 */ + $name = (string) $name; + + $newPath = $path; + $newPath[] = $name; + + $newParentIds = $parentIds; + $newParentIds[] = spl_object_id($object); + + $exportedValue = $this->exporter->export($value, $newPath, $newParentIds); + $exportedValue = $this->exporter->wrap($exportedValue, '$object->' . $this->escapePropName($name) . ' = ', ';'); + $lines = array_merge($lines, $exportedValue); + } + + foreach ($unsetPublicProperties as $name) { + $lines[] = 'unset($object->' . $this->escapePropName($name) . ');'; + } + } + + if ($nonPublicProperties || $unsetNonPublicProperties) { + $closureLines = []; + + if ($this->exporter->addTypeHints) { + $closureLines[] = '/** @var \\' . $current->getName() . ' $this */'; + } + + foreach ($nonPublicProperties as $name => $value) { + $newPath = $path; + $newPath[] = $name; + + $newParentIds = $parentIds; + $newParentIds[] = spl_object_id($object); + + $exportedValue = $this->exporter->export($value, $newPath, $newParentIds); + $exportedValue = $this->exporter->wrap($exportedValue, '$this->' . $this->escapePropName($name) . ' = ', ';'); + $closureLines = array_merge($closureLines, $exportedValue); + } + + foreach ($unsetNonPublicProperties as $name) { + $closureLines[] = 'unset($this->' . $this->escapePropName($name) . ');'; + } + + $lines[] = ''; + $lines[] = '(function() {'; + $lines = array_merge($lines, $this->exporter->indent($closureLines)); + $lines[] = '})->bindTo($object, \\' . $current->getName() . '::class)();'; + } + + $current = $current->getParentClass(); + $isParentClass = true; + } + + if ($returnNewObject) { + // no constructor, no properties + return ['new \\' . $reflectionObject->getName()]; + } + + $lines[] = ''; + $lines[] = 'return $object;'; + + return $this->wrapInClosure($lines); + } + + /** + * Returns the key of the given property in the object-to-array cast. + * + * @param \ReflectionProperty $property + * + * @return string + */ + private function getPropertyKey(\ReflectionProperty $property) : string + { + $name = $property->getName(); + + if ($property->isPrivate()) { + return "\0" . $property->getDeclaringClass()->getName() . "\0" . $name; + } + + if ($property->isProtected()) { + return "\0*\0" . $name; + } + + return $name; + } + + /** + * @param string $var + * + * @return string + */ + private function escapePropName(string $var) : string + { + if (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $var) === 1) { + return $var; + } + + return '{' . var_export($var, true) . '}'; + } +} diff --git a/app/vendor/brick/varexporter/src/Internal/ObjectExporter/ClosureExporter.php b/app/vendor/brick/varexporter/src/Internal/ObjectExporter/ClosureExporter.php new file mode 100644 index 000000000..6cfb9f57f --- /dev/null +++ b/app/vendor/brick/varexporter/src/Internal/ObjectExporter/ClosureExporter.php @@ -0,0 +1,272 @@ +getName() === \Closure::class; + } + + /** + * {@inheritDoc} + */ + public function export($object, \ReflectionObject $reflectionObject, array $path, array $parentIds) : array + { + assert($object instanceof Closure); + + $reflectionFunction = new \ReflectionFunction($object); + + $file = $reflectionFunction->getFileName(); + $line = $reflectionFunction->getStartLine(); + + $ast = $this->parseFile($file, $path); + $ast = $this->resolveNames($ast); + + $closure = $this->getClosure($reflectionFunction, $ast, $file, $line, $path); + + $prettyPrinter = new ClosureExporter\PrettyPrinter(); + $prettyPrinter->setVarExporterNestingLevel(count($path) + $this->exporter->indentLevel); + + $code = $prettyPrinter->prettyPrintExpr($closure); + + // Consider the pretty-printer output as a single line, to avoid breaking multiline quoted strings and + // heredocs / nowdocs. We must leave the indenting responsibility to the pretty-printer. + + return [$code]; + } + + /** + * @return Parser + */ + private function getParser() + { + if ($this->parser === null) { + $this->parser = (new ParserFactory)->create(ParserFactory::ONLY_PHP7); + } + + return $this->parser; + } + + /** + * Parses the given source file. + * + * @param string $filename The source file name. + * @param string[] $path The path to the closure in the array/object graph. + * + * @return Node\Stmt[] The AST. + * + * @throws ExportException + */ + private function parseFile(string $filename, array $path) : array + { + if (substr($filename, -16) === " : eval()'d code") { + throw new ExportException("Closure defined in eval()'d code cannot be exported.", $path); + } + + $source = @ file_get_contents($filename); + + if ($source === false) { + // @codeCoverageIgnoreStart + throw new ExportException("Cannot open source file \"$filename\" for reading closure code.", $path); + // @codeCoverageIgnoreEnd + } + + try { + $nodes = $this->getParser()->parse($source); + + // throwing error handler + assert($nodes !== null); + + return $nodes; + // @codeCoverageIgnoreStart + } catch (Error $e) { + throw new ExportException("Cannot parse file \"$filename\" for reading closure code.", $path, $e); + // @codeCoverageIgnoreEnd + } + } + + /** + * Resolves namespaced names in the AST. + * + * @param Node[] $ast + * + * @return Node[] + */ + private function resolveNames(array $ast) : array + { + $nameResolver = new NameResolver(); + $nodeTraverser = new NodeTraverser(); + $nodeTraverser->addVisitor($nameResolver); + + return $nodeTraverser->traverse($ast); + } + + /** + * Finds a closure in the source file and returns its node. + * + * @param ReflectionFunction $reflectionFunction Reflection of the closure. + * @param Node[] $ast The AST. + * @param string $file The file name. + * @param int $line The line number where the closure is located in the source file. + * @param string[] $path The path to the closure in the array/object graph. + * + * @return Node\Expr\Closure + * + * @throws ExportException + */ + private function getClosure( + ReflectionFunction $reflectionFunction, + array $ast, + string $file, + int $line, + array $path + ) : Node\Expr\Closure { + $finder = new FindingVisitor(function(Node $node) use ($line) : bool { + return ($node instanceof Node\Expr\Closure || $node instanceof Node\Expr\ArrowFunction) + && $node->getStartLine() === $line; + }); + + $traverser = new NodeTraverser(); + $traverser->addVisitor($finder); + $traverser->traverse($ast); + + $closures = $finder->getFoundNodes(); + $count = count($closures); + + if ($count !== 1) { + throw new ExportException(sprintf( + 'Expected exactly 1 closure in %s on line %d, found %d.', + $file, + $line, + $count + ), $path); + } + + /** @var Node\Expr\Closure|Node\Expr\ArrowFunction $closure */ + $closure = $closures[0]; + + if ($closure instanceof Node\Expr\ArrowFunction) { + $closure = $this->convertArrowFunction($reflectionFunction, $closure); + } + + if ($closure->uses) { + $this->closureHandleUses($reflectionFunction, $closure, $path); + } + + return $closure; + } + + /** + * Convert a parsed arrow function to a closure. + * + * @param ReflectionFunction $reflectionFunction Reflection of the closure. + * @param Node\Expr\ArrowFunction $arrowFunction Parsed arrow function. + * + * @return Node\Expr\Closure + */ + private function convertArrowFunction( + ReflectionFunction $reflectionFunction, + Node\Expr\ArrowFunction $arrowFunction + ) : Node\Expr\Closure { + $closure = new Node\Expr\Closure([], ['arrow_function' => true]); + + $closure->static = false; + $closure->params = $arrowFunction->params; + $closure->returnType = $arrowFunction->returnType; + + $closure->stmts[] = new Node\Stmt\Return_($arrowFunction->expr); + + $static = $reflectionFunction->getStaticVariables(); + + foreach (array_keys($static) as $var) { + assert(is_string($var)); + + $closure->uses[] = new Node\Expr\ClosureUse( + new Node\Expr\Variable($var) + ); + } + + return $closure; + } + + /** + * Handle `use` part of closure. + * + * @param ReflectionFunction $reflectionFunction Reflection of the closure. + * @param Node\Expr\Closure $closure Parsed closure. + * @param string[] $path The path to the closure in the array/object graph. + * + * @throws ExportException + */ + private function closureHandleUses( + ReflectionFunction $reflectionFunction, + Node\Expr\Closure $closure, + array $path + ) : void { + if (! $this->exporter->closureSnapshotUses) { + $message = $closure->hasAttribute('arrow_function') + ? "The arrow function uses variables in the parent scope, this is not supported by default" + : "The closure has bound variables through 'use', this is not supported by default"; + + throw new ExportException("$message. Use the CLOSURE_SNAPSHOT_USE option to export them.", $path); + } + + $static = $reflectionFunction->getStaticVariables(); + $stmts = []; + + $parser = $this->getParser(); + + foreach ($closure->uses as $use) { + $var = $use->var->name; + + assert(is_string($var)); + + $export = array_merge(['exporter->export($static[$var], $path, []), [';']); + $nodes = $parser->parse(implode(PHP_EOL, $export)); + + // throwing error handler + assert($nodes !== null); + + /** @var Node\Stmt\Expression $expr */ + $expr = $nodes[0]; + + $assign = new Node\Expr\Assign( + new Node\Expr\Variable($var), + $expr->expr + ); + $stmts[] = new Node\Stmt\Expression($assign); + } + + $closure->uses = []; + $closure->stmts = array_merge($stmts, $closure->stmts); + } +} diff --git a/app/vendor/brick/varexporter/src/Internal/ObjectExporter/ClosureExporter/PrettyPrinter.php b/app/vendor/brick/varexporter/src/Internal/ObjectExporter/ClosureExporter/PrettyPrinter.php new file mode 100644 index 000000000..4010810a4 --- /dev/null +++ b/app/vendor/brick/varexporter/src/Internal/ObjectExporter/ClosureExporter/PrettyPrinter.php @@ -0,0 +1,41 @@ +varExporterNestingLevel = $level; + } + + /** + * {@inheritDoc} + */ + protected function resetState() : void + { + parent::resetState(); + + $this->indentLevel = 4 * $this->varExporterNestingLevel; + $this->nl = "\n" . str_repeat(' ', $this->indentLevel); + } +} diff --git a/app/vendor/brick/varexporter/src/Internal/ObjectExporter/InternalClassExporter.php b/app/vendor/brick/varexporter/src/Internal/ObjectExporter/InternalClassExporter.php new file mode 100644 index 000000000..1deb23e61 --- /dev/null +++ b/app/vendor/brick/varexporter/src/Internal/ObjectExporter/InternalClassExporter.php @@ -0,0 +1,34 @@ +isInternal(); + } + + /** + * {@inheritDoc} + */ + public function export($object, \ReflectionObject $reflectionObject, array $path, array $parentIds) : array + { + $className = $reflectionObject->getName(); + + throw new ExportException('Class "' . $className . '" is internal, and cannot be exported.', $path); + } +} diff --git a/app/vendor/brick/varexporter/src/Internal/ObjectExporter/SerializeExporter.php b/app/vendor/brick/varexporter/src/Internal/ObjectExporter/SerializeExporter.php new file mode 100644 index 000000000..7c826c3ac --- /dev/null +++ b/app/vendor/brick/varexporter/src/Internal/ObjectExporter/SerializeExporter.php @@ -0,0 +1,50 @@ +hasMethod('__serialize') + && $reflectionObject->hasMethod('__unserialize'); + } + + /** + * {@inheritDoc} + */ + public function export($object, \ReflectionObject $reflectionObject, array $path, array $parentIds) : array + { + $lines = $this->getCreateObjectCode($reflectionObject); + + $lines[] = ''; + + /** + * @psalm-suppress MixedAssignment + * @psalm-suppress MixedMethodCall + */ + $values = $object->__serialize(); + + $exportedValues = $this->exporter->export($values, $path, $parentIds); + $exportedValues = $this->exporter->wrap($exportedValues, '$object->__unserialize(', ');'); + + $lines = array_merge($lines, $exportedValues); + + $lines[] = ''; + $lines[] = 'return $object;'; + + return $this->wrapInClosure($lines); + } +} diff --git a/app/vendor/brick/varexporter/src/Internal/ObjectExporter/SetStateExporter.php b/app/vendor/brick/varexporter/src/Internal/ObjectExporter/SetStateExporter.php new file mode 100644 index 000000000..4963dd260 --- /dev/null +++ b/app/vendor/brick/varexporter/src/Internal/ObjectExporter/SetStateExporter.php @@ -0,0 +1,114 @@ +hasMethod('__set_state')) { + $method = $reflectionObject->getMethod('__set_state'); + + return $method->isPublic() && $method->isStatic(); + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function export($object, \ReflectionObject $reflectionObject, array $path, array $parentIds) : array + { + $className = $reflectionObject->getName(); + + $vars = $this->getObjectVars($object, $path); + + $exportedVars = $this->exporter->exportArray($vars, $path, $parentIds); + $exportedVars = $this->exporter->wrap($exportedVars, '\\' . $className . '::__set_state(', ')'); + + return $exportedVars; + } + + /** + * Returns public and private object properties, as an associative array. + * + * This is unlike get_object_vars(), which only returns properties accessible from the current scope. + * + * The returned values are in line with those returned by var_export() in the array passed to __set_state(); unlike + * var_export() however, this method throws an exception if the object has overridden private properties, as this + * would result in a conflict in array keys. In this case, var_export() would return multiple values in the output, + * which once executed would yield an array containing only the last value for this key in the output. + * + * This way we offer a better safety guarantee, while staying compatible with var_export() in the output. + * + * @psalm-suppress MixedAssignment + * + * @param object $object The object to dump. + * @param string[] $path The path to the object, in the array/object graph. + * + * @return array An associative array of property name to value. + * + * @throws ExportException + */ + private function getObjectVars(object $object, array $path) : array + { + $result = []; + + foreach ((array) $object as $name => $value) { + $name = (string) $name; + $pos = strrpos($name, "\0"); + + if ($pos !== false) { + $name = substr($name, $pos + 1); + } + + assert($name !== false); + + if (array_key_exists($name, $result)) { + $className = get_class($object); + + throw new ExportException( + 'Class "' . $className . '" has overridden private property "' . $name . '". ' . + 'This is not supported for exporting objects with __set_state().', + $path + ); + } + + if ($this->exporter->skipDynamicProperties && $this->isDynamicProperty($object, $name)) { + continue; + } + + $result[$name] = $value; + } + + return $result; + } + + /** + * @param object $object + * @param string $name + * + * @return bool + */ + private function isDynamicProperty(object $object, string $name) : bool + { + $reflectionClass = new \ReflectionClass($object); + $reflectionObject = new \ReflectionObject($object); + + return $reflectionObject->hasProperty($name) && ! $reflectionClass->hasProperty($name); + } +} diff --git a/app/vendor/brick/varexporter/src/Internal/ObjectExporter/StdClassExporter.php b/app/vendor/brick/varexporter/src/Internal/ObjectExporter/StdClassExporter.php new file mode 100644 index 000000000..52fc0a97c --- /dev/null +++ b/app/vendor/brick/varexporter/src/Internal/ObjectExporter/StdClassExporter.php @@ -0,0 +1,35 @@ +getName() === \stdClass::class; + } + + /** + * {@inheritDoc} + */ + public function export($object, \ReflectionObject $reflectionObject, array $path, array $parentIds) : array + { + $exported = $this->exporter->exportArray((array) $object, $path, $parentIds); + + $exported[0] = '(object) ' . $exported[0]; + + return $exported; + } +} diff --git a/app/vendor/brick/varexporter/src/VarExporter.php b/app/vendor/brick/varexporter/src/VarExporter.php new file mode 100644 index 000000000..c2717683f --- /dev/null +++ b/app/vendor/brick/varexporter/src/VarExporter.php @@ -0,0 +1,98 @@ +export($var, [], []); + + if ($indentLevel < 1 || count($lines) < 2) { + $export = implode(PHP_EOL, $lines); + } else { + $firstLine = array_shift($lines); + $lines = array_map(function ($line) use ($indentLevel) { + return str_repeat(' ', $indentLevel) . $line; + }, $lines); + + $export = $firstLine . PHP_EOL . implode(PHP_EOL, $lines); + } + + if ($options & self::ADD_RETURN) { + return 'return ' . $export . ';' . PHP_EOL; + } + + return $export; + } +} diff --git a/app/vendor/cakephp/bake/.appveyor.yml b/app/vendor/cakephp/bake/.appveyor.yml deleted file mode 100644 index ed138279f..000000000 --- a/app/vendor/cakephp/bake/.appveyor.yml +++ /dev/null @@ -1,48 +0,0 @@ -build: false -platform: 'x64' -clone_folder: C:\projects\bake -clone_depth: 10 - -services: - - mssql2012sp1 - -environment: - global: - db_dsn: 'sqlserver://sa:Password12!@.\SQL2012SP1/cakephp?MultipleActiveResultSets=false' - -init: - - SET PATH=C:\Program Files\OpenSSL;c:\php;c:\composer;%PATH% - - SET COMPOSER_NO_INTERACTION=1 - - SET ANSICON=121x90 (121x90) - -install: - - curl -fsS https://windows.php.net/downloads/releases/latest/php-7.2-nts-Win32-VC15-x64-latest.zip -o php.zip - - curl -fsS https://windows.php.net/downloads/pecl/releases/pdo_sqlsrv/5.6.1/php_pdo_sqlsrv-5.6.1-7.2-nts-vc15-x64.zip -o pdosqlsrv.zip - - curl -fsS https://windows.php.net/downloads/pecl/releases/sqlsrv/5.6.1/php_sqlsrv-5.6.1-7.2-nts-vc15-x64.zip -o sqlsrv.zip - - 7z x php.zip -oC:\php\ -aoa > nul - - 7z x pdosqlsrv.zip -oC:\php\ext php_pdo_sqlsrv.dll -aoa > nul - - 7z x sqlsrv.zip -oC:\php\ext php_sqlsrv.dll -aoa > nul - - - cd C:\php - - copy php.ini-production php.ini /Y - - echo date.timezone="UTC" >> php.ini - - echo extension_dir=ext >> php.ini - - echo extension=openssl >> php.ini - - echo extension=mbstring >> php.ini - - echo extension=intl >> php.ini - - echo extension=pdo_sqlsrv >> php.ini - - echo extension=sqlsrv >> php.ini - - php -v - - - mkdir C:\composer - - cd C:\composer - - appveyor-retry appveyor DownloadFile https://getcomposer.org/composer.phar - - - cd C:\projects\bake - - php C:\composer\composer.phar update --no-progress - - php C:\composer\composer.phar show - -test_script: - - sqlcmd -S localhost,1433 -U sa -P Password12! -Q "create database cakephp;" - - cd c:\projects\bake - - vendor\bin\phpunit diff --git a/app/vendor/cakephp/bake/README.md b/app/vendor/cakephp/bake/README.md index 4c6455154..bd99b84b7 100644 --- a/app/vendor/cakephp/bake/README.md +++ b/app/vendor/cakephp/bake/README.md @@ -1,6 +1,6 @@ # Bake plugin for CakePHP -[![Build Status](https://img.shields.io/travis/com/cakephp/bake/master.svg?style=flat-square)](https://travis-ci.com/cakephp/bake) +![Build Status](https://github.com/cakephp/bake/actions/workflows/ci.yml/badge.svg?branch=master) [![Latest Stable Version](https://img.shields.io/github/v/release/cakephp/bake?sort=semver&style=flat-square)](https://packagist.org/packages/cakephp/bake) [![Coverage Status](https://img.shields.io/codecov/c/github/cakephp/bake.svg?style=flat-square)](https://codecov.io/github/cakephp/bake) [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.txt) diff --git a/app/vendor/cakephp/bake/composer.json b/app/vendor/cakephp/bake/composer.json index 104afae34..cd74c330b 100644 --- a/app/vendor/cakephp/bake/composer.json +++ b/app/vendor/cakephp/bake/composer.json @@ -19,13 +19,15 @@ }, "require": { "php": ">=7.2", - "cakephp/cakephp": "^4.0", - "cakephp/twig-view": "^1.0" + "cakephp/cakephp": "^4.1", + "cakephp/twig-view": "^1.0.2", + "brick/varexporter": "^0.3.5" }, "require-dev": { "cakephp/cakephp-codesniffer": "^4.0", "phpunit/phpunit": "~8.5.0", - "cakephp/debug_kit": "^4.1" + "cakephp/debug_kit": "^4.1", + "cakephp/plugin-installer": "^1.3" }, "autoload": { "psr-4": { @@ -37,6 +39,7 @@ "BakeTest\\": "tests/test_app/Plugin/BakeTest/src/", "Company\\Pastry\\": "tests/test_app/Plugin/Company/Pastry/src/", "Pastry\\PastryTest\\": "tests/test_app/Plugin/PastryTest/src/", + "WithBakeSubFolder\\": "tests/test_app/Plugin/WithBakeSubFolder/src/", "Bake\\Test\\": "tests/", "Bake\\Test\\App\\": "tests/test_app/App/", "Cake\\Test\\": "vendor/cakephp/cakephp/tests/" @@ -50,7 +53,7 @@ "cs-check": "phpcs --parallel=16 -p src/ tests/", "cs-fix": "phpcbf --parallel=16 -p src/ tests/", "stan": "phpstan analyse src/ && psalm.phar", - "stan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan:^0.12.7 psalm/phar:~3.17.2 && mv composer.backup composer.json", + "stan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan:0.12.82 psalm/phar:~4.7.0 && mv composer.backup composer.json", "test": "phpunit", "test-coverage": "phpunit --coverage-clover=clover.xml" } diff --git a/app/vendor/cakephp/bake/docs/en/development.rst b/app/vendor/cakephp/bake/docs/en/development.rst index 369367ad7..e1c1aa7bf 100644 --- a/app/vendor/cakephp/bake/docs/en/development.rst +++ b/app/vendor/cakephp/bake/docs/en/development.rst @@ -17,12 +17,12 @@ output, for example to add another helper to the bake view class this event can be used:: on('Bake.initialize', function (Event $event) { + // in src/Application::bootstrapCli() + + EventManager::instance()->on('Bake.initialize', function (EventInterface $event) { $view = $event->getSubject(); // In my bake templates, allow the use of the MySpecial helper @@ -30,24 +30,20 @@ be used:: // And add an $author variable so it's always available $view->set('author', 'Andy'); - }); -If you want to modify bake from within another plugin, putting your plugin's -bake events in the plugin ``config/bootstrap.php`` file is a good idea. - Bake events can be handy for making small changes to existing templates. For example, to change the variable names used when baking controller/template files one can use a function listening for ``Bake.beforeRender`` to modify the variables used in the bake templates:: on('Bake.beforeRender', function (Event $event) { + // in src/Application::bootstrapCli() + + EventManager::instance()->on('Bake.beforeRender', function (EventInterface $event) { $view = $event->getSubject(); // Use $rows for the main data variable in indexes @@ -65,7 +61,6 @@ variables used in the bake templates:: if ($view->get('singularVar')) { $view->set('singularVar', 'theOne'); } - }); You may also scope the ``Bake.beforeRender`` and ``Bake.afterRender`` events to @@ -74,17 +69,17 @@ your UsersController when generating from a **Controller/controller.twig** file, you can use the following event:: on( 'Bake.beforeRender.Controller.controller', - function (Event $event) { + function (EventInterface $event) { $view = $event->getSubject(); - if ($view->viewVars['name'] == 'Users') { + if ($view->get('name') === 'Users') { // add the login and logout actions to the Users controller $view->set('actions', [ 'login', @@ -112,57 +107,97 @@ to modify bake template files, is to bake a class and compare the template used with the pre-processed template file which is left in the application's **tmp/bake** folder. -So, for example, when baking a shell like so: +So, for example, when baking a command like so: .. code-block:: bash - bin/cake bake shell Foo + bin/cake bake command Foo -The template used (**vendor/cakephp/bake/templates/bake/Shell/shell.twig**) +The template used (**vendor/cakephp/bake/templates/bake/Command/command.twig**) looks like this:: Test->classSuffixes[$this->name()])) { - $this->Test->classSuffixes[$this->name()] = 'Foo'; + $this->Test->classSuffixes[$this->name()] = 'Foo'; } $name = ucfirst($this->name()); if (!isset($this->Test->classTypes[$name])) { - $this->Test->classTypes[$name] = 'Foo'; + $this->Test->classTypes[$name] = 'Foo'; } return parent::bakeTest($className); @@ -295,7 +337,7 @@ how you can load your own helper so that it can be used in bake templates:: on( - 'Bake.initialize', + 'Bake.initialize', function ($event, $view) { $view->loadHelper('Foo'); } diff --git a/app/vendor/cakephp/bake/docs/en/index.rst b/app/vendor/cakephp/bake/docs/en/index.rst index fc697c5fc..28899b7b0 100644 --- a/app/vendor/cakephp/bake/docs/en/index.rst +++ b/app/vendor/cakephp/bake/docs/en/index.rst @@ -14,7 +14,7 @@ Installation Before trying to use or extend bake, make sure it is installed in your application. Bake is provided as a plugin that you can install with Composer:: - composer require --dev cakephp/bake:~2.0 + composer require --dev cakephp/bake:"^2.0" The above will install bake as a development dependency. This means that it will not be installed when you do production deployments. diff --git a/app/vendor/cakephp/bake/docs/en/usage.rst b/app/vendor/cakephp/bake/docs/en/usage.rst index efac63f1b..d68a745f0 100644 --- a/app/vendor/cakephp/bake/docs/en/usage.rst +++ b/app/vendor/cakephp/bake/docs/en/usage.rst @@ -14,112 +14,41 @@ If you have problems running the script, ensure that: Before running bake you should make sure you have at least one database connection configured. -When run with no arguments ``bin/cake bake`` will output a list of available -tasks. - -For windows system try with ``bin\cake bake``. - -You should see something like:: - - $ bin/cake bake - - Welcome to CakePHP v3.4.6 Console - --------------------------------------------------------------- - App : src - Path: /var/www/cakephp.dev/src/ - PHP : 5.6.20 - --------------------------------------------------------------- - The following commands can be used to generate skeleton code for your application. - - Available bake commands: - - - all - - behavior - - cell - - component - - controller - - fixture - - form - - helper - - mailer - - migration - - migration_diff - - migration_snapshot - - model - - plugin - - seed - - shell - - shell_helper - - task - - template - - test - - By using `cake bake [name]` you can invoke a specific bake task. - -You can get more information on what each task does, and what options are -available using the ``--help`` option:: +You can get the list of available bake command by running ``bin/cake bake --help`` +(For Windows usage ``bin\cake bake --help``) :: $ bin/cake bake --help - - Welcome to CakePHP v3.4.6 Console - --------------------------------------------------------------- - App : src - Path: /var/www/cakephp.dev/src/ - PHP : 5.6.20 - --------------------------------------------------------------- - The Bake script generates controllers, models and template files for - your application. If run with no command line arguments, Bake guides the - user through the class creation process. You can customize the - generation process by telling Bake where different parts of your - application are using command line arguments. - - Usage: - cake bake.bake [subcommand] [options] - - Subcommands: - - all Bake a complete MVC skeleton. - behavior Bake a behavior class file. - cell Bake a cell class file. - component Bake a component class file. - controller Bake a controller skeleton. - fixture Generate fixtures for use with the test suite. You - can use `bake fixture all` to bake all fixtures. - form Bake a form class file. - helper Bake a helper class file. - mailer Bake a mailer class file. - migration Bake migration class. - migration_diff Bake migration class. - migration_snapshot Bake migration snapshot class. - model Bake table and entity classes. - plugin Create the directory structure, AppController class - and testing setup for a new plugin. Can create - plugins in any of your bootstrapped plugin paths. - seed Bake seed class. - shell Bake a shell class file. - shell_helper Bake a shell_helper class file. - task Bake a task class file. - template Bake views for a controller, using built-in or - custom templates. - test Bake test case skeletons for classes. - - To see help on a subcommand use `cake bake.bake [subcommand] --help` - - Options: - - --connection, -c Database connection to use in conjunction with `bake - all`. (default: default) - --everything Bake a complete MVC skeleton, using all the available - tables. Usage: "bake all --everything" - --force, -f Force overwriting existing files without prompting. - --help, -h Display this help. - --plugin, -p Plugin to bake into. - --prefix Prefix to bake controllers and templates into. - --quiet, -q Enable quiet output. - --tablePrefix Table prefix to be used in models. - --theme, -t The theme to use when baking code. (choices: - Bake|Migrations) - --verbose, -v Enable verbose output. + Current Paths: + + * app: src/ + * root: /path/to/your/app/ + * core: /path/to/your/app/vendor/cakephp/cakephp/ + + Available Commands: + + Bake: + - bake all + - bake behavior + - bake cell + - bake command + - bake component + - bake controller + - bake controller all + - bake fixture + - bake fixture all + - bake form + - bake helper + - bake mailer + - bake middleware + - bake model + - bake model all + - bake plugin + - bake template + - bake template all + - bake test + + To run a command, type `cake command_name [args|options]` + To get help on a specific command, type `cake command_name --help` Bake Themes =========== diff --git a/app/vendor/cakephp/bake/docs/ja/index.rst b/app/vendor/cakephp/bake/docs/ja/index.rst index 7eeca3dd3..a4e503963 100644 --- a/app/vendor/cakephp/bake/docs/ja/index.rst +++ b/app/vendor/cakephp/bake/docs/ja/index.rst @@ -14,7 +14,7 @@ bake は数分で完全に機能するアプリケーションを作成できま bake を使用したり拡張する前に、アプリケーションに bake をインストールしておいてください。 bake は Composer を使ってインストールするプラグインとして提供されています。 :: - composer require --dev cakephp/bake:~1.0 + composer require --dev cakephp/bake:"^2.0" 上記のコマンドは、bake を開発環境で使用するパッケージとしてインストールします。 この入れ方の場合、本番環境としてデプロイする際には、 bake はインストールされません。 diff --git a/app/vendor/cakephp/bake/phpstan.neon b/app/vendor/cakephp/bake/phpstan.neon index 691502eb2..4decabc09 100644 --- a/app/vendor/cakephp/bake/phpstan.neon +++ b/app/vendor/cakephp/bake/phpstan.neon @@ -1,5 +1,7 @@ parameters: level: 6 checkMissingIterableValueType: false + paths: + - src bootstrapFiles: - tests/bootstrap.php diff --git a/app/vendor/cakephp/bake/src/Command/BakeCommand.php b/app/vendor/cakephp/bake/src/Command/BakeCommand.php index d8d2a433b..24f23da69 100644 --- a/app/vendor/cakephp/bake/src/Command/BakeCommand.php +++ b/app/vendor/cakephp/bake/src/Command/BakeCommand.php @@ -42,6 +42,25 @@ abstract class BakeCommand extends Command */ protected $pathFragment; + /** + * Get the command name. + * + * Returns the command name based on class name. + * For e.g. for a command with class name `UpdateTableCommand` or `BakeUpdateTableCommand` + * the default name returned would be `'bake update_table'`. + * + * @return string + */ + public static function defaultName(): string + { + $name = parent::defaultName(); + if (strpos($name, 'bake_') === 0) { + $name = substr($name, 5); + } + + return 'bake ' . $name; + } + /** * Handles splitting up the plugin prefix and classname. * diff --git a/app/vendor/cakephp/bake/src/Command/CellCommand.php b/app/vendor/cakephp/bake/src/Command/CellCommand.php index 5fc3184f6..962f6dbd4 100644 --- a/app/vendor/cakephp/bake/src/Command/CellCommand.php +++ b/app/vendor/cakephp/bake/src/Command/CellCommand.php @@ -61,7 +61,8 @@ public function template(): string * Get template data. * * @param \Cake\Console\Arguments $arguments Arguments object. - * @return string[] + * @return array + * @phpstan-return array */ public function templateData(Arguments $arguments): array { diff --git a/app/vendor/cakephp/bake/src/Command/ControllerAllCommand.php b/app/vendor/cakephp/bake/src/Command/ControllerAllCommand.php index d1d2b8b93..cea1c95ec 100644 --- a/app/vendor/cakephp/bake/src/Command/ControllerAllCommand.php +++ b/app/vendor/cakephp/bake/src/Command/ControllerAllCommand.php @@ -35,6 +35,14 @@ class ControllerAllCommand extends BakeCommand */ protected $controllerCommand; + /** + * @inheritDoc + */ + public static function defaultName(): string + { + return 'bake controller all'; + } + /** * initialize * diff --git a/app/vendor/cakephp/bake/src/Command/EntryCommand.php b/app/vendor/cakephp/bake/src/Command/EntryCommand.php index 6ad972121..ba7b51558 100644 --- a/app/vendor/cakephp/bake/src/Command/EntryCommand.php +++ b/app/vendor/cakephp/bake/src/Command/EntryCommand.php @@ -49,6 +49,14 @@ class EntryCommand extends Command implements CommandCollectionAwareInterface */ protected $help; + /** + * @inheritDoc + */ + public static function defaultName(): string + { + return 'bake'; + } + /** * @inheritDoc */ diff --git a/app/vendor/cakephp/bake/src/Command/FixtureAllCommand.php b/app/vendor/cakephp/bake/src/Command/FixtureAllCommand.php index 88e29a39c..e6268fc70 100644 --- a/app/vendor/cakephp/bake/src/Command/FixtureAllCommand.php +++ b/app/vendor/cakephp/bake/src/Command/FixtureAllCommand.php @@ -30,6 +30,14 @@ class FixtureAllCommand extends BakeCommand { use CommonOptionsTrait; + /** + * @inheritDoc + */ + public static function defaultName(): string + { + return 'bake fixture all'; + } + /** * Gets the option parser instance and configures it. * diff --git a/app/vendor/cakephp/bake/src/Command/FixtureCommand.php b/app/vendor/cakephp/bake/src/Command/FixtureCommand.php index ec92e87d7..1fd184934 100644 --- a/app/vendor/cakephp/bake/src/Command/FixtureCommand.php +++ b/app/vendor/cakephp/bake/src/Command/FixtureCommand.php @@ -358,6 +358,7 @@ protected function _generateRecords(TableSchemaInterface $table, int $recordCoun } break; case 'timestamp': + case 'timestamptimezone': case 'timestampfractional': $insert = time(); break; diff --git a/app/vendor/cakephp/bake/src/Command/ModelAllCommand.php b/app/vendor/cakephp/bake/src/Command/ModelAllCommand.php index 3ce9ac059..74180239e 100644 --- a/app/vendor/cakephp/bake/src/Command/ModelAllCommand.php +++ b/app/vendor/cakephp/bake/src/Command/ModelAllCommand.php @@ -35,6 +35,14 @@ class ModelAllCommand extends BakeCommand */ protected $modelCommand; + /** + * @inheritDoc + */ + public static function defaultName(): string + { + return 'bake model all'; + } + /** * initialize * diff --git a/app/vendor/cakephp/bake/src/Command/ModelCommand.php b/app/vendor/cakephp/bake/src/Command/ModelCommand.php index 87a4f2c98..2be46282b 100644 --- a/app/vendor/cakephp/bake/src/Command/ModelCommand.php +++ b/app/vendor/cakephp/bake/src/Command/ModelCommand.php @@ -200,8 +200,8 @@ public function getAssociations(Table $table, Arguments $args, ConsoleIo $io): a $associations = $this->findBelongsTo($table, $associations); if (is_array($primary) && count($primary) > 1) { - $io->err( - 'Bake cannot generate associations for composite primary keys at this time.' + $io->warning( + 'Bake cannot generate associations for composite primary keys at this time.' ); return $associations; @@ -745,7 +745,7 @@ public function fieldValidation( if (in_array($fieldName, $primaryKey, true)) { $validation['allowEmpty'] = [ 'rule' => $this->getEmptyMethod($fieldName, $metaData), - 'args' => ['null', "'create'"], + 'args' => [null, 'create'], ]; } elseif ($metaData['null'] === true) { $validation['allowEmpty'] = [ @@ -756,7 +756,7 @@ public function fieldValidation( if ($metaData['default'] === null || $metaData['default'] === false) { $validation['requirePresence'] = [ 'rule' => 'requirePresence', - 'args' => ["'create'"], + 'args' => ['create'], ]; } $validation['notEmpty'] = [ @@ -913,7 +913,7 @@ public function getBehaviors(Table $model): array * Get CounterCaches * * @param \Cake\ORM\Table $model The table to get counter cache fields for. - * @return string[] CounterCache configurations + * @return array CounterCache configurations */ public function getCounterCache(Table $model): array { @@ -933,7 +933,7 @@ public function getCounterCache(Table $model): array $alias = $model->getAlias(); $field = Inflector::singularize(Inflector::underscore($alias)) . '_count'; if (in_array($field, $otherFields, true)) { - $counterCache[] = "'{$otherAlias}' => ['{$field}']"; + $counterCache[$otherAlias] = [$field]; } } diff --git a/app/vendor/cakephp/bake/src/Command/PluginCommand.php b/app/vendor/cakephp/bake/src/Command/PluginCommand.php index 9afc3558f..9cc30f277 100644 --- a/app/vendor/cakephp/bake/src/Command/PluginCommand.php +++ b/app/vendor/cakephp/bake/src/Command/PluginCommand.php @@ -369,6 +369,15 @@ public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionPar ])->addOption('composer', [ 'default' => ROOT . DS . 'composer.phar', 'help' => 'The path to the composer executable.', + ])->addOption('force', [ + 'short' => 'f', + 'boolean' => true, + 'help' => 'Force overwriting existing files without prompting.', + ])->addOption('theme', [ + 'short' => 't', + 'help' => 'The theme to use when baking code.', + 'default' => Configure::read('Bake.theme') ?? '', + 'choices' => $this->_getBakeThemes(), ]); return $parser; diff --git a/app/vendor/cakephp/bake/src/Command/ShellCommand.php b/app/vendor/cakephp/bake/src/Command/ShellCommand.php deleted file mode 100644 index a724415bc..000000000 --- a/app/vendor/cakephp/bake/src/Command/ShellCommand.php +++ /dev/null @@ -1,54 +0,0 @@ - */ public function templateData(Arguments $arguments): array { diff --git a/app/vendor/cakephp/bake/src/Command/TaskCommand.php b/app/vendor/cakephp/bake/src/Command/TaskCommand.php deleted file mode 100644 index e508e5374..000000000 --- a/app/vendor/cakephp/bake/src/Command/TaskCommand.php +++ /dev/null @@ -1,54 +0,0 @@ -discoverCommands($commands); // Add entry command to handle entry point and backwards compat. - $commands->add('bake', EntryCommand::class); + $commands->add(EntryCommand::defaultName(), EntryCommand::class); return $commands; } @@ -115,40 +114,43 @@ protected function discoverCommands(CommandCollection $commands): CommandCollect * @param string $namespace The namespace classes are expected to be in. * @param string $path The path to look in. * @return string[] - * @phpstan-return class-string<\Bake\Command\BakeCommand>[] + * @psalm-return array> */ protected function findInPath(string $namespace, string $path): array { + $hasSubfolder = false; $path .= 'Command/'; - if (!file_exists($path)) { + $namespace .= '\Command\\'; + + if (file_exists($path . 'Bake/')) { + $hasSubfolder = true; + $path .= 'Bake/'; + $namespace .= 'Bake\\'; + } elseif (!file_exists($path)) { return []; } + $iterator = new DirectoryIterator($path); $candidates = []; foreach ($iterator as $item) { if ($item->isDot() || $item->isDir()) { continue; } - $class = $namespace . '\Command\\' . $item->getBasename('.php'); - - try { - $reflection = new ReflectionClass($class); - } catch (ReflectionException $e) { - continue; - } - if (!$reflection->isInstantiable() || !$reflection->isSubclassOf(BakeCommand::class)) { - continue; + /** @psalm-var class-string<\Bake\Command\BakeCommand> $class */ + $class = $namespace . $item->getBasename('.php'); + + if (!$hasSubfolder) { + try { + $reflection = new ReflectionClass($class); + } catch (ReflectionException $e) { + continue; + } + if (!$reflection->isInstantiable() || !$reflection->isSubclassOf(BakeCommand::class)) { + continue; + } } - // Trim off 'Command' from the name. - [$ns, $className] = namespaceSplit($class); - $name = Inflector::underscore(substr($className, 0, -7)); - - // Commands ending with `_all` should be ` all` instead. - if (substr($name, -4) === '_all') { - $name = substr($name, 0, -4) . ' all'; - } - $candidates["bake {$name}"] = $class; + $candidates[$class::defaultName()] = $class; } return $candidates; diff --git a/app/vendor/cakephp/bake/src/Shell/Task/BakeTask.php b/app/vendor/cakephp/bake/src/Shell/Task/BakeTask.php index 88458b53f..b4191cc83 100644 --- a/app/vendor/cakephp/bake/src/Shell/Task/BakeTask.php +++ b/app/vendor/cakephp/bake/src/Shell/Task/BakeTask.php @@ -125,8 +125,6 @@ public function main() $this->plugin = implode('/', array_map([$this, '_camelize'], $parts)); if (strpos($this->plugin, '\\')) { $this->abort('Invalid plugin namespace separator, please use / instead of \ for plugins.'); - - return static::CODE_ERROR; } } if (isset($this->params['connection'])) { diff --git a/app/vendor/cakephp/bake/src/Shell/Task/SimpleBakeTask.php b/app/vendor/cakephp/bake/src/Shell/Task/SimpleBakeTask.php index 98a506085..a42c94cc5 100644 --- a/app/vendor/cakephp/bake/src/Shell/Task/SimpleBakeTask.php +++ b/app/vendor/cakephp/bake/src/Shell/Task/SimpleBakeTask.php @@ -51,7 +51,8 @@ abstract public function template(): string; /** * Get template data. * - * @return string[] + * @return array + * @phpstan-return array */ public function templateData(): array { @@ -74,8 +75,6 @@ public function main(?string $name = null): ?int parent::main(); if (empty($name)) { $this->abort('You must provide a name to bake a ' . $this->name()); - - return null; } $name = $this->_getName($name); $name = Inflector::camelize($name); diff --git a/app/vendor/cakephp/bake/src/Utility/CommonOptionsTrait.php b/app/vendor/cakephp/bake/src/Utility/CommonOptionsTrait.php index c58310476..2ec2d448d 100644 --- a/app/vendor/cakephp/bake/src/Utility/CommonOptionsTrait.php +++ b/app/vendor/cakephp/bake/src/Utility/CommonOptionsTrait.php @@ -73,12 +73,11 @@ protected function extractCommonProperties(Arguments $args): void } /** - * Set common options used by all bake tasks. + * Get available bake themes * - * @param \Cake\Console\ConsoleOptionParser $parser Options parser. - * @return \Cake\Console\ConsoleOptionParser + * @return array */ - protected function _setCommonOptions(ConsoleOptionParser $parser): ConsoleOptionParser + protected function _getBakeThemes(): array { $bakeThemes = []; $templates = 'templates' . DS . 'bake'; @@ -89,6 +88,17 @@ protected function _setCommonOptions(ConsoleOptionParser $parser): ConsoleOption } } + return $bakeThemes; + } + + /** + * Set common options used by all bake tasks. + * + * @param \Cake\Console\ConsoleOptionParser $parser Options parser. + * @return \Cake\Console\ConsoleOptionParser + */ + protected function _setCommonOptions(ConsoleOptionParser $parser): ConsoleOptionParser + { $parser->addOption('plugin', [ 'short' => 'p', 'help' => 'Plugin to bake into.', @@ -104,7 +114,7 @@ protected function _setCommonOptions(ConsoleOptionParser $parser): ConsoleOption 'short' => 't', 'help' => 'The theme to use when baking code.', 'default' => Configure::read('Bake.theme') ?? '', - 'choices' => $bakeThemes, + 'choices' => $this->_getBakeThemes(), ]); return $parser; diff --git a/app/vendor/cakephp/bake/src/View/Helper/BakeHelper.php b/app/vendor/cakephp/bake/src/View/Helper/BakeHelper.php index 4c6183e72..99cdbb899 100644 --- a/app/vendor/cakephp/bake/src/View/Helper/BakeHelper.php +++ b/app/vendor/cakephp/bake/src/View/Helper/BakeHelper.php @@ -4,6 +4,7 @@ namespace Bake\View\Helper; use Bake\Utility\Model\AssociationFilter; +use Brick\VarExporter\VarExporter; use Cake\Core\Configure; use Cake\Core\ConventionsTrait; use Cake\Database\Schema\TableSchema; @@ -64,6 +65,7 @@ public function arrayProperty(string $name, array $value = [], array $options = * @param array $list array of items to be stringified * @param array $options options to use * @return string + * @deprecated 2.5.0 Use BakeHelper::exportVar() instead. */ public function stringifyList(array $list, array $options = []): string { @@ -125,6 +127,44 @@ public function stringifyList(array $list, array $options = []): string return $start . implode($join, $list) . $end; } + /** + * Export variable to string representation. + * + * (Similar to `var_export()` but better). + * + * @param mixed $var Variable to export. + * @param int $indentLevel Identation level. + * @param int $options VarExporter option flags + * @return string + * @see https://github.com/brick/varexporter#options + */ + public function exportVar($var, int $indentLevel = 0, int $options = 0): string + { + $options |= VarExporter::TRAILING_COMMA_IN_ARRAY; + + return VarExporter::export($var, $options, $indentLevel); + } + + /** + * Export array to string representation. + * + * (Similar to `var_export()` but better). + * + * @param array $var Array to export. + * @param int $indentLevel Identation level. + * @param bool $inline Inline numeric scalar array (adds INLINE_NUMERIC_SCALAR_ARRAY flag) + * @return string + */ + public function exportArray(array $var, int $indentLevel = 0, bool $inline = true): string + { + $options = 0; + if ($inline) { + $options = VarExporter::INLINE_NUMERIC_SCALAR_ARRAY; + } + + return $this->exportVar($var, $indentLevel, $options); + } + /** * Extract the aliases for associations, filters hasMany associations already extracted as * belongsToMany @@ -329,21 +369,10 @@ public function getValidationMethods(string $field, array $rules): array foreach ($rules as $ruleName => $rule) { if ($rule['rule'] && !isset($rule['provider']) && !isset($rule['args'])) { $validationMethods[] = sprintf("->%s('%s')", $rule['rule'], $field); - } elseif ($rule['rule'] && !isset($rule['provider'])) { - $formatTemplate = "->%s('%s')"; - if (!empty($rule['args'])) { - $formatTemplate = "->%s('%s', %s)"; - } - $validationMethods[] = sprintf( - $formatTemplate, - $rule['rule'], - $field, - $this->stringifyList( - $rule['args'], - ['indent' => false, 'quotes' => false] - ) - ); - } elseif ($rule['rule'] && isset($rule['provider'])) { + continue; + } + + if ($rule['rule'] && isset($rule['provider'])) { $validationMethods[] = sprintf( "->add('%s', '%s', ['rule' => '%s', 'provider' => '%s'])", $field, @@ -351,7 +380,31 @@ public function getValidationMethods(string $field, array $rules): array $rule['rule'], $rule['provider'] ); + continue; } + + if (empty($rule['args'])) { + $validationMethods[] = sprintf( + "->%s('%s')", + $rule['rule'], + $field + ); + continue; + } + + $rule['args'] = array_map(function ($item) { + return $this->exportVar( + $item, + is_array($item) ? 3 : 0, + VarExporter::INLINE_NUMERIC_SCALAR_ARRAY + ); + }, $rule['args']); + $validationMethods[] = sprintf( + "->%s('%s', %s)", + $rule['rule'], + $field, + implode(', ', $rule['args']) + ); } return $validationMethods; @@ -362,7 +415,7 @@ public function getValidationMethods(string $field, array $rules): array * * @param string[]|false|null $fields Fields list. * @param string[]|null $primaryKey Primary key. - * @return string[] + * @return array */ public function getFieldAccessibility($fields = null, $primaryKey = null): array { @@ -371,12 +424,12 @@ public function getFieldAccessibility($fields = null, $primaryKey = null): array if (!isset($fields) || $fields !== false) { if (!empty($fields)) { foreach ($fields as $field) { - $accessible[$field] = 'true'; + $accessible[$field] = true; } } elseif (!empty($primaryKey)) { - $accessible['*'] = 'true'; + $accessible['*'] = true; foreach ($primaryKey as $field) { - $accessible[$field] = 'false'; + $accessible[$field] = false; } } } diff --git a/app/vendor/cakephp/bake/templates/bake/Controller/controller.twig b/app/vendor/cakephp/bake/templates/bake/Controller/controller.twig index c7eea01ff..257ef1f9b 100644 --- a/app/vendor/cakephp/bake/templates/bake/Controller/controller.twig +++ b/app/vendor/cakephp/bake/templates/bake/Controller/controller.twig @@ -58,7 +58,7 @@ class {{ name }}Controller extends AppController $this->loadComponent('{{ component }}'); {% endfor %} {% if helpers %} - $this->viewBuilder()->setHelpers([{{ Bake.stringifyList(helpers, {'indent': false})|raw }}]); + $this->viewBuilder()->setHelpers({{ Bake.exportArray(helpers)|raw }}); {% endif %} } {% if actions|length %}{{ "\n" }}{% endif %} diff --git a/app/vendor/cakephp/bake/templates/bake/Model/entity.twig b/app/vendor/cakephp/bake/templates/bake/Model/entity.twig index 60e3a21fe..6dd3cdb76 100644 --- a/app/vendor/cakephp/bake/templates/bake/Model/entity.twig +++ b/app/vendor/cakephp/bake/templates/bake/Model/entity.twig @@ -43,7 +43,7 @@ class {{ name }} extends Entity * * @var array */ - protected $_accessible = [{{ Bake.stringifyList(accessible, {'quotes': false})|raw }}]; + protected $_accessible = {{ Bake.exportVar(accessible, 1)|raw }}; {% endif %} {% if accessible and hidden %} @@ -54,6 +54,6 @@ class {{ name }} extends Entity * * @var array */ - protected $_hidden = [{{ Bake.stringifyList(hidden)|raw }}]; + protected $_hidden = {{ Bake.exportVar(hidden, 1)|raw }}; {% endif %} } diff --git a/app/vendor/cakephp/bake/templates/bake/Model/table.twig b/app/vendor/cakephp/bake/templates/bake/Model/table.twig index d2a4eee80..edd58d349 100644 --- a/app/vendor/cakephp/bake/templates/bake/Model/table.twig +++ b/app/vendor/cakephp/bake/templates/bake/Model/table.twig @@ -45,7 +45,7 @@ class {{ name }}Table extends Table {%- if primaryKey %} {%- if primaryKey is iterable and primaryKey|length > 1 %} - $this->setPrimaryKey([{{ Bake.stringifyList(primaryKey, {'indent': false})|raw }}]); + $this->setPrimaryKey({{ Bake.exportArray(primaryKey)|raw }}); {{- "\n" }} {%- else %} $this->setPrimaryKey('{{ primaryKey|as_array|first }}'); @@ -58,7 +58,7 @@ class {{ name }}Table extends Table {% endif %} {%- for behavior, behaviorData in behaviors %} - $this->addBehavior('{{ behavior }}'{{ (behaviorData ? (", [" ~ Bake.stringifyList(behaviorData, {'indent': 3, 'quotes': false})|raw ~ ']') : '')|raw }}); + $this->addBehavior('{{ behavior }}'{{ (behaviorData ? (", " ~ Bake.exportArray(behaviorData, 2)|raw ~ '') : '')|raw }}); {% endfor %} {%- if associations.belongsTo or associations.hasMany or associations.belongsToMany %} @@ -73,7 +73,7 @@ class {{ name }}Table extends Table {%- set assocData = assocData|merge({(key): val}) %} {%- endif %} {%- endfor %} - $this->{{ type }}('{{ assoc.alias }}', [{{ Bake.stringifyList(assocData, {'indent': 3})|raw }}]); + $this->{{ type }}('{{ assoc.alias }}', {{ Bake.exportArray(assocData, 2)|raw }}); {{- "\n" }} {%- endfor %} {% endfor %} diff --git a/app/vendor/cakephp/bake/templates/bake/Plugin/composer.json.twig b/app/vendor/cakephp/bake/templates/bake/Plugin/composer.json.twig index a3614bb67..eebe5dc8e 100644 --- a/app/vendor/cakephp/bake/templates/bake/Plugin/composer.json.twig +++ b/app/vendor/cakephp/bake/templates/bake/Plugin/composer.json.twig @@ -21,10 +21,10 @@ "license": "MIT", "require": { "php": ">=7.2", - "cakephp/cakephp": "{{ cakeVersion }}" + "cakephp/cakephp": "{{ cakeVersion|raw }}" }, "require-dev": { - "phpunit/phpunit": "^8.0" + "phpunit/phpunit": "^8.5 || ^9.3" }, "autoload": { "psr-4": { diff --git a/app/vendor/cakephp/bake/templates/bake/Shell/shell.twig b/app/vendor/cakephp/bake/templates/bake/Shell/shell.twig deleted file mode 100644 index 3ec71a3ed..000000000 --- a/app/vendor/cakephp/bake/templates/bake/Shell/shell.twig +++ /dev/null @@ -1,52 +0,0 @@ -{# -/** - * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) - * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) - * - * Licensed under The MIT License - * For full copyright and license information, please see the LICENSE.txt - * Redistributions of files must retain the above copyright notice. - * - * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) - * @link http://cakephp.org CakePHP(tm) Project - * @since 2.0.0 - * @license http://www.opensource.org/licenses/mit-license.php MIT License - */ -#} -out($this->OptionParser->help()); - } -} diff --git a/app/vendor/cakephp/bake/templates/bake/Shell/task.twig b/app/vendor/cakephp/bake/templates/bake/Shell/task.twig deleted file mode 100644 index 255ce0ef6..000000000 --- a/app/vendor/cakephp/bake/templates/bake/Shell/task.twig +++ /dev/null @@ -1,49 +0,0 @@ -{# -/** - * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) - * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) - * - * Licensed under The MIT License - * For full copyright and license information, please see the LICENSE.txt - * Redistributions of files must retain the above copyright notice. - * - * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) - * @link http://cakephp.org CakePHP(tm) Project - * @license http://www.opensource.org/licenses/mit-license.php MIT License - */ -#} - {{ element('Bake.form') }} \ No newline at end of file diff --git a/app/vendor/cakephp/bake/templates/bake/Template/edit.twig b/app/vendor/cakephp/bake/templates/bake/Template/edit.twig index 35b66d4da..c0df18eb5 100644 --- a/app/vendor/cakephp/bake/templates/bake/Template/edit.twig +++ b/app/vendor/cakephp/bake/templates/bake/Template/edit.twig @@ -17,6 +17,19 @@ /** * @var \{{ namespace }}\View\AppView $this * @var \{{ entityClass }} ${{ singularVar }} + {{- "\n" }} +{%- if associations.BelongsTo is defined %} + {%- for assocName, assocData in associations.BelongsTo %} + * @var string[]|\Cake\Collection\CollectionInterface ${{ assocData.variable }} + {{- "\n" }} + {%- endfor %} +{% endif %} +{%- if associations.BelongsToMany is defined %} + {%- for assocName, assocData in associations.BelongsToMany %} + * @var string[]|\Cake\Collection\CollectionInterface ${{ assocData.variable }} + {{- "\n" }} + {%- endfor %} +{% endif %} */ ?> {{ element('Bake.form') }} \ No newline at end of file diff --git a/app/vendor/cakephp/bake/templates/bake/element/Controller/edit.twig b/app/vendor/cakephp/bake/templates/bake/element/Controller/edit.twig index 469e1bbc7..b29da3b66 100644 --- a/app/vendor/cakephp/bake/templates/bake/element/Controller/edit.twig +++ b/app/vendor/cakephp/bake/templates/bake/element/Controller/edit.twig @@ -26,7 +26,7 @@ public function edit($id = null) { ${{ singularName }} = $this->{{ currentModelName }}->get($id, [ - 'contain' => [{{ Bake.stringifyList(belongsToMany, {'indent': false})|raw }}], + 'contain' => {{ Bake.exportArray(belongsToMany)|raw }}, ]); if ($this->request->is(['patch', 'post', 'put'])) { ${{ singularName }} = $this->{{ currentModelName }}->patchEntity(${{ singularName }}, $this->request->getData()); diff --git a/app/vendor/cakephp/bake/templates/bake/element/Controller/index.twig b/app/vendor/cakephp/bake/templates/bake/element/Controller/index.twig index 49ff3ad84..e65a78c93 100644 --- a/app/vendor/cakephp/bake/templates/bake/element/Controller/index.twig +++ b/app/vendor/cakephp/bake/templates/bake/element/Controller/index.twig @@ -23,7 +23,7 @@ {% set belongsTo = Bake.aliasExtractor(modelObj, 'BelongsTo') %} {% if belongsTo %} $this->paginate = [ - 'contain' => [{{ Bake.stringifyList(belongsTo, {'indent': false})|raw }}], + 'contain' => {{ Bake.exportArray(belongsTo)|raw }}, ]; {% endif %} ${{ pluralName }} = $this->paginate($this->{{ currentModelName }}); diff --git a/app/vendor/cakephp/bake/templates/bake/element/Controller/view.twig b/app/vendor/cakephp/bake/templates/bake/element/Controller/view.twig index 8d85232c9..148d8483d 100644 --- a/app/vendor/cakephp/bake/templates/bake/element/Controller/view.twig +++ b/app/vendor/cakephp/bake/templates/bake/element/Controller/view.twig @@ -27,7 +27,7 @@ public function view($id = null) { ${{ singularName }} = $this->{{ currentModelName }}->get($id, [ - 'contain' => [{{ Bake.stringifyList(allAssociations, {'indent': false})|raw }}], + 'contain' => {{ Bake.exportArray(allAssociations)|raw }}, ]); $this->set(compact('{{ singularName }}')); diff --git a/app/vendor/cakephp/bake/templates/bake/element/array_property.twig b/app/vendor/cakephp/bake/templates/bake/element/array_property.twig index ff5bd9fe1..a96011e31 100644 --- a/app/vendor/cakephp/bake/templates/bake/element/array_property.twig +++ b/app/vendor/cakephp/bake/templates/bake/element/array_property.twig @@ -3,4 +3,4 @@ * * @var array */ - public ${{ name }} = [{{ Bake.stringifyList(value, {'indent': false})|raw }}]; \ No newline at end of file + public ${{ name }} = {{ Bake.exportArray(value)|raw }}; \ No newline at end of file diff --git a/app/vendor/cakephp/bake/templates/bake/tests/test_case.twig b/app/vendor/cakephp/bake/templates/bake/tests/test_case.twig index 8fad4c0c8..0ca9bc303 100644 --- a/app/vendor/cakephp/bake/templates/bake/tests/test_case.twig +++ b/app/vendor/cakephp/bake/templates/bake/tests/test_case.twig @@ -76,7 +76,7 @@ class {{ className }}Test extends TestCase * * @var array */ - protected $fixtures = [{{ Bake.stringifyList(fixtures|values)|raw }}]; + protected $fixtures = {{ Bake.exportVar(fixtures|values, 1)|raw }}; {% if construction or methods %} {% endif %} @@ -130,6 +130,7 @@ class {{ className }}Test extends TestCase * Test {{ method }} method * * @return void + * @uses \{{ fullClassName }}::{{ method }}() */ public function test{{ method|camelize }}(): void { diff --git a/app/vendor/cakephp/bake/tests/bootstrap.php b/app/vendor/cakephp/bake/tests/bootstrap.php index e0b2611fd..4c4e3d14d 100644 --- a/app/vendor/cakephp/bake/tests/bootstrap.php +++ b/app/vendor/cakephp/bake/tests/bootstrap.php @@ -70,9 +70,9 @@ ], ]); -if (!getenv('db_dsn')) { - putenv('db_dsn=sqlite:///:memory:'); +if (!getenv('DB_URL')) { + putenv('DB_URL=sqlite:///:memory:'); } -ConnectionManager::setConfig('test', ['url' => getenv('db_dsn')]); +ConnectionManager::setConfig('test', ['url' => getenv('DB_URL')]); Plugin::getCollection()->add(new \Bake\Plugin()); diff --git a/app/vendor/cakephp/cakephp-codesniffer/.editorconfig b/app/vendor/cakephp/cakephp-codesniffer/.editorconfig deleted file mode 100644 index e43d09569..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/.editorconfig +++ /dev/null @@ -1,17 +0,0 @@ -; This file is for unifying the coding style for different editors and IDEs. -; More information at http://editorconfig.org - -root = true - -[*] -indent_style = space -indent_size = 4 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true - -[*.bat] -end_of_line = crlf - -[*.yml] -indent_size = 2 diff --git a/app/vendor/cakephp/cakephp-codesniffer/.gitattributes b/app/vendor/cakephp/cakephp-codesniffer/.gitattributes deleted file mode 100644 index af6a86494..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/.gitattributes +++ /dev/null @@ -1,10 +0,0 @@ -# Define the line ending behavior of the different file extensions -# Set default behaviour, in case users don't have core.autocrlf set. -* text=lf - -# Explicitly declare text files we want to always be normalized and converted -# to native line endings on checkout. -*.php eol=lf - -# Declare files that will always have CRLF line endings on checkout. -*.bat eol=crlf diff --git a/app/vendor/cakephp/cakephp-codesniffer/.github/codecov.yml b/app/vendor/cakephp/cakephp-codesniffer/.github/codecov.yml new file mode 100644 index 000000000..0d79235e3 --- /dev/null +++ b/app/vendor/cakephp/cakephp-codesniffer/.github/codecov.yml @@ -0,0 +1,7 @@ +codecov: + require_ci_to_pass: yes + +coverage: + range: "90...100" + +comment: false diff --git a/app/vendor/cakephp/cakephp-codesniffer/.github/workflows/ci.yml b/app/vendor/cakephp/cakephp-codesniffer/.github/workflows/ci.yml new file mode 100644 index 000000000..84355bcbb --- /dev/null +++ b/app/vendor/cakephp/cakephp-codesniffer/.github/workflows/ci.yml @@ -0,0 +1,106 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + branches: + - '*' + +jobs: + testsuite: + runs-on: ubuntu-18.04 + strategy: + fail-fast: false + matrix: + php-version: ['7.2', '7.4', '8.0'] + prefer-lowest: [''] + include: + - php-version: '7.2' + prefer-lowest: 'prefer-lowest' + + steps: + - uses: actions/checkout@v1 + with: + fetch-depth: 1 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: mbstring, intl + coverage: pcov + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Get date part for cache key + id: key-date + run: echo "::set-output name=date::$(date +'%Y-%m')" + + - name: Cache composer dependencies + uses: actions/cache@v1 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ steps.key-date.outputs.date }}-${{ hashFiles('composer.json') }}-${{ matrix.prefer-lowest }} + + - name: Composer Install + run: | + if [[ ${{ matrix.php-version }} == '8.0' ]]; then + composer update --ignore-platform-reqs + elif ${{ matrix.prefer-lowest == 'prefer-lowest' }}; then + composer update --prefer-lowest --prefer-stable + else + composer update + fi + + - name: Configure PHPUnit matcher + if: matrix.php-version == '7.4' + uses: mheap/phpunit-matcher-action@master + + - name: Run PHPUnit + run: | + vendor/bin/phpcs --config-set installed_paths $(pwd) + vendor/bin/phpunit --filter CakePHP + + - name: Submit code coverage + if: matrix.php-version == '7.4' + uses: codecov/codecov-action@v1 + + cs-stan: + name: Coding Standard & Static Analysis + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v1 + with: + fetch-depth: 1 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.2' + extensions: mbstring, intl + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Get date part for cache key + id: key-date + run: echo "::set-output name=date::$(date +'%Y-%m')" + + - name: Cache composer dependencies + uses: actions/cache@v1 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ steps.key-date.outputs.date }}-${{ hashFiles('composer.json') }}-${{ matrix.prefer-lowest }} + + - name: Composer install + run: composer update + + - name: Run PHP CodeSniffer + run: vendor/bin/phpcs --report=checkstyle CakePHP/ diff --git a/app/vendor/cakephp/cakephp-codesniffer/.gitignore b/app/vendor/cakephp/cakephp-codesniffer/.gitignore deleted file mode 100644 index 217b42f4c..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/build/ -/dist/ -tags -composer.lock -vendor/ -/.idea/ diff --git a/app/vendor/cakephp/cakephp-codesniffer/.travis.yml b/app/vendor/cakephp/cakephp-codesniffer/.travis.yml deleted file mode 100644 index 4d2d14b6c..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/.travis.yml +++ /dev/null @@ -1,37 +0,0 @@ -language: php - -php: - - 5.6 - - 7.0 - - 7.1 - - 7.2 - - 7.3 - - 7.4snapshot - -matrix: - fast_finish: true - - include: - - php: 7.2 - env: PHPCS=1 - -before_install: - - if [[ $TRAVIS_PHP_VERSION != "7.4snapshot" ]]; then phpenv config-rm xdebug.ini; fi - -before_script: - - composer install --prefer-source - -script: - - if [[ $PHPCS != 1 && $TRAVIS_PHP_VERSION = 7.1 ]]; then composer run-script test-coverage --timeout=0; fi - - if [[ $PHPCS != 1 && $TRAVIS_PHP_VERSION != 7.1 ]]; then composer test; fi - - if [[ $PHPCS = 1 ]]; then composer cs-check; fi - -after_success: - - if [[ $PHPCS != 1 && $TRAVIS_PHP_VERSION = 7.1 ]]; then bash <(curl -s https://codecov.io/bash); fi - -cache: - directories: - - $HOME/.composer/cache - -notifications: - email: false diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Classes/ReturnTypeHintSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Classes/ReturnTypeHintSniff.php new file mode 100644 index 000000000..a0267fa7a --- /dev/null +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Classes/ReturnTypeHintSniff.php @@ -0,0 +1,236 @@ +getTokens(); + + $openParenthesisIndex = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr + 1); + $closeParenthesisIndex = $tokens[$openParenthesisIndex]['parenthesis_closer']; + + $colonIndex = $phpcsFile->findNext(Tokens::$emptyTokens, $closeParenthesisIndex + 1, null, true); + if (!$colonIndex) { + return; + } + + $startIndex = $phpcsFile->findNext(Tokens::$emptyTokens, $colonIndex + 1, $colonIndex + 3, true); + if (!$startIndex) { + return; + } + + if (!$this->isChainingMethod($phpcsFile, $stackPtr)) { + $this->assertNotThisOrStatic($phpcsFile, $stackPtr); + + return; + } + + // We skip for interface methods + if (empty($tokens[$stackPtr]['scope_opener']) || empty($tokens[$stackPtr]['scope_closer'])) { + return []; + } + + $returnTokenType = $tokens[$startIndex]['type']; + if ($returnTokenType !== 'T_SELF') { + // Then we can only warn, but not auto-fix + $phpcsFile->addError( + 'Chaining methods (@return $this) should not have any return-type-hint.', + $startIndex, + 'TypeHint.Invalid.Self' + ); + + return; + } + + $fix = $phpcsFile->addFixableError( + 'Chaining methods (@return $this) should not have any return-type-hint (Remove "self").', + $startIndex, + 'TypeHint.Invalid.Self' + ); + if (!$fix) { + return; + } + + $phpcsFile->fixer->beginChangeset(); + for ($i = $colonIndex; $i <= $startIndex; $i++) { + $phpcsFile->fixer->replaceToken($i, ''); + } + $phpcsFile->fixer->endChangeset(); + } + + /** + * @param \PHP_CodeSniffer\Files\File $phpCsFile File + * @param int $stackPointer Stack pointer + * @return bool + */ + protected function isChainingMethod(File $phpCsFile, int $stackPointer): bool + { + $docBlockEndIndex = $this->findRelatedDocBlock($phpCsFile, $stackPointer); + + if (!$docBlockEndIndex) { + return false; + } + + $tokens = $phpCsFile->getTokens(); + + $docBlockStartIndex = $tokens[$docBlockEndIndex]['comment_opener']; + + for ($i = $docBlockStartIndex + 1; $i < $docBlockEndIndex; $i++) { + if ($tokens[$i]['type'] !== 'T_DOC_COMMENT_TAG') { + continue; + } + if ($tokens[$i]['content'] !== '@return') { + continue; + } + + $classNameIndex = $i + 2; + + if ($tokens[$classNameIndex]['type'] !== 'T_DOC_COMMENT_STRING') { + continue; + } + + $content = $tokens[$classNameIndex]['content']; + if (!$content) { + continue; + } + + return $content === '$this'; + } + + return false; + } + + /** + * @param \PHP_CodeSniffer\Files\File $phpCsFile File + * @param int $stackPointer Stack pointer + * @return void + */ + protected function assertNotThisOrStatic(File $phpCsFile, int $stackPointer): void + { + $docBlockEndIndex = $this->findRelatedDocBlock($phpCsFile, $stackPointer); + + if (!$docBlockEndIndex) { + return; + } + + $tokens = $phpCsFile->getTokens(); + + $docBlockStartIndex = $tokens[$docBlockEndIndex]['comment_opener']; + + for ($i = $docBlockStartIndex + 1; $i < $docBlockEndIndex; $i++) { + if ($tokens[$i]['type'] !== 'T_DOC_COMMENT_TAG') { + continue; + } + if ($tokens[$i]['content'] !== '@return') { + continue; + } + + $classNameIndex = $i + 2; + + if ($tokens[$classNameIndex]['type'] !== 'T_DOC_COMMENT_STRING') { + continue; + } + + $content = $tokens[$classNameIndex]['content']; + if (!$content || strpos($content, '\\') !== 0) { + continue; + } + + $classNameWithNamespace = $this->getClassNameWithNamespace($phpCsFile); + if ($content !== $classNameWithNamespace) { + continue; + } + + $phpCsFile->addError( + 'Class name repeated, expected `self` or `$this`.', + $classNameIndex, + 'TypeHint.Invalid.Class' + ); + } + } + + /** + * @param \PHP_CodeSniffer\Files\File $phpCsFile File + * @param int $stackPointer Stack pointer + * @return int|null Stackpointer value of docblock end tag, or null if cannot be found + */ + protected function findRelatedDocBlock(File $phpCsFile, int $stackPointer): ?int + { + $tokens = $phpCsFile->getTokens(); + + $line = $tokens[$stackPointer]['line']; + $beginningOfLine = $stackPointer; + while (!empty($tokens[$beginningOfLine - 1]) && $tokens[$beginningOfLine - 1]['line'] === $line) { + $beginningOfLine--; + } + + if ( + !empty($tokens[$beginningOfLine - 2]) + && $tokens[$beginningOfLine - 2]['type'] === 'T_DOC_COMMENT_CLOSE_TAG' + ) { + return $beginningOfLine - 2; + } + + if ( + !empty($tokens[$beginningOfLine - 3]) + && $tokens[$beginningOfLine - 3]['type'] === 'T_DOC_COMMENT_CLOSE_TAG' + ) { + return $beginningOfLine - 3; + } + + return null; + } + + /** + * @param \PHP_CodeSniffer\Files\File $phpCsFile File + * @return string|null + */ + protected function getClassNameWithNamespace(File $phpCsFile): ?string + { + try { + $lastToken = TokenHelper::getLastTokenPointer($phpCsFile); + } catch (EmptyFileException $e) { + return null; + } + + if (!NamespaceHelper::findCurrentNamespaceName($phpCsFile, $lastToken)) { + return null; + } + + return ClassHelper::getFullyQualifiedName( + $phpCsFile, + $phpCsFile->findPrevious(TokenHelper::$typeKeywordTokenCodes, $lastToken) + ); + } +} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Commenting/DocBlockAlignmentSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Commenting/DocBlockAlignmentSniff.php index 799d737c7..5e75161b7 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Commenting/DocBlockAlignmentSniff.php +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Commenting/DocBlockAlignmentSniff.php @@ -1,14 +1,14 @@ - * @author Marc McIntyre - * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) - * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence - * @link http://pear.php.net/package/PHP_CodeSniffer + * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) + * @link https://github.com/cakephp/cakephp-codesniffer + * @since CakePHP CodeSniffer 0.1.24 + * @license http://www.opensource.org/licenses/mit-license.php MIT License */ namespace CakePHP\Sniffs\Commenting; @@ -50,19 +50,43 @@ class FunctionCommentSniff extends PearFunctionCommentSniff { /** - * Is the comment an inheritdoc? + * Checks if the doc comment is an inheritDoc comment. * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * @param int $commentStart The position in the stack where the comment started. * @return bool True if the comment is an inheritdoc */ - protected function isInheritDoc(File $phpcsFile, $stackPtr) + protected function isInheritDoc(File $phpcsFile, $commentStart) { - $start = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, $stackPtr - 1); - $end = $phpcsFile->findNext(T_DOC_COMMENT_CLOSE_TAG, $start); - $content = $phpcsFile->getTokensAsString($start, ($end - $start)); + $tokens = $phpcsFile->getTokens(); + + $empty = [ + T_DOC_COMMENT_WHITESPACE, + T_DOC_COMMENT_STAR, + ]; + + $commentEnd = $tokens[$commentStart]['comment_closer']; + $inheritDoc = $phpcsFile->findNext($empty, $commentStart + 1, $commentEnd, true); + if ($inheritDoc === false) { + return false; + } - return preg_match('/{@inheritDoc}|@inheritDoc/i', $content) === 1; + if (preg_match('/^@inheritDoc$/i', $tokens[$inheritDoc]['content']) === 1) { + return true; + } + + if (preg_match('/^{@inheritDoc}$/i', $tokens[$inheritDoc]['content']) !== 1) { + return false; + } + + $notAllowed = ['@param', '@return']; + foreach ($tokens[$commentStart]['comment_tags'] as $tag) { + if (in_array($tokens[$tag]['content'], $notAllowed, true)) { + return false; + } + } + + return true; } /** @@ -75,7 +99,7 @@ protected function isInheritDoc(File $phpcsFile, $stackPtr) */ protected function processReturn(File $phpcsFile, $stackPtr, $commentStart) { - if ($this->isInheritDoc($phpcsFile, $stackPtr)) { + if ($this->isInheritDoc($phpcsFile, $commentStart)) { return; } @@ -121,48 +145,22 @@ protected function processReturn(File $phpcsFile, $stackPtr, $commentStart) return; } - $content = $tokens[($return + 2)]['content']; - if (empty($content) === true || $tokens[($return + 2)]['code'] !== T_DOC_COMMENT_STRING) { + $content = $tokens[$return + 2]['content']; + if (empty($content) === true || $tokens[$return + 2]['code'] !== T_DOC_COMMENT_STRING) { $error = 'Return type missing for @return tag in function comment'; $phpcsFile->addError($error, $return, 'MissingReturnType'); return; } - // Check return type (can be multiple, separated by '|'). - list($types, ) = explode(' ', $content); - $typeNames = explode('|', $types); - $suggestedNames = []; - foreach ($typeNames as $i => $typeName) { - if ($typeName === 'integer') { - $suggestedName = 'int'; - } elseif ($typeName === 'boolean') { - $suggestedName = 'bool'; - } elseif (in_array($typeName, ['int', 'bool'])) { - $suggestedName = $typeName; - } else { - $suggestedName = Common::suggestType($typeName); - } - if (in_array($suggestedName, $suggestedNames) === false) { - $suggestedNames[] = $suggestedName; - } - } - - $suggestedType = implode('|', $suggestedNames); - if ($types !== $suggestedType) { - $error = 'Expected "%s" but found "%s" for function return type'; - $data = [ - $suggestedType, - $types, - ]; - $phpcsFile->addError($error, $return, 'InvalidReturn', $data); - } - - $endToken = isset($tokens[$stackPtr]['scope_closer']) ? $tokens[$stackPtr]['scope_closer'] : false; + $endToken = $tokens[$stackPtr]['scope_closer'] ?? false; if (!$endToken) { return; } + [$types, ] = explode(' ', $content); + $typeNames = explode('|', $types); + // If the return type is void, make sure there is // no non-void return statements in the function. if ($typeNames === ['void']) { @@ -184,7 +182,7 @@ protected function processReturn(File $phpcsFile, $stackPtr, $commentStart) if ($returnToken !== $endToken) { // If the function is not returning anything, just // exiting, then there is no problem. - $semicolon = $phpcsFile->findNext(T_WHITESPACE, ($returnToken + 1), null, true); + $semicolon = $phpcsFile->findNext(T_WHITESPACE, $returnToken + 1, null, true); if ($tokens[$semicolon]['code'] !== T_SEMICOLON) { $error = 'Function return type is void, but function contains return statement'; $phpcsFile->addWarning($error, $return, 'InvalidReturnVoid'); @@ -198,11 +196,11 @@ protected function processReturn(File $phpcsFile, $stackPtr, $commentStart) // somewhere in the function that returns something. if (!in_array('mixed', $typeNames, true) && !in_array('void', $typeNames, true)) { $returnToken = $phpcsFile->findNext([T_RETURN, T_YIELD, T_YIELD_FROM], $stackPtr, $endToken); - if ($returnToken === false) { + if ($returnToken === false && !$this->hasException($phpcsFile, $stackPtr, $endToken)) { $error = 'Function return type is not void, but function has no return statement'; $phpcsFile->addWarning($error, $return, 'InvalidNoReturn'); } else { - $semicolon = $phpcsFile->findNext(T_WHITESPACE, ($returnToken + 1), null, true); + $semicolon = $phpcsFile->findNext(T_WHITESPACE, $returnToken + 1, null, true); if ($tokens[$semicolon]['code'] === T_SEMICOLON) { $error = 'Function return type is not void, but function is returning void here'; $phpcsFile->addWarning($error, $returnToken, 'InvalidReturnNotVoid'); @@ -211,6 +209,19 @@ protected function processReturn(File $phpcsFile, $stackPtr, $commentStart) } } + /** + * @param \PHP_CodeSniffer\Files\File $phpcsFile File + * @param int $startIndex Start index + * @param int $endIndex End index + * @return bool + */ + protected function hasException(File $phpcsFile, $startIndex, $endIndex) + { + $throwIndex = $phpcsFile->findNext([T_THROW], $startIndex, $endIndex); + + return $throwIndex !== false; + } + /** * Process any throw tags that this function comment has. * @@ -229,9 +240,9 @@ protected function processThrows(File $phpcsFile, $stackPtr, $commentStart) } $exception = $comment = null; - if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) { + if ($tokens[$tag + 2]['code'] === T_DOC_COMMENT_STRING) { $matches = []; - preg_match('/([^\s]+)(?:\s+(.*))?/', $tokens[($tag + 2)]['content'], $matches); + preg_match('/([^\s]+)(?:\s+(.*))?/', $tokens[$tag + 2]['content'], $matches); $exception = $matches[1]; if (isset($matches[2]) === true) { $comment = $matches[2]; @@ -241,35 +252,6 @@ protected function processThrows(File $phpcsFile, $stackPtr, $commentStart) if ($exception === null) { $error = 'Exception type and comment missing for @throws tag in function comment'; $phpcsFile->addWarning($error, $tag, 'InvalidThrows'); - } elseif ($comment === null) { - $error = 'Comment missing for @throws tag in function comment'; - $phpcsFile->addWarning($error, $tag, 'EmptyThrows'); - } else { - // Any strings until the next tag belong to this comment. - if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) { - $end = $tokens[$commentStart]['comment_tags'][($pos + 1)]; - } else { - $end = $tokens[$commentStart]['comment_closer']; - } - - for ($i = ($tag + 3); $i < $end; $i++) { - if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) { - $comment .= ' ' . $tokens[$i]['content']; - } - } - - // Starts with a capital letter and ends with a fullstop. - $firstChar = $comment[0]; - if (strtoupper($firstChar) !== $firstChar) { - $error = '@throws tag comment must start with a capital letter'; - $phpcsFile->addWarning($error, ($tag + 2), 'ThrowsNotCapital'); - } - - $lastChar = substr($comment, -1); - if ($lastChar !== '.') { - $error = '@throws tag comment must end with a full stop'; - $phpcsFile->addWarning($error, ($tag + 2), 'ThrowsNoFullStop'); - } } } } @@ -284,7 +266,7 @@ protected function processThrows(File $phpcsFile, $stackPtr, $commentStart) */ protected function processParams(File $phpcsFile, $stackPtr, $commentStart) { - if ($this->isInheritDoc($phpcsFile, $stackPtr)) { + if ($this->isInheritDoc($phpcsFile, $commentStart)) { return; } @@ -300,13 +282,13 @@ protected function processParams(File $phpcsFile, $stackPtr, $commentStart) $type = $var = $comment = ''; $typeSpace = $varSpace = 0; $commentLines = []; - if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) { + if ($tokens[$tag + 2]['code'] === T_DOC_COMMENT_STRING) { $matches = []; - preg_match('/([^$&]+)(?:((?:\$|&)[^\s]+)(?:(\s+)(.*))?)?/', $tokens[($tag + 2)]['content'], $matches); + preg_match('/([^$]+)(?:((?:\$|&)[^\s]+)(?:(\s+)(.*))?)?/', $tokens[$tag + 2]['content'], $matches); $typeLen = strlen($matches[1]); $type = trim($matches[1]); - $typeSpace = ($typeLen - strlen($type)); + $typeSpace = $typeLen - strlen($type); $typeLen = strlen($type); if ($typeLen > $maxType) { $maxType = $typeLen; @@ -324,22 +306,22 @@ protected function processParams(File $phpcsFile, $stackPtr, $commentStart) $comment = $matches[4]; $commentLines[] = [ 'comment' => $comment, - 'token' => ($tag + 2), + 'token' => $tag + 2, 'indent' => $varSpace, ]; // Any strings until the next tag belong to this comment. - if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) { - $end = $tokens[$commentStart]['comment_tags'][($pos + 1)]; + if (isset($tokens[$commentStart]['comment_tags'][$pos + 1]) === true) { + $end = $tokens[$commentStart]['comment_tags'][$pos + 1]; } else { $end = $tokens[$commentStart]['comment_closer']; } - for ($i = ($tag + 3); $i < $end; $i++) { + for ($i = $tag + 3; $i < $end; $i++) { if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) { $indent = 0; - if ($tokens[($i - 1)]['code'] === T_DOC_COMMENT_WHITESPACE) { - $indent = strlen($tokens[($i - 1)]['content']); + if ($tokens[$i - 1]['code'] === T_DOC_COMMENT_WHITESPACE) { + $indent = strlen($tokens[$i - 1]['content']); } $comment .= ' ' . $tokens[$i]['content']; @@ -402,7 +384,7 @@ protected function processParams(File $phpcsFile, $stackPtr, $commentStart) if (isset($param['commentLines'][0])) { $content .= $param['commentLines'][0]['comment']; } - $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content); + $phpcsFile->fixer->replaceToken($param['tag'] + 2, $content); } } } @@ -436,7 +418,7 @@ protected function processParams(File $phpcsFile, $stackPtr, $commentStart) $content .= $realName; $content .= str_repeat(' ', $param['varSpace']); $content .= $param['commentLines'][0]['comment']; - $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content); + $phpcsFile->fixer->replaceToken($param['tag'] + 2, $content); } } } elseif (substr($param['var'], -4) !== ',...') { @@ -444,23 +426,6 @@ protected function processParams(File $phpcsFile, $stackPtr, $commentStart) $error = 'Superfluous parameter comment'; $phpcsFile->addError($error, $param['tag'], 'ExtraParamComment'); } - - if ($param['comment'] === '') { - continue; - } - - // Param comments must start with a capital letter and end with the full stop. - $firstChar = $param['comment'][0]; - if (preg_match('|\p{Lu}|u', $firstChar) === 0) { - $error = 'Parameter comment must start with a capital letter'; - $phpcsFile->addWarning($error, $param['tag'], 'ParamCommentNotCapital'); - } - - $lastChar = substr($param['comment'], -1); - if ($lastChar !== '.') { - $error = 'Parameter comment must end with a full stop'; - $phpcsFile->addWarning($error, $param['tag'], 'ParamCommentFullStop'); - } } $realNames = []; diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Commenting/InheritDocSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Commenting/InheritDocSniff.php new file mode 100644 index 000000000..a46746215 --- /dev/null +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Commenting/InheritDocSniff.php @@ -0,0 +1,105 @@ +getTokens(); + + $commentStart = $stackPtr; + $commentEnd = $tokens[$stackPtr]['comment_closer']; + + $empty = [ + T_DOC_COMMENT_WHITESPACE, + T_DOC_COMMENT_STAR, + ]; + + $inheritDoc = $phpcsFile->findNext($empty, $stackPtr + 1, $commentEnd, true); + if ($inheritDoc === false) { + // Ignore empty comments + return; + } + + if ( + preg_match('/@inheritDoc/i', $tokens[$inheritDoc]['content']) === 1 && + preg_match('/@inheritDoc/', $tokens[$inheritDoc]['content']) === 0 + ) { + $msg = 'inheritDoc is not capitalized correctly'; + $fix = $phpcsFile->addFixableWarning($msg, $inheritDoc, 'BadSpelling'); + if ($fix === true) { + $fixed = preg_replace('/inheritDoc/i', 'inheritDoc', $tokens[$inheritDoc]['content']); + $phpcsFile->fixer->replaceToken($inheritDoc, $fixed); + } + } + + if ( + preg_match('/^@inheritDoc/i', $tokens[$inheritDoc]['content']) === 1 && + ( preg_match('/^@inheritDoc$/i', $tokens[$inheritDoc]['content']) !== 1 || + $phpcsFile->findNext($empty, $inheritDoc + 1, $commentEnd, true) !== false + ) + ) { + $msg = 'When using @inheritDoc, it must be the only doc comment.'; + $phpcsFile->addWarning($msg, $inheritDoc, 'NotEmpty'); + } + + if ( + preg_match('/^{@inheritDoc}$/i', $tokens[$inheritDoc]['content']) === 1 && + $phpcsFile->findNext($empty, $inheritDoc + 1, $commentEnd, true) === false + ) { + $msg = 'When inheriting entire doc comment, @inheritDoc must be used instead of {@inheritDoc}.'; + $fix = $phpcsFile->addFixableWarning($msg, $inheritDoc, 'ShouldNotWrap'); + if ($fix === true) { + $phpcsFile->fixer->replaceToken($inheritDoc, '@inheritDoc'); + } + } + + if ( + preg_match('/^{@inheritDoc}/i', $tokens[$inheritDoc]['content']) === 1 && + preg_match('/^{@inheritDoc}$/i', $tokens[$inheritDoc]['content']) !== 1 + ) { + $msg = 'If using {@inheritDoc} to copy description, it must be the first line in doc comment.'; + $phpcsFile->addWarning($msg, $inheritDoc, 'FirstLine'); + } + + $nextComment = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $inheritDoc + 1, $commentEnd); + while ($nextComment !== false) { + if (preg_match('/^{@inheritDoc}$/i', $tokens[$nextComment]['content']) === 1) { + $msg = 'If using {@inheritDoc} to copy description, it must be the first line in doc comment.'; + $phpcsFile->addWarning($msg, $nextComment, 'FirstLine'); + } + $nextComment = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $nextComment + 1, $commentEnd); + } + } +} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/ControlStructures/ControlStructuresSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/ControlStructures/ControlStructuresSniff.php index 0727230c9..6fcd6dfc1 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/ControlStructures/ControlStructuresSniff.php +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/ControlStructures/ControlStructuresSniff.php @@ -1,16 +1,14 @@ getTokens(); - $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true); + $nextToken = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true); if ($tokens[$nextToken]['code'] === T_OPEN_PARENTHESIS) { $closer = $tokens[$nextToken]['parenthesis_closer']; $diff = $closer - $stackPtr; diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/ControlStructures/ElseIfDeclarationSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/ControlStructures/ElseIfDeclarationSniff.php index fa1816e11..fd2873c36 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/ControlStructures/ElseIfDeclarationSniff.php +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/ControlStructures/ElseIfDeclarationSniff.php @@ -1,16 +1,14 @@ getTokens(); - $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true); + $nextToken = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true); if ($tokens[$nextToken]['code'] !== T_IF) { return; } diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/ControlStructures/WhileStructuresSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/ControlStructures/WhileStructuresSniff.php index 42045f8a7..2307cbbeb 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/ControlStructures/WhileStructuresSniff.php +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/ControlStructures/WhileStructuresSniff.php @@ -1,16 +1,14 @@ getTokens(); - $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true); + $nextToken = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true); if ($tokens[$nextToken]['code'] === T_OPEN_PARENTHESIS) { $closer = $tokens[$nextToken]['parenthesis_closer']; $diff = $closer - $stackPtr; diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Formatting/BlankLineBeforeReturnSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Formatting/BlankLineBeforeReturnSniff.php index bebba1c67..7c9980555 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Formatting/BlankLineBeforeReturnSniff.php +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Formatting/BlankLineBeforeReturnSniff.php @@ -1,4 +1,18 @@ _processed[$phpcsFile->getFilename()])) { - return; - } - $filename = $phpcsFile->getFilename(); - - $this->_uses = []; - $next = $stackPtr; - - while ($next !== false) { - $this->_checkUseToken($phpcsFile, $next); - $next = $phpcsFile->findNext(T_USE, $next + 1); - } - - // Prevent multiple uses in the same file from entering - $this->_processed[$phpcsFile->getFilename()] = true; - - foreach ($this->_uses as $scope => $used) { - $defined = $sorted = array_keys($used); - - natcasesort($sorted); - $sorted = array_values($sorted); - if ($sorted === $defined) { - continue; - } - - foreach ($defined as $i => $name) { - if ($name !== $sorted[$i]) { - $error = 'Use classes must be in alphabetical order. Was expecting ' . $sorted[$i]; - $phpcsFile->addError($error, $used[$name], 'UseInAlphabeticalOrder'); - } - } - } - } - - /** - * Check all the use tokens in a file. - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file to check. - * @param int $stackPtr The index of the first use token. - * @return void - */ - protected function _checkUseToken($phpcsFile, $stackPtr) - { - // If the use token is for a closure we want to ignore it. - $isClosure = $this->_isClosure($phpcsFile, $stackPtr); - if ($isClosure) { - return; - } - - $tokens = $phpcsFile->getTokens(); - - $scope = 'use'; - $useType = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); - if ($useType !== false && $tokens[$useType]['code'] === T_STRING) { - $useContent = strtolower($tokens[$useType]['content']); - if ($useContent === 'function' || $useContent === 'const') { - $scope .= ' ' . $useContent; - $stackPtr = $useType + 1; - } - } - - $content = ''; - $end = $phpcsFile->findNext([T_SEMICOLON, T_OPEN_CURLY_BRACKET], $stackPtr); - $useTokens = array_slice($tokens, $stackPtr, $end - $stackPtr, true); - - foreach ($useTokens as $index => $token) { - if ($token['code'] === T_STRING || $token['code'] === T_NS_SEPARATOR) { - $content .= $token['content']; - } - } - - // Check for class scoping on use. Traits should be - // ordered independently. - if (!empty($token['conditions'])) { - $scope .= key($token['conditions']); - } - $this->_uses[$scope][$content] = $stackPtr; - } - - /** - * Check if the current stackPtr is a use token that is for a closure. - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The index of the first use token. - * @return bool - */ - protected function _isClosure($phpcsFile, $stackPtr) - { - return $phpcsFile->findPrevious( - [T_CLOSURE], - ($stackPtr - 1), - null, - false, - null, - true - ); - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Functions/ClosureDeclarationSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Functions/ClosureDeclarationSniff.php index eb0bbfa8d..4b2a86e05 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Functions/ClosureDeclarationSniff.php +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Functions/ClosureDeclarationSniff.php @@ -1,16 +1,14 @@ getTokens(); $spaces = 0; - if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) { - $spaces = strlen($tokens[($stackPtr + 1)]['content']); + if ($tokens[$stackPtr + 1]['code'] === T_WHITESPACE) { + $spaces = strlen($tokens[$stackPtr + 1]['content']); } if ($spaces !== 1) { diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Functions/FunctionDeclarationArgumentSpacingSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Functions/FunctionDeclarationArgumentSpacingSniff.php deleted file mode 100644 index e3f4ea50c..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Functions/FunctionDeclarationArgumentSpacingSniff.php +++ /dev/null @@ -1,34 +0,0 @@ -= 70300; - - /** - * @inheritDoc - */ - public function register() - { - return [ - T_OPEN_SHORT_ARRAY, - ]; - } - - /** - * @inheritDoc - */ - public function process(File $phpcsFile, $stackPtr) - { - $tokens = $phpcsFile->getTokens(); - $arrayToken = $tokens[$stackPtr]; - $closeParenthesisPointer = $arrayToken['bracket_closer']; - $openParenthesisToken = $tokens[$arrayToken['bracket_opener']]; - $closeParenthesisToken = $tokens[$closeParenthesisPointer]; - if ($openParenthesisToken['line'] === $closeParenthesisToken['line']) { - return; - } - - $previousToCloseParenthesisPointer = $phpcsFile->findPrevious(Tokens::$emptyTokens, $closeParenthesisPointer - 1, 0, true); - $previousToCloseParenthesisToken = $tokens[$previousToCloseParenthesisPointer]; - if ( - $previousToCloseParenthesisPointer === $arrayToken['bracket_opener'] - || $previousToCloseParenthesisToken['code'] === T_COMMA - || $closeParenthesisToken['line'] === $previousToCloseParenthesisToken['line'] - ) { - return; - } - if (!$this->enableAfterHeredoc && in_array($previousToCloseParenthesisToken['code'], [T_END_HEREDOC, T_END_NOWDOC], true)) { - return; - } - $fix = $phpcsFile->addFixableError( - 'Multi-line arrays must have a trailing comma after the last element.', - $previousToCloseParenthesisPointer, - 'MissingTrailingComma' - ); - if (!$fix) { - return; - } - - $phpcsFile->fixer->beginChangeset(); - $phpcsFile->fixer->addContent($previousToCloseParenthesisPointer, ','); - $phpcsFile->fixer->endChangeset(); - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/PHP/DisallowShortOpenTagSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/PHP/DisallowShortOpenTagSniff.php index eaf65480a..f1fbab200 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/PHP/DisallowShortOpenTagSniff.php +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/PHP/DisallowShortOpenTagSniff.php @@ -1,34 +1,30 @@ + * Dariusz Rumiński + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace CakePHP\Sniffs\PHP; + +use PHP_CodeSniffer\Files\File; +use PHP_CodeSniffer\Sniffs\Sniff; +use PHP_CodeSniffer\Util\Tokens; + +/** + * Converts double quotes to single quotes for simple strings. + * + * @author Gregor Harlan + * @author Mark Scherer + */ +class SingleQuoteSniff implements Sniff +{ + /** + * @inheritDoc + */ + public function register() + { + return [T_CONSTANT_ENCAPSED_STRING]; + } + + /** + * @inheritDoc + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + // Skip for complex multiline + $prevIndex = $phpcsFile->findPrevious(Tokens::$emptyTokens, $stackPtr - 1, null, true); + if ($prevIndex && $tokens[$prevIndex]['code'] === T_CONSTANT_ENCAPSED_STRING) { + return; + } + + $content = $tokens[$stackPtr]['content']; + if ( + $content[0] === '"' + && strpos($content, "'") === false + && strpos($content, "\n") === false + // regex: odd number of backslashes, not followed by double quote or dollar + && !preg_match('/(?addFixableError( + 'Use single instead of double quotes for simple strings.', + $stackPtr, + 'UseSingleQuote' + ); + if ($fix) { + $content = substr($content, 1, -1); + $content = str_replace(['\\"', '\\$'], ['"', '$'], $content); + $phpcsFile->fixer->replaceToken($stackPtr, '\'' . $content . '\''); + } + } + } +} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/PHP/TypeCastingSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/PHP/TypeCastingSniff.php deleted file mode 100644 index 0ccae90ea..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/PHP/TypeCastingSniff.php +++ /dev/null @@ -1,86 +0,0 @@ -getTokens(); - - // Process !! casts - if ($tokens[$stackPtr]['code'] == T_BOOLEAN_NOT) { - $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true); - if (!$nextToken) { - return; - } - if ($tokens[$nextToken]['code'] != T_BOOLEAN_NOT) { - return; - } - $error = 'Usage of !! cast is not allowed. Please use (bool) to cast.'; - $phpcsFile->addError($error, $stackPtr, 'NotAllowed'); - - return; - } - - // Only allow short forms if both short and long forms are possible - $matching = [ - '(boolean)' => '(bool)', - '(integer)' => '(int)', - ]; - $content = $tokens[$stackPtr]['content']; - $key = strtolower($content); - if (isset($matching[$key])) { - $error = 'Please use ' . $matching[$key] . ' instead of ' . $content . '.'; - $phpcsFile->addError($error, $stackPtr, 'NotAllowed'); - - return; - } - if ($content !== $key) { - $error = 'Please use ' . $key . ' instead of ' . $content . '.'; - $phpcsFile->addError($error, $stackPtr, 'NotAllowed'); - - return; - } - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Strings/ConcatenationSpacingSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Strings/ConcatenationSpacingSniff.php deleted file mode 100644 index 05b544d83..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/Strings/ConcatenationSpacingSniff.php +++ /dev/null @@ -1,71 +0,0 @@ -getTokens(); - if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE) { - $message = 'Expected 1 space before ., but 0 found'; - $phpcsFile->addError($message, $stackPtr, 'MissingBefore'); - } else { - $content = str_replace("\r\n", "\n", $tokens[($stackPtr - 1)]['content']); - $spaces = strlen($content); - if ($spaces > 1) { - $message = 'Expected 1 space before ., but %d found'; - $data = [$spaces]; - $phpcsFile->addError($message, $stackPtr, 'TooManyBefore', $data); - } - } - - if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) { - $message = 'Expected 1 space after ., but 0 found'; - $phpcsFile->addError($message, $stackPtr, 'MissingAfter'); - } else { - $content = str_replace("\r\n", "\n", $tokens[($stackPtr + 1)]['content']); - $spaces = strlen($content); - if ($spaces > 1) { - $message = 'Expected 1 space after ., but %d found'; - $data = [$spaces]; - $phpcsFile->addError($message, $stackPtr, 'TooManyAfter', $data); - } - } - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/CommaSpacingSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/CommaSpacingSniff.php deleted file mode 100644 index 7f96cda7c..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/CommaSpacingSniff.php +++ /dev/null @@ -1,72 +0,0 @@ -getTokens(); - - $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true); - - if ($tokens[$next]['code'] !== T_WHITESPACE && ($next !== $stackPtr + 2)) { - // Skip if immediate char is comma - if ($tokens[$next]['code'] === T_COMMA) { - return; - } - - // Last character in a line is ok. - if ($tokens[$next]['line'] === $tokens[$stackPtr]['line']) { - $error = 'Missing space after comma'; - $fix = $phpcsFile->addFixableError($error, $next, 'MissingSpaceAfterComma'); - if ($fix === true) { - $phpcsFile->fixer->beginChangeset(); - $phpcsFile->fixer->addContent($stackPtr, ' '); - $phpcsFile->fixer->endChangeset(); - } - } - } - - $previous = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true); - - if ($tokens[$previous]['code'] !== T_WHITESPACE && ($previous !== $stackPtr - 1)) { - $error = 'Space before comma, expected none, though'; - $phpcsFile->addError($error, $next, 'SpaceBeforeComma'); - } - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/EmptyLinesSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/EmptyLinesSniff.php index 667feed8a..e196fd758 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/EmptyLinesSniff.php +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/EmptyLinesSniff.php @@ -1,16 +1,14 @@ getTokens(); // Find the next non-empty token. - $openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + $openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true); if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) { // Not a function call. @@ -60,7 +56,7 @@ public function process(File $phpcsFile, $stackPtr) } // Look for funcName ( - if (($stackPtr + 1) !== $openBracket) { + if ($stackPtr + 1 !== $openBracket) { $error = 'Space before opening parenthesis of function call not allowed'; $phpcsFile->addError($error, $stackPtr, 'SpaceBeforeOpenBracket'); } diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/FunctionClosingBraceSpaceSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/FunctionClosingBraceSpaceSniff.php index 1a97f5d88..1f9fef674 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/FunctionClosingBraceSpaceSniff.php +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/FunctionClosingBraceSpaceSniff.php @@ -1,22 +1,19 @@ findPrevious(T_WHITESPACE, ($closeBrace - 1), null, true); + $prevContent = $phpcsFile->findPrevious(T_WHITESPACE, $closeBrace - 1, null, true); $braceLine = $tokens[$closeBrace]['line']; $prevLine = $tokens[$prevContent]['line']; - $found = ($braceLine - $prevLine - 1); - if ($phpcsFile->hasCondition($stackPtr, T_FUNCTION) === true || isset($tokens[$stackPtr]['nested_parenthesis']) === true) { + $found = $braceLine - $prevLine - 1; + if ( + $phpcsFile->hasCondition($stackPtr, T_FUNCTION) === true + || isset($tokens[$stackPtr]['nested_parenthesis']) === true + ) { // Nested function. if ($found < 0) { $error = 'Closing brace of nested function must be on a new line'; diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/FunctionOpeningBraceSpaceSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/FunctionOpeningBraceSpaceSniff.php index d0d54c0f7..76dbb12d4 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/FunctionOpeningBraceSpaceSniff.php +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/FunctionOpeningBraceSpaceSniff.php @@ -1,22 +1,19 @@ findNext(T_WHITESPACE, ($openBrace + 1), null, true); + $nextContent = $phpcsFile->findNext(T_WHITESPACE, $openBrace + 1, null, true); if ($nextContent === $tokens[$stackPtr]['scope_closer']) { // The next bit of content is the closing brace, so this @@ -58,7 +55,7 @@ public function process(File $phpcsFile, $stackPtr) $braceLine = $tokens[$openBrace]['line']; $nextLine = $tokens[$nextContent]['line']; - $found = ($nextLine - $braceLine - 1); + $found = $nextLine - $braceLine - 1; if ($found > 0) { $error = 'Expected 0 blank lines after opening function brace; %s found'; $data = [$found]; diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/FunctionSpacingSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/FunctionSpacingSniff.php index 75498ab7e..069b36597 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/FunctionSpacingSniff.php +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/FunctionSpacingSniff.php @@ -27,7 +27,7 @@ class FunctionSpacingSniff implements Sniff { /** - * {@inheritDoc} + * @inheritDoc */ public function register() { @@ -35,7 +35,7 @@ public function register() } /** - * {@inheritDoc} + * @inheritDoc */ public function process(File $phpCsFile, $stackPointer) { @@ -62,7 +62,11 @@ public function process(File $phpCsFile, $stackPointer) } if ($tokens[$nextContentIndex]['line'] - $tokens[$semicolonIndex]['line'] <= 1) { - $fix = $phpCsFile->addFixableError('Every function/method needs a newline afterwards', $closingParenthesisIndex, 'Abstract'); + $fix = $phpCsFile->addFixableError( + 'Every function/method needs a newline afterwards', + $closingParenthesisIndex, + 'Abstract' + ); if ($fix) { $phpCsFile->fixer->addNewline($semicolonIndex); } @@ -94,7 +98,6 @@ public function process(File $phpCsFile, $stackPointer) * @param \PHP_CodeSniffer\Files\File $phpCsFile File * @param int $closingBraceIndex Index * @param int|null $nextContentIndex Index - * * @return void */ protected function assertNewLineAtTheEnd(File $phpCsFile, $closingBraceIndex, $nextContentIndex) @@ -102,7 +105,11 @@ protected function assertNewLineAtTheEnd(File $phpCsFile, $closingBraceIndex, $n $tokens = $phpCsFile->getTokens(); if (!$nextContentIndex || $tokens[$nextContentIndex]['line'] - $tokens[$closingBraceIndex]['line'] <= 1) { - $fix = $phpCsFile->addFixableError('Every function/method needs a newline afterwards', $closingBraceIndex, 'Concrete'); + $fix = $phpCsFile->addFixableError( + 'Every function/method needs a newline afterwards', + $closingBraceIndex, + 'Concrete' + ); if ($fix) { $phpCsFile->fixer->addNewline($closingBraceIndex); } @@ -114,7 +121,6 @@ protected function assertNewLineAtTheEnd(File $phpCsFile, $closingBraceIndex, $n * * @param \PHP_CodeSniffer\Files\File $phpCsFile File * @param int $stackPointer Stack pointer - * * @return void */ protected function assertNewLineAtTheBeginning(File $phpCsFile, $stackPointer) @@ -146,7 +152,11 @@ protected function assertNewLineAtTheBeginning(File $phpCsFile, $stackPointer) return; } - $fix = $phpCsFile->addFixableError('Every function/method needs a newline before', $firstTokenInLineIndex, 'Concrete'); + $fix = $phpCsFile->addFixableError( + 'Every function/method needs a newline before', + $firstTokenInLineIndex, + 'Concrete' + ); if ($fix) { $phpCsFile->fixer->addNewline($prevContentIndex); } diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/ObjectOperatorSpacingSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/ObjectOperatorSpacingSniff.php deleted file mode 100644 index c14a16377..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/ObjectOperatorSpacingSniff.php +++ /dev/null @@ -1,50 +0,0 @@ -getTokens(); - - $nextType = $tokens[($stackPtr + 1)]['code']; - if (in_array($nextType, Tokens::$emptyTokens) === true) { - $error = 'Space found after object operator'; - $phpcsFile->addError($error, $stackPtr, 'After'); - } - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/OperatorSpacingSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/OperatorSpacingSniff.php deleted file mode 100644 index 8a332c7e5..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/OperatorSpacingSniff.php +++ /dev/null @@ -1,208 +0,0 @@ -getTokens(); - - // Skip default values in function declarations. - // and declare statements - if ( - $tokens[$stackPtr]['code'] === T_EQUAL - || $tokens[$stackPtr]['code'] === T_MINUS - ) { - if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) { - $parenthesis = array_keys($tokens[$stackPtr]['nested_parenthesis']); - $bracket = array_pop($parenthesis); - if (isset($tokens[$bracket]['parenthesis_owner']) === true) { - $function = $tokens[$bracket]['parenthesis_owner']; - if ( - $tokens[$function]['code'] === T_FUNCTION || - $tokens[$function]['code'] === T_DECLARE - ) { - return; - } - } - } - } - - if ($tokens[$stackPtr]['code'] === T_EQUAL) { - // Skip for '=&' case. - if (isset($tokens[($stackPtr + 1)]) === true && $tokens[($stackPtr + 1)]['code'] === T_BITWISE_AND) { - return; - } - } - - if ($tokens[$stackPtr]['code'] === T_BITWISE_AND) { - // If its not a reference, then we expect one space either side of the - // bitwise operator. - if (!$phpcsFile->isReference($stackPtr) && !$this->_isVariable($stackPtr, $tokens, $phpcsFile)) { - // Check there is one space before the & operator. - if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE) { - $error = 'Expected 1 space before "&" operator; 0 found'; - $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBeforeAmp'); - if ($fix === true) { - $phpcsFile->fixer->addContentBefore($stackPtr, ' '); - } - } - - // Check there is one space after the & operator. - if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) { - $error = 'Expected 1 space after "&" operator; 0 found'; - $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfterAmp'); - if ($fix === true) { - $phpcsFile->fixer->addContent($stackPtr, ' '); - } - } - } - } else { - if ($tokens[$stackPtr]['code'] === T_MINUS) { - // Skip declaration of negative value in new array format; eg. $arr = [-1]. - if ($tokens[($stackPtr - 1)]['code'] === T_OPEN_SHORT_ARRAY) { - return; - } - - // Check minus spacing, but make sure we aren't just assigning - // a minus value or returning one. - $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true); - if ($tokens[$prev]['code'] === T_RETURN) { - // Just returning a negative value; eg. return -1. - return; - } - - if (in_array($tokens[$prev]['code'], Tokens::$operators) === true) { - // Just trying to operate on a negative value; eg. ($var * -1). - return; - } - - if (in_array($tokens[$prev]['code'], Tokens::$comparisonTokens) === true) { - // Just trying to compare a negative value; eg. ($var === -1). - return; - } - - // A list of tokens that indicate that the token is not - // part of an arithmetic operation. - $invalidTokens = [ - T_COMMA, - T_OPEN_PARENTHESIS, - T_OPEN_SQUARE_BRACKET, - T_DOUBLE_ARROW, - T_COLON, - T_INLINE_THEN, - T_INLINE_ELSE, - T_CASE, - ]; - - if (in_array($tokens[$prev]['code'], $invalidTokens) === true) { - // Just trying to use a negative value; eg. myFunction($var, -2). - return; - } - if (in_array($tokens[$prev]['code'], Tokens::$assignmentTokens) === true) { - // Just trying to assign a negative value; eg. ($var = -1). - return; - } - } - - $operator = $tokens[$stackPtr]['content']; - - if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE) { - $error = "Expected 1 space before \"$operator\"; 0 found"; - $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBefore'); - if ($fix === true) { - $phpcsFile->fixer->addContentBefore($stackPtr, ' '); - } - } - - if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) { - $error = "Expected 1 space after \"$operator\"; 0 found"; - $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfter'); - if ($fix === true) { - $phpcsFile->fixer->addContent($stackPtr, ' '); - } - } - } - } - - /** - * Check if the current token is inside an array. - * - * @param int $stackPtr The current token offset. - * @param array $tokens The current token list. - * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked. - * @return bool - */ - protected function _isVariable($stackPtr, $tokens, $phpcsFile) - { - $tokenAfter = $phpcsFile->findNext( - Tokens::$emptyTokens, - ($stackPtr + 1), - null, - true - ); - $tokenBefore = $phpcsFile->findNext( - Tokens::$emptyTokens, - ($stackPtr - 1), - null, - true - ); - - return ($tokens[$tokenAfter]['code'] === T_VARIABLE && - ( - $tokens[$tokenBefore]['code'] === T_OPEN_PARENTHESIS || - $tokens[$tokenBefore]['code'] === T_COMMA || - $tokens[$tokenBefore]['code'] === T_OPEN_SHORT_ARRAY - ) - ); - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/TabAndSpaceSniff.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/TabAndSpaceSniff.php index e9d14cbf7..6d977d589 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/TabAndSpaceSniff.php +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Sniffs/WhiteSpace/TabAndSpaceSniff.php @@ -1,16 +1,14 @@ getTokens(); $line = $tokens[$stackPtr]['line']; - if ($stackPtr > 0 && $tokens[($stackPtr - 1)]['line'] !== $line) { + if ($stackPtr > 0 && $tokens[$stackPtr - 1]['line'] !== $line) { return; } diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Commenting/DocBlockAlignmentUnitTest.1.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Commenting/DocBlockAlignmentUnitTest.1.inc deleted file mode 100644 index 38bf963be..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Commenting/DocBlockAlignmentUnitTest.1.inc +++ /dev/null @@ -1,34 +0,0 @@ - 1, - 7 => 1, - 14 => 1, - 21 => 1, - 30 => 1, - ]; - - default: - return []; - } - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Commenting/FunctionCommentUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Commenting/FunctionCommentUnitTest.inc deleted file mode 100644 index 4225b82ab..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Commenting/FunctionCommentUnitTest.inc +++ /dev/null @@ -1,251 +0,0 @@ - 1, - 13 => 1, - 23 => 1, - 24 => 1, - 34 => 1, - 35 => 1, - 90 => 1, - 97 => 1, - 104 => 1, - 112 => 1, - 222 => 1, - 231 => 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return [ - 14 => 1, - 31 => 2, - 45 => 1, - 140 => 1, - 145 => 1, - 155 => 1, - 165 => 1, - 174 => 1, - 182 => 1, - 190 => 1, - 197 => 1, - 198 => 1, - 205 => 1, - 206 => 1, - 215 => 1, - 221 => 1, - ]; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/ControlStructures/ControlStructuresUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/ControlStructures/ControlStructuresUnitTest.inc deleted file mode 100644 index b51457787..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/ControlStructures/ControlStructuresUnitTest.inc +++ /dev/null @@ -1,14 +0,0 @@ - 100) { - echo 'i > 100'; -} - -if($abc == true) - echo 'hello'; diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/ControlStructures/ControlStructuresUnitTest.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/ControlStructures/ControlStructuresUnitTest.php deleted file mode 100644 index 1e1cc02bd..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/ControlStructures/ControlStructuresUnitTest.php +++ /dev/null @@ -1,26 +0,0 @@ - 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/ControlStructures/ElseIfDeclarationUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/ControlStructures/ElseIfDeclarationUnitTest.inc deleted file mode 100644 index aa97c9e14..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/ControlStructures/ElseIfDeclarationUnitTest.inc +++ /dev/null @@ -1,6 +0,0 @@ - 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/ControlStructures/WhileStructuresUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/ControlStructures/WhileStructuresUnitTest.inc deleted file mode 100644 index 79b57dae3..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/ControlStructures/WhileStructuresUnitTest.inc +++ /dev/null @@ -1,16 +0,0 @@ - 1, - 5 => 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Formatting/BlankLineBeforeReturnUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Formatting/BlankLineBeforeReturnUnitTest.inc deleted file mode 100644 index c5c3725c5..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Formatting/BlankLineBeforeReturnUnitTest.inc +++ /dev/null @@ -1,104 +0,0 @@ - 'bar' -]; - -function invalidFunctionReturnOne() -{ - echo ""; - return null; -} - -function invalidFunctionReturnOne() -{ - if(true) { - echo ""; - } - return null; -} - -/** - * [validFunctionReturnOne description] - * - * @return null - */ -function validFunctionReturnOne() -{ - return null; -} - -/** - * [validFunctionReturnTwo description] - * - * @return null|bool - */ -function validFunctionReturnTwo() -{ - if ($a) { - return true; - } - - return null; -} - -/** - * [validFunctionReturnThree description] - * - * @return null - */ -function validFunctionReturnThree() -{ - echo ""; - - return null; -} - -/** - * [validFunctionReturnFour description] - * - * @return null - */ -function validFunctionReturnFour() -{ - // comment - return null; -} - -/** - * [validFunctionReturnFive description] - * - * @return null - */ -function validFunctionReturnFive() -{ - /** - * multi-line - */ - return null; -} - -/** - * [validFunctionReturnSix description] - * - * @return null|bool - */ -function validFunctionReturnSix() -{ - switch ($condition) { - case 'foo': - return true; - default: - return false; - } -} - -/** - * [validFunctionReturnSeven description] - * - * @return null - */ -function validFunctionReturnSeven() -{ - /** @var Foo $foo */ - return null; -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Formatting/BlankLineBeforeReturnUnitTest.inc.fixed b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Formatting/BlankLineBeforeReturnUnitTest.inc.fixed deleted file mode 100644 index cb38504f4..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Formatting/BlankLineBeforeReturnUnitTest.inc.fixed +++ /dev/null @@ -1,106 +0,0 @@ - 'bar' -]; - -function invalidFunctionReturnOne() -{ - echo ""; - - return null; -} - -function invalidFunctionReturnOne() -{ - if(true) { - echo ""; - } - - return null; -} - -/** - * [validFunctionReturnOne description] - * - * @return null - */ -function validFunctionReturnOne() -{ - return null; -} - -/** - * [validFunctionReturnTwo description] - * - * @return null|bool - */ -function validFunctionReturnTwo() -{ - if ($a) { - return true; - } - - return null; -} - -/** - * [validFunctionReturnThree description] - * - * @return null - */ -function validFunctionReturnThree() -{ - echo ""; - - return null; -} - -/** - * [validFunctionReturnFour description] - * - * @return null - */ -function validFunctionReturnFour() -{ - // comment - return null; -} - -/** - * [validFunctionReturnFive description] - * - * @return null - */ -function validFunctionReturnFive() -{ - /** - * multi-line - */ - return null; -} - -/** - * [validFunctionReturnSix description] - * - * @return null|bool - */ -function validFunctionReturnSix() -{ - switch ($condition) { - case 'foo': - return true; - default: - return false; - } -} - -/** - * [validFunctionReturnSeven description] - * - * @return null - */ -function validFunctionReturnSeven() -{ - /** @var Foo $foo */ - return null; -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Formatting/BlankLineBeforeReturnUnitTest.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Formatting/BlankLineBeforeReturnUnitTest.php deleted file mode 100644 index 42a87ed16..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Formatting/BlankLineBeforeReturnUnitTest.php +++ /dev/null @@ -1,27 +0,0 @@ - 1, - 17 => 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Formatting/UseInAlphabeticalOrderUnitTest.1.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Formatting/UseInAlphabeticalOrderUnitTest.1.inc deleted file mode 100644 index 99780ef56..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Formatting/UseInAlphabeticalOrderUnitTest.1.inc +++ /dev/null @@ -1,10 +0,0 @@ - 1, - 4 => 1, - 8 => 1, - 9 => 1, - ]; - - default: - return []; - } - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Functions/ClosureDeclarationUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Functions/ClosureDeclarationUnitTest.inc deleted file mode 100644 index 013c2f015..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Functions/ClosureDeclarationUnitTest.inc +++ /dev/null @@ -1,36 +0,0 @@ - 1, - 30 => 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Functions/FunctionDeclarationArgumentSpacingUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Functions/FunctionDeclarationArgumentSpacingUnitTest.inc deleted file mode 100644 index 82a3e7a0d..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Functions/FunctionDeclarationArgumentSpacingUnitTest.inc +++ /dev/null @@ -1,21 +0,0 @@ - 1, - 4 => 2, - 5 => 2, - 6 => 2, - 7 => 2, - 8 => 2, - 9 => 9, - 10 => 6, - 11 => 6, - 12 => 4, - 13 => 4, - 14 => 2, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/NamingConventions/ValidFunctionNameUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/NamingConventions/ValidFunctionNameUnitTest.inc deleted file mode 100644 index e4bc5652d..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/NamingConventions/ValidFunctionNameUnitTest.inc +++ /dev/null @@ -1,177 +0,0 @@ -passingPublic = 'changed'; - $this->underscored = 'has value now'; - $this->doubleUnderscore = 'not recommended'; - } - - public static function setStatics() - { - self::$publicStatic = true; - self::$protectedStatic = true; - self::$privateStatic = true; - } - - protected function _someFunc() - { - // code here - } - - protected function noUnderscorePrefix() - { - // code here - $closure = function () { - // code here - }; - } - - public function __call($name, $arguments) - { - } - public function __construct() - { - } - public function __clone() - { - } - public function __debugInfo() - { - } - public function __destruct() - { - } - public function __get($name) - { - } - public function __invoke() - { - } - public function __isset($name) - { - } - public function __set($name, $value) - { - } - public function __sleep() - { - } - public function __toString() - { - return ''; - } - public function __unset($name) - { - } - public function __wakeup() - { - } -} - -interface FunctionNamesInterface -{ - public function _forbidden(); - - public function setVariables(); - - public static function setStatics(); -} - -trait FunctionNamesTrait -{ - public function _forbidden() - { - echo "I emit an error"; - } - - private function notDiscouraged() - { - echo "I don't emit a warning anymore"; - } - - public function setVariables() - { - $this->passingPublic = 'changed'; - $this->underscored = 'has value now'; - $this->doubleUnderscore = 'not recommended'; - } - - public static function setStatics() - { - self::$publicStatic = true; - self::$protectedStatic = true; - self::$privateStatic = true; - } - - protected function _someFunc() - { - // code here - } - - protected function noUnderscorePrefix() - { - // code here - } - - function _noScopeSpecified() - { - echo 'handled by an other Sniff'; - } - - public function __call($name, $arguments) - { - } - public function __construct() - { - } - public function __clone() - { - } - public function __debugInfo() - { - } - public function __destruct() - { - } - public function __get($name) - { - } - public function __invoke() - { - } - public function __isset($name) - { - } - public function __set($name, $value) - { - } - public function __sleep() - { - } - public function __toString() - { - return ''; - } - public function __unset($name) - { - } - public function __wakeup() - { - } -} - -public function __passingOutside() {} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/NamingConventions/ValidFunctionNameUnitTest.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/NamingConventions/ValidFunctionNameUnitTest.php deleted file mode 100644 index 5312675f4..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/NamingConventions/ValidFunctionNameUnitTest.php +++ /dev/null @@ -1,28 +0,0 @@ - 1, - 87 => 1, - 96 => 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/NamingConventions/ValidTraitNameUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/NamingConventions/ValidTraitNameUnitTest.inc deleted file mode 100644 index 57a83ea23..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/NamingConventions/ValidTraitNameUnitTest.inc +++ /dev/null @@ -1,6 +0,0 @@ - 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/PHP/DisallowShortOpenTagUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/PHP/DisallowShortOpenTagUnitTest.inc deleted file mode 100644 index 97995d335..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/PHP/DisallowShortOpenTagUnitTest.inc +++ /dev/null @@ -1,9 +0,0 @@ - - - 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/PHP/TypeCastingUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/PHP/TypeCastingUnitTest.inc deleted file mode 100644 index d1c47b389..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/PHP/TypeCastingUnitTest.inc +++ /dev/null @@ -1,11 +0,0 @@ - 1, - 4 => 1, - 5 => 1, - 6 => 1, - 7 => 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Strings/ConcatenationSpacingUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Strings/ConcatenationSpacingUnitTest.inc deleted file mode 100644 index 44cc71658..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/Strings/ConcatenationSpacingUnitTest.inc +++ /dev/null @@ -1,10 +0,0 @@ - 1, - 5 => 1, - 6 => 2, - 7 => 1, - 8 => 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/CommaSpacingUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/CommaSpacingUnitTest.inc deleted file mode 100644 index a4c9adbab..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/CommaSpacingUnitTest.inc +++ /dev/null @@ -1,5 +0,0 @@ - 1, - 3 => 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/EmptyLinesUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/EmptyLinesUnitTest.inc deleted file mode 100644 index 7fb201f93..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/EmptyLinesUnitTest.inc +++ /dev/null @@ -1,49 +0,0 @@ - 1, - 21 => 1, - 42 => 1, - 43 => 1, - 47 => 1, - 48 => 1, - 49 => 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/FunctionCallSpacingUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/FunctionCallSpacingUnitTest.inc deleted file mode 100644 index 57070a382..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/FunctionCallSpacingUnitTest.inc +++ /dev/null @@ -1,8 +0,0 @@ -something ('testing'); -fail_whale (); diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/FunctionCallSpacingUnitTest.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/FunctionCallSpacingUnitTest.php deleted file mode 100644 index d98f76dba..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/FunctionCallSpacingUnitTest.php +++ /dev/null @@ -1,32 +0,0 @@ - 1, - 3 => 1, - 4 => 1, - 5 => 1, - 6 => 1, - 7 => 1, - 8 => 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/FunctionClosingBraceSpaceUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/FunctionClosingBraceSpaceUnitTest.inc deleted file mode 100644 index fc76fc8d0..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/FunctionClosingBraceSpaceUnitTest.inc +++ /dev/null @@ -1,31 +0,0 @@ - 1, - 6 => 1, - 10 => 1, - 25 => 1, - 28 => 1, - 30 => 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/FunctionOpeningBraceSpaceUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/FunctionOpeningBraceSpaceUnitTest.inc deleted file mode 100644 index 6e81d9f25..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/FunctionOpeningBraceSpaceUnitTest.inc +++ /dev/null @@ -1,10 +0,0 @@ - 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/ObjectOperatorSpacingUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/ObjectOperatorSpacingUnitTest.inc deleted file mode 100644 index 0dc8f6aab..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/ObjectOperatorSpacingUnitTest.inc +++ /dev/null @@ -1,4 +0,0 @@ - failling() ; -$test->passing() ; diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/ObjectOperatorSpacingUnitTest.php b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/ObjectOperatorSpacingUnitTest.php deleted file mode 100644 index 513bbbc65..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/ObjectOperatorSpacingUnitTest.php +++ /dev/null @@ -1,26 +0,0 @@ - 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/OperatorSpacingUnitTest.inc b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/OperatorSpacingUnitTest.inc deleted file mode 100644 index d04d62a05..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/OperatorSpacingUnitTest.inc +++ /dev/null @@ -1,34 +0,0 @@ - 1, - 4 => 1, - 5 => 2, - 6 => 1, - 7 => 1, - 8 => 2, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/OperatorSpacingunitTest.inc.fixed b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/OperatorSpacingunitTest.inc.fixed deleted file mode 100644 index 26c56125d..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/Tests/WhiteSpace/OperatorSpacingunitTest.inc.fixed +++ /dev/null @@ -1,34 +0,0 @@ - 1, - 3 => 1, - ]; - } - - /** - * {@inheritDoc} - */ - public function getWarningList() - { - return []; - } -} diff --git a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/ruleset.xml b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/ruleset.xml index 02573fc58..c205a1275 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/CakePHP/ruleset.xml +++ b/app/vendor/cakephp/cakephp-codesniffer/CakePHP/ruleset.xml @@ -1,12 +1,35 @@ - + CakePHP coding standard + + \.git - */Config/*.ini.php /*/tmp/ + tests/*/templates/* + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + 0 + - - 0 - - - 0 - - - 0 - - - - - 0 - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - - - + - - + + + + + + + + + */tests/* + + + + + + */tests/* + */config/* */tests/* - - */tests/* - + */src/Controller/* + */src/Command/* + */src/Shell/* */tests/* - - */CakePHP/* - */tests/* + + + + + + - - */tests/* + + + + + - - */tests/* + + + + + + + + + + - - */src/* - */tests/* + + + + + - - */src/* - */tests/* + + 0 + + + 0 - - */src/* + + + + + + + + + */tests/* + + + + + + */templates/* + + + + + + + + + + + + */config/* + */templates/* + */tests/Fixture/* + + + + + + diff --git a/app/vendor/cakephp/cakephp-codesniffer/LICENSE b/app/vendor/cakephp/cakephp-codesniffer/LICENSE new file mode 100644 index 000000000..291d80e1b --- /dev/null +++ b/app/vendor/cakephp/cakephp-codesniffer/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2005-present, Cake Software Foundation, Inc. (https://cakefoundation.org) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/app/vendor/cakephp/cakephp-codesniffer/LICENSE.txt b/app/vendor/cakephp/cakephp-codesniffer/LICENSE.txt deleted file mode 100644 index 0a0a98c8e..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/LICENSE.txt +++ /dev/null @@ -1,28 +0,0 @@ -The MIT License - -CakePHP(tm) : The Rapid Development PHP Framework (http://cakephp.org) -Copyright (c) 2005-present, Cake Software Foundation, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - -Cake Software Foundation, Inc. -1785 E. Sahara Avenue, -Suite 490-204 -Las Vegas, Nevada 89104, -United States of America. diff --git a/app/vendor/cakephp/cakephp-codesniffer/README.md b/app/vendor/cakephp/cakephp-codesniffer/README.md index 5ef17389e..533515fa3 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/README.md +++ b/app/vendor/cakephp/cakephp-codesniffer/README.md @@ -1,35 +1,31 @@ # CakePHP Code Sniffer -[![Build Status](https://img.shields.io/travis/cakephp/cakephp-codesniffer/master.svg?style=flat-square)](https://travis-ci.org/cakephp/cakephp-codesniffer) -[![Coverage Status](https://img.shields.io/codecov/c/github/cakephp/cakephp-codesniffer.svg?style=flat-square)](https://codecov.io/github/cakephp/cakephp-codesniffer) +[![Build Status](https://img.shields.io/travis/com/cakephp/cakephp-codesniffer/master.svg?style=flat-square)](https://travis-ci.com/cakephp/cakephp-codesniffer) [![Total Downloads](https://img.shields.io/packagist/dt/cakephp/cakephp-codesniffer.svg?style=flat-square)](https://packagist.org/packages/cakephp/cakephp-codesniffer) [![Latest Stable Version](https://img.shields.io/packagist/v/cakephp/cakephp-codesniffer.svg?style=flat-square)](https://packagist.org/packages/cakephp/cakephp-codesniffer) +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) -This code works with [phpcs](http://pear.php.net/manual/en/package.php.php-codesniffer.php) +This code works with [squizlabs/php_codesniffer](https://github.com/squizlabs/PHP_CodeSniffer) and checks code against the coding standards used in CakePHP. -:warning: The `master` branch contains codesniffer rules that are based on the -PSR2 standard. If you want to check against the historical CakePHP coding -standard use any of the `1.x` releases. +This sniffer package follows [PSR-12](https://www.php-fig.org/psr/psr-12/) completely and ships with a lot of additional fixers on top. -## Which version should I use? +[List of included sniffs](/docs) -| Sniffer version | CakePHP version | PHP min | -| -------- | ------- | ------- | -| 1.x | 2.x | PHP 5.4 | -| 2.x | 3.x | PHP 5.5 | -| 3.x | 3.x | PHP 5.6 | -| 4.x | 4.x | PHP 7.1 | +## Which version should I use? +See [version map](https://github.com/cakephp/cakephp-codesniffer/wiki). ## Installation You should install this codesniffer with composer: - composer require --dev "cakephp/cakephp-codesniffer" + composer require --dev cakephp/cakephp-codesniffer vendor/bin/phpcs --config-set installed_paths /path/to/your/app/vendor/cakephp/cakephp-codesniffer The second command lets `phpcs` know where to find your new sniffs. Ensure that -you do not overwrite any existing `installed_paths` value. +you do not overwrite any existing `installed_paths` value. Alternatively, install +the [`dealerdirect/phpcodesniffer-composer-installer`](https://github.com/Dealerdirect/phpcodesniffer-composer-installer) +composer package which will handle configuring the `phpcs` `installed_paths` for you. ## Usage @@ -39,20 +35,20 @@ you have configured the CodeSniffer `installed_paths` setting. Depending on how you installed the code sniffer changes how you run it. If you have installed phpcs, and this package with PEAR, you can do the following: - vendor/bin/phpcs --colors -p -s --standard=CakePHP /path/to/code + vendor/bin/phpcs --colors -p -s --standard=CakePHP /path/to/code/ You can also copy the `phpcs.xml.dist` file to your project's root folder as `phpcs.xml`. This file will import the CakePHP Coding Standard. From there you can edit it to include/exclude as needed. With this file in place, you can run: - vendor/bin/phpcs --colors -p -s /path/to/code + vendor/bin/phpcs --colors -p -s /path/to/code/ If you are using Composer to manage your CakePHP project, you can also add the below to your composer.json file: ```json { "scripts": { - "cs-check": "vendor/bin/phpcs --colors -p -s --extensions=ctp,php ./src ./tests" + "cs-check": "vendor/bin/phpcs --colors -p -s --extensions=php src/ tests/" } } ``` diff --git a/app/vendor/cakephp/cakephp-codesniffer/composer.json b/app/vendor/cakephp/cakephp-codesniffer/composer.json index 551c0c632..3ceb62a7a 100644 --- a/app/vendor/cakephp/cakephp-codesniffer/composer.json +++ b/app/vendor/cakephp/cakephp-codesniffer/composer.json @@ -18,11 +18,12 @@ "source": "https://github.com/cakephp/cakephp-codesniffer" }, "require": { - "php": ">=5.6", - "squizlabs/php_codesniffer": "^3.0.0" + "php": ">=7.2.0", + "slevomat/coding-standard": "^6.3.6", + "squizlabs/php_codesniffer": "~3.5.5" }, "require-dev": { - "phpunit/phpunit": "<6.0" + "phpunit/phpunit": "^7.1" }, "autoload": { "psr-4": { @@ -30,26 +31,14 @@ } }, "scripts": { - "increase-severity": "sed -i.bak 's/0<\\/severity>//' CakePHP/ruleset.xml", - "reset-ruleset": [ - "sed -i.bak 's//0<\\/severity>/' CakePHP/ruleset.xml", - "rm -f CakePHP/ruleset.xml.bak" - ], "add-standard" : "phpcs --config-set installed_paths $(pwd)", "test": [ "@add-standard", - "@increase-severity", - "phpunit", - "@reset-ruleset" - ], - "test-coverage": [ - "@add-standard", - "@increase-severity", - "phpunit --coverage-clover=clover.xml", - "@reset-ruleset" + "phpunit --filter CakePHP" ], - "cs-check": "vendor/bin/phpcs --colors -p -s --extensions=php CakePHP/", - "cs-fix": "vendor/bin/phpcbf --colors -p --extensions=php CakePHP/", - "explain": "vendor/bin/phpcs -e" + "cs-check": "phpcs --colors --parallel=16 -p -s CakePHP/", + "cs-fix": "phpcbf --colors --parallel=16 -p CakePHP/", + "docs": "php docs/generate.php", + "explain": "phpcs -e --standard=CakePHP" } } diff --git a/app/vendor/cakephp/cakephp-codesniffer/docs/README.md b/app/vendor/cakephp/cakephp-codesniffer/docs/README.md new file mode 100644 index 000000000..46929d92a --- /dev/null +++ b/app/vendor/cakephp/cakephp-codesniffer/docs/README.md @@ -0,0 +1,169 @@ +# CakePHP ruleset + +The CakePHP standard contains 140 sniffs + +CakePHP (19 sniffs) +------------------- +- CakePHP.Classes.ReturnTypeHint +- CakePHP.Commenting.DocBlockAlignment +- CakePHP.Commenting.FunctionComment +- CakePHP.Commenting.InheritDoc +- CakePHP.ControlStructures.ControlStructures +- CakePHP.ControlStructures.ElseIfDeclaration +- CakePHP.ControlStructures.WhileStructures +- CakePHP.Formatting.BlankLineBeforeReturn +- CakePHP.Functions.ClosureDeclaration +- CakePHP.NamingConventions.ValidFunctionName +- CakePHP.NamingConventions.ValidTraitName +- CakePHP.PHP.DisallowShortOpenTag +- CakePHP.PHP.SingleQuote +- CakePHP.WhiteSpace.EmptyLines +- CakePHP.WhiteSpace.FunctionCallSpacing +- CakePHP.WhiteSpace.FunctionClosingBraceSpace +- CakePHP.WhiteSpace.FunctionOpeningBraceSpace +- CakePHP.WhiteSpace.FunctionSpacing +- CakePHP.WhiteSpace.TabAndSpace + +Generic (25 sniffs) +------------------- +- Generic.Arrays.DisallowLongArraySyntax +- Generic.CodeAnalysis.ForLoopShouldBeWhileLoop +- Generic.CodeAnalysis.ForLoopWithTestFunctionCall +- Generic.CodeAnalysis.JumbledIncrementer +- Generic.CodeAnalysis.UnconditionalIfStatement +- Generic.CodeAnalysis.UnnecessaryFinalModifier +- Generic.ControlStructures.InlineControlStructure +- Generic.Files.ByteOrderMark +- Generic.Files.LineEndings +- Generic.Files.LineLength +- Generic.Formatting.DisallowMultipleStatements +- Generic.Formatting.NoSpaceAfterCast +- Generic.Functions.FunctionCallArgumentSpacing +- Generic.NamingConventions.UpperCaseConstantName +- Generic.PHP.DeprecatedFunctions +- Generic.PHP.DisallowAlternativePHPTags +- Generic.PHP.DisallowShortOpenTag +- Generic.PHP.ForbiddenFunctions +- Generic.PHP.LowerCaseConstant +- Generic.PHP.LowerCaseKeyword +- Generic.PHP.LowerCaseType +- Generic.PHP.NoSilencedErrors +- Generic.WhiteSpace.DisallowTabIndent +- Generic.WhiteSpace.IncrementDecrementSpacing +- Generic.WhiteSpace.ScopeIndent + +PEAR (2 sniffs) +--------------- +- PEAR.Functions.ValidDefaultValue +- PEAR.NamingConventions.ValidFunctionName + +PSR1 (3 sniffs) +--------------- +- PSR1.Classes.ClassDeclaration +- PSR1.Files.SideEffects +- PSR1.Methods.CamelCapsMethodName + +PSR12 (16 sniffs) +----------------- +- PSR12.Classes.AnonClassDeclaration +- PSR12.Classes.ClassInstantiation +- PSR12.Classes.ClosingBrace +- PSR12.ControlStructures.BooleanOperatorPlacement +- PSR12.ControlStructures.ControlStructureSpacing +- PSR12.Files.DeclareStatement +- PSR12.Files.FileHeader +- PSR12.Files.ImportStatement +- PSR12.Files.OpenTag +- PSR12.Functions.NullableTypeDeclaration +- PSR12.Functions.ReturnTypeDeclaration +- PSR12.Keywords.ShortFormTypeKeywords +- PSR12.Namespaces.CompoundNamespaceDepth +- PSR12.Operators.OperatorSpacing +- PSR12.Properties.ConstantVisibility +- PSR12.Traits.UseDeclaration + +PSR2 (12 sniffs) +---------------- +- PSR2.Classes.ClassDeclaration +- PSR2.Classes.PropertyDeclaration +- PSR2.ControlStructures.ControlStructureSpacing +- PSR2.ControlStructures.ElseIfDeclaration +- PSR2.ControlStructures.SwitchDeclaration +- PSR2.Files.ClosingTag +- PSR2.Files.EndFileNewline +- PSR2.Methods.FunctionCallSignature +- PSR2.Methods.FunctionClosingBrace +- PSR2.Methods.MethodDeclaration +- PSR2.Namespaces.NamespaceDeclaration +- PSR2.Namespaces.UseDeclaration + +SlevomatCodingStandard (32 sniffs) +---------------------------------- +- SlevomatCodingStandard.Arrays.TrailingArrayComma +- SlevomatCodingStandard.Classes.ClassConstantVisibility +- SlevomatCodingStandard.Classes.EmptyLinesAroundClassBraces +- SlevomatCodingStandard.Classes.ModernClassNameReference +- SlevomatCodingStandard.Commenting.DisallowOneLinePropertyDocComment +- SlevomatCodingStandard.Commenting.DocCommentSpacing +- SlevomatCodingStandard.Commenting.EmptyComment +- SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration +- SlevomatCodingStandard.ControlStructures.AssignmentInCondition +- SlevomatCodingStandard.ControlStructures.DisallowContinueWithoutIntegerOperandInSwitch +- SlevomatCodingStandard.ControlStructures.DisallowYodaComparison +- SlevomatCodingStandard.ControlStructures.LanguageConstructWithParentheses +- SlevomatCodingStandard.ControlStructures.NewWithParentheses +- SlevomatCodingStandard.ControlStructures.RequireNullCoalesceOperator +- SlevomatCodingStandard.Exceptions.DeadCatch +- SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses +- SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation +- SlevomatCodingStandard.Namespaces.NamespaceDeclaration +- SlevomatCodingStandard.Namespaces.UnusedUses +- SlevomatCodingStandard.Namespaces.UseDoesNotStartWithBackslash +- SlevomatCodingStandard.Namespaces.UseFromSameNamespace +- SlevomatCodingStandard.Namespaces.UseSpacing +- SlevomatCodingStandard.PHP.ShortList +- SlevomatCodingStandard.PHP.TypeCast +- SlevomatCodingStandard.PHP.UselessParentheses +- SlevomatCodingStandard.PHP.UselessSemicolon +- SlevomatCodingStandard.TypeHints.DeclareStrictTypes +- SlevomatCodingStandard.TypeHints.LongTypeHints +- SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue +- SlevomatCodingStandard.TypeHints.ParameterTypeHintSpacing +- SlevomatCodingStandard.TypeHints.ReturnTypeHintSpacing +- SlevomatCodingStandard.Variables.DuplicateAssignmentToVariable + +Squiz (29 sniffs) +----------------- +- Squiz.Arrays.ArrayBracketSpacing +- Squiz.Classes.ClassFileName +- Squiz.Classes.LowercaseClassKeywords +- Squiz.Classes.ValidClassName +- Squiz.Commenting.DocCommentAlignment +- Squiz.ControlStructures.ControlSignature +- Squiz.ControlStructures.ForEachLoopDeclaration +- Squiz.ControlStructures.ForLoopDeclaration +- Squiz.ControlStructures.LowercaseDeclaration +- Squiz.Functions.FunctionDeclaration +- Squiz.Functions.FunctionDeclarationArgumentSpacing +- Squiz.Functions.LowercaseFunctionKeywords +- Squiz.Functions.MultiLineFunctionDeclaration +- Squiz.NamingConventions.ValidFunctionName +- Squiz.Operators.ValidLogicalOperators +- Squiz.PHP.DisallowSizeFunctionsInLoops +- Squiz.PHP.Eval +- Squiz.PHP.NonExecutableCode +- Squiz.Scope.MemberVarScope +- Squiz.Scope.MethodScope +- Squiz.Scope.StaticThisUsage +- Squiz.WhiteSpace.CastSpacing +- Squiz.WhiteSpace.ControlStructureSpacing +- Squiz.WhiteSpace.LanguageConstructSpacing +- Squiz.WhiteSpace.LogicalOperatorSpacing +- Squiz.WhiteSpace.ScopeClosingBrace +- Squiz.WhiteSpace.ScopeKeywordSpacing +- Squiz.WhiteSpace.SemicolonSpacing +- Squiz.WhiteSpace.SuperfluousWhitespace + +Zend (1 sniff) +--------------- +- Zend.NamingConventions.ValidVariableName diff --git a/app/vendor/cakephp/cakephp-codesniffer/docs/generate.php b/app/vendor/cakephp/cakephp-codesniffer/docs/generate.php new file mode 100644 index 000000000..2689e2f14 --- /dev/null +++ b/app/vendor/cakephp/cakephp-codesniffer/docs/generate.php @@ -0,0 +1,26 @@ +#!/usr/bin/env php + - - CakePHP coding standard - - - diff --git a/app/vendor/cakephp/cakephp-codesniffer/phpcs.xml.dist b/app/vendor/cakephp/cakephp-codesniffer/phpcs.xml.dist deleted file mode 100644 index f3b4f08f2..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/phpcs.xml.dist +++ /dev/null @@ -1,6 +0,0 @@ - - - App Coding Standard - - - diff --git a/app/vendor/cakephp/cakephp-codesniffer/phpunit.xml b/app/vendor/cakephp/cakephp-codesniffer/phpunit.xml deleted file mode 100644 index de25c2672..000000000 --- a/app/vendor/cakephp/cakephp-codesniffer/phpunit.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - vendor/squizlabs/php_codesniffer/tests/AllTests.php - - - - - - - ./CakePHP/Sniffs - - - diff --git a/app/vendor/cakephp/cakephp/.coveralls.yml b/app/vendor/cakephp/cakephp/.coveralls.yml deleted file mode 100644 index bc71b62f8..000000000 --- a/app/vendor/cakephp/cakephp/.coveralls.yml +++ /dev/null @@ -1,2 +0,0 @@ -coverage_clover: clover.xml -json_path: coveralls-upload.json diff --git a/app/vendor/cakephp/cakephp/README.md b/app/vendor/cakephp/cakephp/README.md index 627f72609..259e3fb1d 100644 --- a/app/vendor/cakephp/cakephp/README.md +++ b/app/vendor/cakephp/cakephp/README.md @@ -7,11 +7,9 @@ Software License - - Build Status - - - Coverage Status + Build Status + + Coverage Status Code Consistency @@ -41,6 +39,8 @@ a starting point. For existing applications you can run the following: $ composer require cakephp/cakephp ``` +For details on the (minimum/maximum) PHP version see [version map](https://github.com/cakephp/cakephp/wiki#version-map). + ## Running Tests Assuming you have PHPUnit installed system wide using one of the methods stated @@ -68,6 +68,7 @@ tests for CakePHP by doing the following: ## Get Support! * [Slack](https://cakesf.herokuapp.com/) - Join us on Slack. +* [Discord](https://discord.gg/k4trEMPebj) - Join us on Discord. * [#cakephp](https://webchat.freenode.net/?channels=#cakephp) on irc.freenode.net - Come chat with us, we have cake. * [Forum](https://discourse.cakephp.org/) - Official CakePHP forum. * [GitHub Issues](https://github.com/cakephp/cakephp/issues) - Got issues? Please tell us! diff --git a/app/vendor/cakephp/cakephp/SECURITY.md b/app/vendor/cakephp/cakephp/SECURITY.md index fed257e9e..551efc470 100644 --- a/app/vendor/cakephp/cakephp/SECURITY.md +++ b/app/vendor/cakephp/cakephp/SECURITY.md @@ -4,13 +4,12 @@ We support fixing security issues on the following releases: -| Version | Supported | -| ------- | ------------------ | -| 4.0.x | :white_check_mark: | -| 3.8.x | :white_check_mark: | -| 3.7.x | :white_check_mark: | -| <= 3.6 | :x: | -| 2.10.x | :white_check_mark: | +| Version | Supported | Security fixes until +| ------- | ------------------ | -------------------- +| 4.x | :white_check_mark: | +| 3.10.x | :white_check_mark: | 15th December 2022 (36 months after 4.0.0) +| <= 3.9 | :x: | +| 2.10.x | :x: | 15th June 2021 (18 months after 4.0.0) | <= 2.9 | :x: | ## Reporting a Vulnerability diff --git a/app/vendor/cakephp/cakephp/VERSION.txt b/app/vendor/cakephp/cakephp/VERSION.txt index c4bcf7458..741c5af23 100644 --- a/app/vendor/cakephp/cakephp/VERSION.txt +++ b/app/vendor/cakephp/cakephp/VERSION.txt @@ -16,4 +16,4 @@ // @license https://opensource.org/licenses/mit-license.php MIT License // +--------------------------------------------------------------------------------------------+ // //////////////////////////////////////////////////////////////////////////////////////////////////// -4.0.10 +4.2.10 diff --git a/app/vendor/cakephp/cakephp/composer.json b/app/vendor/cakephp/cakephp/composer.json index e89f5c60f..2575cda2d 100644 --- a/app/vendor/cakephp/cakephp/composer.json +++ b/app/vendor/cakephp/cakephp/composer.json @@ -1,7 +1,7 @@ { "name": "cakephp/cakephp", - "description": "The CakePHP framework", "type": "library", + "description": "The CakePHP framework", "keywords": [ "framework", "mvc", @@ -21,39 +21,54 @@ "homepage": "https://github.com/cakephp/cakephp/graphs/contributors" } ], - "support": { - "issues": "https://github.com/cakephp/cakephp/issues", - "forum": "https://stackoverflow.com/tags/cakephp", - "irc": "irc://irc.freenode.org/cakephp", - "source": "https://github.com/cakephp/cakephp" - }, "require": { "php": ">=7.2.0", - "ext-json": "*", "ext-intl": "*", + "ext-json": "*", "ext-mbstring": "*", - "aura/intl": "^3.0.0", "cakephp/chronos": "^2.0", "composer/ca-bundle": "^1.2", "laminas/laminas-diactoros": "^2.2.2", "laminas/laminas-httphandlerrunner": "^1.1", + "league/container": "^3.2", "psr/http-client": "^1.0", "psr/http-server-handler": "^1.0", "psr/http-server-middleware": "^1.0", "psr/log": "^1.0.0", "psr/simple-cache": "^1.0.0" }, - "suggest": { - "ext-openssl": "To use Security::encrypt() or have secure CSRF token generation.", - "ext-curl": "To enable more efficient network calls in Http\\Client.", - "lib-ICU": "The intl PHP library, to use Text::transliterate() or Text::slug()", - "paragonie/csp-builder": "CSP builder, to use the CSP Middleware" + "replace": { + "cakephp/cache": "self.version", + "cakephp/collection": "self.version", + "cakephp/console": "self.version", + "cakephp/core": "self.version", + "cakephp/database": "self.version", + "cakephp/datasource": "self.version", + "cakephp/event": "self.version", + "cakephp/filesystem": "self.version", + "cakephp/form": "self.version", + "cakephp/http": "self.version", + "cakephp/i18n": "self.version", + "cakephp/log": "self.version", + "cakephp/orm": "self.version", + "cakephp/utility": "self.version", + "cakephp/validation": "self.version" }, "require-dev": { "cakephp/cakephp-codesniffer": "^4.0", "mikey179/vfsstream": "^1.6", "paragonie/csp-builder": "^2.3", - "phpunit/phpunit": "~8.5.0" + "phpunit/phpunit": "^8.5 || ^9.3" + }, + "suggest": { + "ext-curl": "To enable more efficient network calls in Http\\Client.", + "ext-openssl": "To use Security::encrypt() or have secure CSRF token generation.", + "lib-ICU": "The intl PHP library, to use Text::transliterate() or Text::slug()", + "paragonie/csp-builder": "CSP builder, to use the CSP Middleware" + }, + "config": { + "process-timeout": 900, + "sort-packages": true }, "autoload": { "psr-4": { @@ -63,6 +78,7 @@ "src/Core/functions.php", "src/Collection/functions.php", "src/I18n/functions.php", + "src/Routing/functions.php", "src/Utility/bootstrap.php" ] }, @@ -79,23 +95,6 @@ "ParentPlugin\\": "tests/test_app/Plugin/ParentPlugin/src/" } }, - "replace": { - "cakephp/cache": "self.version", - "cakephp/collection": "self.version", - "cakephp/console": "self.version", - "cakephp/core": "self.version", - "cakephp/datasource": "self.version", - "cakephp/database": "self.version", - "cakephp/event": "self.version", - "cakephp/filesystem": "self.version", - "cakephp/form": "self.version", - "cakephp/http": "self.version", - "cakephp/i18n": "self.version", - "cakephp/log": "self.version", - "cakephp/orm": "self.version", - "cakephp/utility": "self.version", - "cakephp/validation": "self.version" - }, "scripts": { "check": [ "@cs-check", @@ -103,18 +102,20 @@ ], "cs-check": "phpcs --colors --parallel=16 -p src/ tests/", "cs-fix": "phpcbf --colors --parallel=16 -p src/ tests/", - "test": "phpunit", - "test-coverage": "phpunit --coverage-clover=clover.xml", "phpstan": "phpstan.phar analyse", "psalm": "psalm.phar --show-info=false", "stan": [ "@phpstan", "@psalm" ], - "stan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan:^0.12 psalm/phar:~3.11.2 && mv composer.backup composer.json" + "stan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan:0.12.94 psalm/phar:~4.9.3 && mv composer.backup composer.json", + "test": "phpunit", + "test-coverage": "phpunit --coverage-clover=clover.xml" }, - "config": { - "sort-packages": true, - "process-timeout": 900 + "support": { + "issues": "https://github.com/cakephp/cakephp/issues", + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "source": "https://github.com/cakephp/cakephp" } } diff --git a/app/vendor/cakephp/cakephp/src/Auth/BasicAuthenticate.php b/app/vendor/cakephp/cakephp/src/Auth/BasicAuthenticate.php index 8e0b62065..a09d438a5 100644 --- a/app/vendor/cakephp/cakephp/src/Auth/BasicAuthenticate.php +++ b/app/vendor/cakephp/cakephp/src/Auth/BasicAuthenticate.php @@ -89,13 +89,13 @@ public function getUser(ServerRequest $request) * * @param \Cake\Http\ServerRequest $request A request object. * @param \Cake\Http\Response $response A response object. - * @return void + * @return \Cake\Http\Response|null|void * @throws \Cake\Http\Exception\UnauthorizedException */ public function unauthenticated(ServerRequest $request, Response $response) { $unauthorizedException = new UnauthorizedException(); - $unauthorizedException->responseHeader($this->loginHeaders($request)); + $unauthorizedException->setHeaders($this->loginHeaders($request)); throw $unauthorizedException; } diff --git a/app/vendor/cakephp/cakephp/src/Auth/ControllerAuthorize.php b/app/vendor/cakephp/cakephp/src/Auth/ControllerAuthorize.php index 89a9e05b5..58355ba78 100644 --- a/app/vendor/cakephp/cakephp/src/Auth/ControllerAuthorize.php +++ b/app/vendor/cakephp/cakephp/src/Auth/ControllerAuthorize.php @@ -18,7 +18,7 @@ use Cake\Controller\ComponentRegistry; use Cake\Controller\Controller; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Http\ServerRequest; /** @@ -80,13 +80,13 @@ public function controller(?Controller $controller = null): Controller * * @param array|\ArrayAccess $user Active user data * @param \Cake\Http\ServerRequest $request Request instance. - * @throws \Cake\Core\Exception\Exception If controller does not have method `isAuthorized()`. + * @throws \Cake\Core\Exception\CakeException If controller does not have method `isAuthorized()`. * @return bool */ public function authorize($user, ServerRequest $request): bool { if (!method_exists($this->_Controller, 'isAuthorized')) { - throw new Exception(sprintf( + throw new CakeException(sprintf( '%s does not implement an isAuthorized() method.', get_class($this->_Controller) )); diff --git a/app/vendor/cakephp/cakephp/src/Auth/DigestAuthenticate.php b/app/vendor/cakephp/cakephp/src/Auth/DigestAuthenticate.php index 738c79029..b5ecda934 100644 --- a/app/vendor/cakephp/cakephp/src/Auth/DigestAuthenticate.php +++ b/app/vendor/cakephp/cakephp/src/Auth/DigestAuthenticate.php @@ -123,7 +123,12 @@ public function getUser(ServerRequest $request) $password = $user[$field]; unset($user[$field]); - $hash = $this->generateResponseHash($digest, $password, (string)$request->getEnv('ORIGINAL_REQUEST_METHOD')); + $requestMethod = $request->getEnv('ORIGINAL_REQUEST_METHOD') ?: $request->getMethod(); + $hash = $this->generateResponseHash( + $digest, + $password, + (string)$requestMethod + ); if (hash_equals($hash, $digest['response'])) { return $user; } diff --git a/app/vendor/cakephp/cakephp/src/Cache/Cache.php b/app/vendor/cakephp/cakephp/src/Cache/Cache.php index 417d8a6ec..56a9c34da 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/Cache.php +++ b/app/vendor/cakephp/cakephp/src/Cache/Cache.php @@ -68,7 +68,7 @@ class Cache use StaticConfigTrait; /** - * An array mapping url schemes to fully qualified caching engine + * An array mapping URL schemes to fully qualified caching engine * class names. * * @var string[] @@ -206,6 +206,8 @@ protected static function _buildEngine(string $name): void */ public static function engine(string $config) { + deprecationWarning('Cache::engine() is deprecated. Use Cache::pool() instead.'); + return static::pool($config); } @@ -568,7 +570,7 @@ public static function enabled(): bool * * ``` * $results = Cache::remember('all_articles', function () { - * return $this->find('all'); + * return $this->find('all')->toArray(); * }); * ``` * @@ -577,9 +579,8 @@ public static function enabled(): bool * the cache key is empty. Can be any callable type supported by your PHP. * @param string $config The cache configuration to use for this operation. * Defaults to default. - * @return mixed If the key is found: the cached data, false if the data - * missing/expired, or an error. If the key is not found: boolean of the - * success of the write + * @return mixed If the key is found: the cached data. + * If the key is not found the value returned by the callable. */ public static function remember(string $key, callable $callable, string $config = 'default') { @@ -587,7 +588,7 @@ public static function remember(string $key, callable $callable, string $config if ($existing !== null) { return $existing; } - $results = call_user_func($callable); + $results = $callable(); self::write($key, $results, $config); return $results; diff --git a/app/vendor/cakephp/cakephp/src/Cache/CacheEngine.php b/app/vendor/cakephp/cakephp/src/Cache/CacheEngine.php index 2ece9ebcd..56db69536 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/CacheEngine.php +++ b/app/vendor/cakephp/cakephp/src/Cache/CacheEngine.php @@ -342,7 +342,7 @@ protected function _key($key): string if ($this->_groupPrefix) { $prefix = md5(implode('_', $this->groups())); } - $key = preg_replace('/[\s]+/', '_', (string)$key); + $key = preg_replace('/[\s]+/', '_', $key); return $this->_config['prefix'] . $prefix . $key; } diff --git a/app/vendor/cakephp/cakephp/src/Cache/CacheRegistry.php b/app/vendor/cakephp/cakephp/src/Cache/CacheRegistry.php index 0b8c9cf63..fb6d0ab06 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/CacheRegistry.php +++ b/app/vendor/cakephp/cakephp/src/Cache/CacheRegistry.php @@ -87,7 +87,10 @@ protected function _create($class, string $alias, array $config): CacheEngine if (!$instance->init($config)) { throw new RuntimeException( - sprintf('Cache engine %s is not properly configured.', get_class($instance)) + sprintf( + 'Cache engine %s is not properly configured. Check error log for additional information.', + get_class($instance) + ) ); } diff --git a/app/vendor/cakephp/cakephp/src/Cache/Engine/ApcuEngine.php b/app/vendor/cakephp/cakephp/src/Cache/Engine/ApcuEngine.php index 14d1f111a..170ea4a43 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/Engine/ApcuEngine.php +++ b/app/vendor/cakephp/cakephp/src/Cache/Engine/ApcuEngine.php @@ -18,6 +18,7 @@ use APCUIterator; use Cake\Cache\CacheEngine; +use RuntimeException; /** * APCu storage engine for cache @@ -43,7 +44,7 @@ class ApcuEngine extends CacheEngine public function init(array $config = []): bool { if (!extension_loaded('apcu')) { - return false; + throw new RuntimeException('The `apcu` extension must be enabled to use ApcuEngine.'); } return parent::init($config); diff --git a/app/vendor/cakephp/cakephp/src/Cache/Engine/ArrayEngine.php b/app/vendor/cakephp/cakephp/src/Cache/Engine/ArrayEngine.php index 0c89c2e21..c33608204 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/Engine/ArrayEngine.php +++ b/app/vendor/cakephp/cakephp/src/Cache/Engine/ArrayEngine.php @@ -43,17 +43,17 @@ class ArrayEngine extends CacheEngine * Write data for key into cache * * @param string $key Identifier for the data - * @param mixed $data Data to be cached + * @param mixed $value Data to be cached * @param \DateInterval|int|null $ttl Optional. The TTL value of this item. If no value is sent and * the driver supports TTL then the library may set a default value * for it or let the driver take care of that. * @return bool True on success and false on failure. */ - public function set($key, $data, $ttl = null): bool + public function set($key, $value, $ttl = null): bool { $key = $this->_key($key); $expires = time() + $this->duration($ttl); - $this->data[$key] = ['exp' => $expires, 'val' => $data]; + $this->data[$key] = ['exp' => $expires, 'val' => $value]; return true; } diff --git a/app/vendor/cakephp/cakephp/src/Cache/Engine/FileEngine.php b/app/vendor/cakephp/cakephp/src/Cache/Engine/FileEngine.php index f8447a036..22ec33ce0 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/Engine/FileEngine.php +++ b/app/vendor/cakephp/cakephp/src/Cache/Engine/FileEngine.php @@ -105,15 +105,15 @@ public function init(array $config = []): bool * Write data for key into cache * * @param string $key Identifier for the data - * @param mixed $data Data to be cached + * @param mixed $value Data to be cached * @param \DateInterval|int|null $ttl Optional. The TTL value of this item. If no value is sent and * the driver supports TTL then the library may set a default value * for it or let the driver take care of that. * @return bool True on success and false on failure. */ - public function set($key, $data, $ttl = null): bool + public function set($key, $value, $ttl = null): bool { - if ($data === '' || !$this->_init) { + if ($value === '' || !$this->_init) { return false; } @@ -124,11 +124,11 @@ public function set($key, $data, $ttl = null): bool } if (!empty($this->_config['serialize'])) { - $data = serialize($data); + $value = serialize($value); } $expires = time() + $this->duration($ttl); - $contents = implode([$expires, PHP_EOL, $data, PHP_EOL]); + $contents = implode([$expires, PHP_EOL, $value, PHP_EOL]); if ($this->_config['lock']) { /** @psalm-suppress PossiblyNullReference */ @@ -198,7 +198,7 @@ public function get($key, $default = null) $data = trim($data); if ($data !== '' && !empty($this->_config['serialize'])) { - $data = unserialize((string)$data); + $data = unserialize($data); } return $data; @@ -251,18 +251,33 @@ public function clear(): bool RecursiveIteratorIterator::SELF_FIRST ); $cleared = []; - foreach ($contents as $path) { - if ($path->isFile()) { + /** @var \SplFileInfo $fileInfo */ + foreach ($contents as $fileInfo) { + if ($fileInfo->isFile()) { + unset($fileInfo); continue; } - $path = $path->getRealPath() . DIRECTORY_SEPARATOR; + $realPath = $fileInfo->getRealPath(); + if (!$realPath) { + unset($fileInfo); + continue; + } + + $path = $realPath . DIRECTORY_SEPARATOR; if (!in_array($path, $cleared, true)) { $this->_clearDirectory($path); $cleared[] = $path; } + + // possible inner iterators need to be unset too in order for locks on parents to be released + unset($fileInfo); } + // unsetting iterators helps releasing possible locks in certain environments, + // which could otherwise make `rmdir()` fail + unset($directory, $contents); + return true; } @@ -365,7 +380,7 @@ protected function _setKey(string $key, bool $createKey = false): bool $this->_File->getBasename() !== $key || $this->_File->valid() === false ) { - $exists = file_exists($path->getPathname()); + $exists = is_file($path->getPathname()); try { $this->_File = $path->openFile('c+'); } catch (Exception $e) { @@ -477,6 +492,10 @@ function (SplFileInfo $current) use ($group, $prefix) { @unlink($path); } + // unsetting iterators helps releasing possible locks in certain environments, + // which could otherwise make `rmdir()` fail + unset($directoryIterator, $contents, $filtered); + return true; } } diff --git a/app/vendor/cakephp/cakephp/src/Cache/Engine/MemcachedEngine.php b/app/vendor/cakephp/cakephp/src/Cache/Engine/MemcachedEngine.php index 6c3f8878a..781c4cfc7 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/Engine/MemcachedEngine.php +++ b/app/vendor/cakephp/cakephp/src/Cache/Engine/MemcachedEngine.php @@ -19,6 +19,7 @@ use Cake\Cache\CacheEngine; use InvalidArgumentException; use Memcached; +use RuntimeException; /** * Memcached storage engine for cache. Memcached has some limitations in the amount of @@ -51,8 +52,8 @@ class MemcachedEngine extends CacheEngine * the same persistent value will share a single underlying connection. * - `prefix` Prepended to all entries. Good for when you need to share a keyspace * with either another cache config or another application. - * - `serialize` The serializer engine used to serialize data. Available engines are php, - * igbinary and json. Beside php, the memcached extension must be compiled with the + * - `serialize` The serializer engine used to serialize data. Available engines are 'php', + * 'igbinary' and 'json'. Beside 'php', the memcached extension must be compiled with the * appropriate serializer support. * - `servers` String or array of memcached servers. If an array MemcacheEngine will use * them as a pool. @@ -79,7 +80,7 @@ class MemcachedEngine extends CacheEngine /** * List of available serializer engines * - * Memcached must be compiled with json and igbinary support to use these engines + * Memcached must be compiled with JSON and igbinary support to use these engines * * @var array */ @@ -103,7 +104,7 @@ class MemcachedEngine extends CacheEngine public function init(array $config = []): bool { if (!extension_loaded('memcached')) { - return false; + throw new RuntimeException('The `memcached` extension must be enabled to use MemcachedEngine.'); } $this->_serializers = [ @@ -133,6 +134,7 @@ public function init(array $config = []): bool $this->_config['servers'] = [$this->_config['servers']]; } + /** @psalm-suppress RedundantPropertyInitializationCheck */ if (isset($this->_Memcached)) { return true; } @@ -144,7 +146,20 @@ public function init(array $config = []): bool } $this->_setOptions(); - if (count($this->_Memcached->getServerList())) { + $serverList = $this->_Memcached->getServerList(); + if ($serverList) { + if ($this->_Memcached->isPersistent()) { + foreach ($serverList as $server) { + if (!in_array($server['host'] . ':' . $server['port'], $this->_config['servers'], true)) { + throw new InvalidArgumentException( + 'Invalid cache configuration. Multiple persistent cache configurations are detected' . + ' with different `servers` values. `servers` values for persistent cache configurations' . + ' must be the same when using the same persistence id.' + ); + } + } + } + return true; } @@ -301,21 +316,21 @@ public function set($key, $value, $ttl = null): bool /** * Write many cache entries to the cache at once * - * @param iterable $data An array of data to be stored in the cache + * @param iterable $values An array of data to be stored in the cache * @param \DateInterval|int|null $ttl Optional. The TTL value of this item. If no value is sent and * the driver supports TTL then the library may set a default value * for it or let the driver take care of that. * @return bool Whether the write was successful or not. */ - public function setMultiple($data, $ttl = null): bool + public function setMultiple($values, $ttl = null): bool { $cacheData = []; - foreach ($data as $key => $value) { + foreach ($values as $key => $value) { $cacheData[$this->_key($key)] = $value; } $duration = $this->duration($ttl); - return (bool)$this->_Memcached->setMulti($cacheData, $duration); + return $this->_Memcached->setMulti($cacheData, $duration); } /** diff --git a/app/vendor/cakephp/cakephp/src/Cache/Engine/RedisEngine.php b/app/vendor/cakephp/cakephp/src/Cache/Engine/RedisEngine.php index 665863c2f..1eeed8cad 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/Engine/RedisEngine.php +++ b/app/vendor/cakephp/cakephp/src/Cache/Engine/RedisEngine.php @@ -18,8 +18,10 @@ namespace Cake\Cache\Engine; use Cake\Cache\CacheEngine; +use Cake\Log\Log; use Redis; use RedisException; +use RuntimeException; /** * Redis storage engine for cache. @@ -45,7 +47,7 @@ class RedisEngine extends CacheEngine * - `port` port number to the Redis server. * - `prefix` Prefix appended to all entries. Good for when you need to share a keyspace * with either another cache config or another application. - * - `server` URL or ip to the Redis server host. + * - `server` URL or IP to the Redis server host. * - `timeout` timeout in seconds (float). * - `unix_socket` Path to the unix socket file (default: false) * @@ -76,7 +78,7 @@ class RedisEngine extends CacheEngine public function init(array $config = []): bool { if (!extension_loaded('redis')) { - return false; + throw new RuntimeException('The `redis` extension must be enabled to use RedisEngine.'); } if (!empty($config['host'])) { @@ -115,6 +117,10 @@ protected function _connect(): bool ); } } catch (RedisException $e) { + if (class_exists(Log::class)) { + Log::error('RedisEngine could not connect. Got error: ' . $e->getMessage()); + } + return false; } if ($return && $this->_config['password']) { @@ -180,7 +186,7 @@ public function increment(string $key, int $offset = 1) $duration = $this->_config['duration']; $key = $this->_key($key); - $value = (int)$this->_Redis->incrBy($key, $offset); + $value = $this->_Redis->incrBy($key, $offset); if ($duration > 0) { $this->_Redis->expire($key, $duration); } @@ -200,7 +206,7 @@ public function decrement(string $key, int $offset = 1) $duration = $this->_config['duration']; $key = $this->_key($key); - $value = (int)$this->_Redis->decrBy($key, $offset); + $value = $this->_Redis->decrBy($key, $offset); if ($duration > 0) { $this->_Redis->expire($key, $duration); } diff --git a/app/vendor/cakephp/cakephp/src/Cache/Engine/WincacheEngine.php b/app/vendor/cakephp/cakephp/src/Cache/Engine/WincacheEngine.php index de6b34834..9d9e36eb6 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/Engine/WincacheEngine.php +++ b/app/vendor/cakephp/cakephp/src/Cache/Engine/WincacheEngine.php @@ -17,6 +17,7 @@ namespace Cake\Cache\Engine; use Cake\Cache\CacheEngine; +use RuntimeException; /** * Wincache storage engine for cache @@ -44,7 +45,7 @@ class WincacheEngine extends CacheEngine public function init(array $config = []): bool { if (!extension_loaded('wincache')) { - return false; + throw new RuntimeException('The `wincache` extension must be enabled to use WincacheEngine.'); } parent::init($config); diff --git a/app/vendor/cakephp/cakephp/src/Cache/InvalidArgumentException.php b/app/vendor/cakephp/cakephp/src/Cache/InvalidArgumentException.php index 69450c9cf..c44a45808 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/InvalidArgumentException.php +++ b/app/vendor/cakephp/cakephp/src/Cache/InvalidArgumentException.php @@ -16,12 +16,12 @@ */ namespace Cake\Cache; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Psr\SimpleCache\InvalidArgumentException as InvalidArgumentInterface; /** * Exception raised when cache keys are invalid. */ -class InvalidArgumentException extends Exception implements InvalidArgumentInterface +class InvalidArgumentException extends CakeException implements InvalidArgumentInterface { } diff --git a/app/vendor/cakephp/cakephp/src/Cache/README.md b/app/vendor/cakephp/cakephp/src/Cache/README.md index 7e3d6c743..5f2c078bd 100644 --- a/app/vendor/cakephp/cakephp/src/Cache/README.md +++ b/app/vendor/cakephp/cakephp/src/Cache/README.md @@ -39,7 +39,7 @@ $object = new FileEngine($config); Cache::config('other', $object); ``` -You can now read a write from the cache: +You can now read and write from the cache: ```php $data = Cache::remember('my_cache_key', function () { diff --git a/app/vendor/cakephp/cakephp/src/Collection/CollectionInterface.php b/app/vendor/cakephp/cakephp/src/Collection/CollectionInterface.php index d1d4467cb..986e63976 100644 --- a/app/vendor/cakephp/cakephp/src/Collection/CollectionInterface.php +++ b/app/vendor/cakephp/cakephp/src/Collection/CollectionInterface.php @@ -28,9 +28,7 @@ interface CollectionInterface extends Iterator, JsonSerializable { /** - * Executes the passed callable for each of the elements in this collection - * and passes both the value and key for them on each step. - * Returns the same collection for chaining. + * Applies a callback to the elements in this collection. * * ### Example: * @@ -40,11 +38,10 @@ interface CollectionInterface extends Iterator, JsonSerializable * }); * ``` * - * @param callable $c callable function that will receive each of the elements - * in this collection + * @param callable $callback Callback to run for each element in collection. * @return $this */ - public function each(callable $c); + public function each(callable $callback); /** * Looks through each value in the collection, and returns another collection with @@ -66,12 +63,12 @@ public function each(callable $c); * }); * ``` * - * @param callable|null $c the method that will receive each of the elements and + * @param callable|null $callback the method that will receive each of the elements and * returns true whether or not they should be in the resulting collection. * If left null, a callback that filters out falsey values will be used. * @return self */ - public function filter(?callable $c = null): CollectionInterface; + public function filter(?callable $callback = null): CollectionInterface; /** * Looks through each value in the collection, and returns another collection with @@ -92,11 +89,11 @@ public function filter(?callable $c = null): CollectionInterface; * }); * ``` * - * @param callable $c the method that will receive each of the elements and + * @param callable $callback the method that will receive each of the elements and * returns true whether or not they should be out of the resulting collection. * @return self */ - public function reject(callable $c): CollectionInterface; + public function reject(callable $callback): CollectionInterface; /** * Returns true if all values in this collection pass the truth test provided @@ -116,11 +113,11 @@ public function reject(callable $c): CollectionInterface; * * Empty collections always return true because it is a vacuous truth. * - * @param callable $c a callback function + * @param callable $callback a callback function * @return bool true if for all elements in this collection the provided * callback returns true, false otherwise. */ - public function every(callable $c): bool; + public function every(callable $callback): bool; /** * Returns true if any of the values in this collection pass the truth test @@ -138,11 +135,11 @@ public function every(callable $c): bool; * }); * ``` * - * @param callable $c a callback function + * @param callable $callback a callback function * @return bool true if the provided callback returns true for any element in this * collection, false otherwise */ - public function some(callable $c): bool; + public function some(callable $callback): bool; /** * Returns true if $value is present in this collection. Comparisons are made @@ -171,11 +168,11 @@ public function contains($value): bool; * }); * ``` * - * @param callable $c the method that will receive each of the elements and + * @param callable $callback the method that will receive each of the elements and * returns the new value for the key that is being iterated * @return self */ - public function map(callable $c): CollectionInterface; + public function map(callable $callback): CollectionInterface; /** * Folds the values in this collection to a single value, as the result of @@ -185,15 +182,15 @@ public function map(callable $c): CollectionInterface; * If $zero is omitted the first value of the collection will be used in its place * and reduction will start from the second item. * - * @param callable $c The callback function to be called - * @param mixed $zero The state of reduction + * @param callable $callback The callback function to be called + * @param mixed $initial The state of reduction * @return mixed */ - public function reduce(callable $c, $zero = null); + public function reduce(callable $callback, $initial = null); /** * Returns a new collection containing the column or property value found in each - * of the elements, as requested in the $matcher param. + * of the elements. * * The matcher can be a string with a property name to extract or a dot separated * path of properties that should be followed to get the last one in the path. @@ -229,16 +226,16 @@ public function reduce(callable $c, $zero = null); * [1, 2, 3, 4] * ``` * - * @param string|callable $matcher A dot separated path of column to follow + * @param string|callable $path A dot separated path of column to follow * so that the final one can be returned or a callable that will take care * of doing that. * @return self */ - public function extract($matcher): CollectionInterface; + public function extract($path): CollectionInterface; /** * Returns the top element in this collection after being sorted by a property. - * Check the sortBy method for information on the callback and $type parameters + * Check the sortBy method for information on the callback and $sort parameters * * ### Examples: * @@ -254,17 +251,17 @@ public function extract($matcher): CollectionInterface; * echo $max->name; * ``` * - * @param callable|string $callback the callback or column name to use for sorting - * @param int $type the type of comparison to perform, either SORT_STRING + * @param callable|string $path The column name to use for sorting or callback that returns the value. + * @param int $sort The sort type, one of SORT_STRING * SORT_NUMERIC or SORT_NATURAL * @see \Cake\Collection\CollectionInterface::sortBy() * @return mixed The value of the top element in the collection */ - public function max($callback, int $type = \SORT_NUMERIC); + public function max($path, int $sort = \SORT_NUMERIC); /** * Returns the bottom element in this collection after being sorted by a property. - * Check the sortBy method for information on the callback and $type parameters + * Check the sortBy method for information on the callback and $sort parameters * * ### Examples: * @@ -280,16 +277,16 @@ public function max($callback, int $type = \SORT_NUMERIC); * echo $min->name; * ``` * - * @param callable|string $callback the callback or column name to use for sorting - * @param int $type the type of comparison to perform, either SORT_STRING + * @param callable|string $path The column name to use for sorting or callback that returns the value. + * @param int $sort The sort type, one of SORT_STRING * SORT_NUMERIC or SORT_NATURAL * @see \Cake\Collection\CollectionInterface::sortBy() * @return mixed The value of the bottom element in the collection */ - public function min($callback, int $type = \SORT_NUMERIC); + public function min($path, int $sort = \SORT_NUMERIC); /** - * Returns the average of all the values extracted with $matcher + * Returns the average of all the values extracted with $path * or of this collection. * * ### Example: @@ -308,15 +305,18 @@ public function min($callback, int $type = \SORT_NUMERIC); * // Total: 2 * ``` * - * @param string|callable|null $matcher The property name to sum or a function + * The average of an empty set or 0 rows is `null`. Collections with `null` + * values are not considered empty. + * + * @param string|callable|null $path The property name to sum or a function * If no value is passed, an identity function will be used. * that will return the value of the property to sum. * @return float|int|null */ - public function avg($matcher = null); + public function avg($path = null); /** - * Returns the median of all the values extracted with $matcher + * Returns the median of all the values extracted with $path * or of this collection. * * ### Example: @@ -338,12 +338,15 @@ public function avg($matcher = null); * // Total: 2.5 * ``` * - * @param string|callable|null $matcher The property name to sum or a function + * The median of an empty set or 0 rows is `null`. Collections with `null` + * values are not considered empty. + * + * @param string|callable|null $path The property name to sum or a function * If no value is passed, an identity function will be used. * that will return the value of the property to sum. * @return float|int|null */ - public function median($matcher = null); + public function median($path = null); /** * Returns a sorted iterator out of the elements in this collection, @@ -375,13 +378,13 @@ public function median($matcher = null); * } * ``` * - * @param callable|string $callback the callback or column name to use for sorting - * @param int $dir either SORT_DESC or SORT_ASC - * @param int $type the type of comparison to perform, either SORT_STRING + * @param callable|string $path The column name to use for sorting or callback that returns the value. + * @param int $order The sort order, either SORT_DESC or SORT_ASC + * @param int $sort The sort type, one of SORT_STRING * SORT_NUMERIC or SORT_NATURAL * @return self */ - public function sortBy($callback, int $dir = SORT_DESC, int $type = \SORT_NUMERIC): CollectionInterface; + public function sortBy($path, int $order = SORT_DESC, int $sort = \SORT_NUMERIC): CollectionInterface; /** * Splits a collection into sets, grouped by the result of running each value @@ -420,11 +423,11 @@ public function sortBy($callback, int $dir = SORT_DESC, int $type = \SORT_NUMERI * ]; * ``` * - * @param callable|string $callback the callback or column name to use for grouping + * @param callable|string $path The column name to use for grouping or callback that returns the value. * or a function returning the grouping key out of the provided element * @return self */ - public function groupBy($callback): CollectionInterface; + public function groupBy($path): CollectionInterface; /** * Given a list and a callback function that returns a key for each element @@ -459,11 +462,11 @@ public function groupBy($callback): CollectionInterface; * ]; * ``` * - * @param callable|string $callback the callback or column name to use for indexing + * @param callable|string $path The column name to use for indexing or callback that returns the value. * or a function returning the indexing key out of the provided element * @return self */ - public function indexBy($callback): CollectionInterface; + public function indexBy($path): CollectionInterface; /** * Sorts a list into groups and returns a count for the number of elements @@ -497,11 +500,11 @@ public function indexBy($callback): CollectionInterface; * ]; * ``` * - * @param callable|string $callback the callback or column name to use for indexing + * @param callable|string $path The column name to use for indexing or callback that returns the value. * or a function returning the indexing key out of the provided element * @return self */ - public function countBy($callback): CollectionInterface; + public function countBy($path): CollectionInterface; /** * Returns the total sum of all the values extracted with $matcher @@ -523,12 +526,12 @@ public function countBy($callback): CollectionInterface; * // Total: 6 * ``` * - * @param string|callable|null $matcher The property name to sum or a function + * @param string|callable|null $path The property name to sum or a function * If no value is passed, an identity function will be used. * that will return the value of the property to sum. * @return float|int */ - public function sumOf($matcher = null); + public function sumOf($path = null); /** * Returns a new collection with the elements placed in a random order, @@ -542,23 +545,23 @@ public function shuffle(): CollectionInterface; * Returns a new collection with maximum $size random elements * from this collection * - * @param int $size the maximum number of elements to randomly + * @param int $length the maximum number of elements to randomly * take from this collection * @return self */ - public function sample(int $size = 10): CollectionInterface; + public function sample(int $length = 10): CollectionInterface; /** * Returns a new collection with maximum $size elements in the internal * order this collection was created. If a second parameter is passed, it * will determine from what position to start taking elements. * - * @param int $size the maximum number of elements to take from + * @param int $length the maximum number of elements to take from * this collection - * @param int $from A positional offset from where to take the elements + * @param int $offset A positional offset from where to take the elements * @return self */ - public function take(int $size = 1, int $from = 0): CollectionInterface; + public function take(int $length = 1, int $offset = 0): CollectionInterface; /** * Returns the last N elements of a collection @@ -574,19 +577,19 @@ public function take(int $size = 1, int $from = 0): CollectionInterface; * [3, 4, 5]; * ``` * - * @param int $howMany The number of elements at the end of the collection + * @param int $length The number of elements at the end of the collection * @return self */ - public function takeLast(int $howMany): CollectionInterface; + public function takeLast(int $length): CollectionInterface; /** * Returns a new collection that will skip the specified amount of elements * at the beginning of the iteration. * - * @param int $howMany The number of elements to skip. + * @param int $length The number of elements to skip. * @return self */ - public function skip(int $howMany): CollectionInterface; + public function skip(int $length): CollectionInterface; /** * Looks through each value in the list, returning a Collection of all the @@ -789,7 +792,8 @@ public function toArray(bool $preserveKeys = true): array; public function toList(): array; /** - * Convert a result set into JSON. + * Returns the data that can be converted to JSON. This returns the same data + * as `toArray()` which contains only unique keys. * * Part of JsonSerializable interface. * @@ -886,12 +890,12 @@ public function buffered(): CollectionInterface; * $flattenedIds = $collection->listNested()->extract('id'); // Yields [1, 2, 3, 4, 5] * ``` * - * @param string|int $dir The direction in which to return the elements + * @param string|int $order The order in which to return the elements * @param string|callable $nestingKey The key name under which children are nested * or a callable function that will return the children list * @return self */ - public function listNested($dir = 'desc', $nestingKey = 'children'): CollectionInterface; + public function listNested($order = 'desc', $nestingKey = 'children'): CollectionInterface; /** * Creates a new collection that when iterated will stop yielding results if @@ -957,11 +961,11 @@ public function stopWhen($condition): CollectionInterface; * }); * ``` * - * @param callable|null $transformer A callable function that will receive each of + * @param callable|null $callback A callable function that will receive each of * the items in the collection and should return an array or Traversable object * @return self */ - public function unfold(?callable $transformer = null): CollectionInterface; + public function unfold(?callable $callback = null): CollectionInterface; /** * Passes this collection through a callable as its first argument. @@ -976,11 +980,11 @@ public function unfold(?callable $transformer = null): CollectionInterface; * }); * ``` * - * @param callable $handler A callable function that will receive + * @param callable $callback A callable function that will receive * this collection as first argument. * @return self */ - public function through(callable $handler): CollectionInterface; + public function through(callable $callback): CollectionInterface; /** * Combines the elements of this collection with each of the elements of the @@ -1015,10 +1019,10 @@ public function zip(iterable $items): CollectionInterface; * ``` * * @param iterable ...$items The collections to zip. - * @param callable $callable The function to use for zipping the elements together. + * @param callable $callback The function to use for zipping the elements together. * @return self */ - public function zipWith(iterable $items, $callable): CollectionInterface; + public function zipWith(iterable $items, $callback): CollectionInterface; /** * Breaks the collection into smaller arrays of the given size. @@ -1113,6 +1117,12 @@ public function transpose(): CollectionInterface; * * ## WARNINGS: * + * ### Will change the current position of the iterator: + * + * Calling this method at the same time that you are iterating this collections, for example in + * a foreach, will result in undefined behavior. Avoid doing this. + * + * * ### Consumes all elements for NoRewindIterator collections: * * On certain type of collections, calling this method may render unusable afterwards. @@ -1135,17 +1145,12 @@ public function transpose(): CollectionInterface; * If you need the count of elements after taking the keys in consideration * (the count of unique keys), you can call `countKeys()` * - * ### Will change the current position of the iterator: - * - * Calling this method at the same time that you are iterating this collections, for example in - * a foreach, will result in undefined behavior. Avoid doing this. - * * @return int */ public function count(): int; /** - * Returns the number of unique keys in this iterator. This is, the number of + * Returns the number of unique keys in this iterator. This is the same as the number of * elements the collection will contain after calling `toArray()` * * This method comes with a number of caveats. Please refer to `CollectionInterface::count()` diff --git a/app/vendor/cakephp/cakephp/src/Collection/CollectionTrait.php b/app/vendor/cakephp/cakephp/src/Collection/CollectionTrait.php index 1a7690c65..18ccbafef 100644 --- a/app/vendor/cakephp/cakephp/src/Collection/CollectionTrait.php +++ b/app/vendor/cakephp/cakephp/src/Collection/CollectionTrait.php @@ -63,10 +63,10 @@ protected function newCollection(...$args): CollectionInterface /** * @inheritDoc */ - public function each(callable $c) + public function each(callable $callback) { foreach ($this->optimizeUnwrap() as $k => $v) { - $c($v, $k); + $callback($v, $k); } return $this; @@ -75,34 +75,34 @@ public function each(callable $c) /** * @inheritDoc */ - public function filter(?callable $c = null): CollectionInterface + public function filter(?callable $callback = null): CollectionInterface { - if ($c === null) { - $c = function ($v) { + if ($callback === null) { + $callback = function ($v) { return (bool)$v; }; } - return new FilterIterator($this->unwrap(), $c); + return new FilterIterator($this->unwrap(), $callback); } /** * @inheritDoc */ - public function reject(callable $c): CollectionInterface + public function reject(callable $callback): CollectionInterface { - return new FilterIterator($this->unwrap(), function ($key, $value, $items) use ($c) { - return !$c($key, $value, $items); + return new FilterIterator($this->unwrap(), function ($key, $value, $items) use ($callback) { + return !$callback($key, $value, $items); }); } /** * @inheritDoc */ - public function every(callable $c): bool + public function every(callable $callback): bool { foreach ($this->optimizeUnwrap() as $key => $value) { - if (!$c($value, $key)) { + if (!$callback($value, $key)) { return false; } } @@ -113,10 +113,10 @@ public function every(callable $c): bool /** * @inheritDoc */ - public function some(callable $c): bool + public function some(callable $callback): bool { foreach ($this->optimizeUnwrap() as $key => $value) { - if ($c($value, $key) === true) { + if ($callback($value, $key) === true) { return true; } } @@ -141,29 +141,29 @@ public function contains($value): bool /** * @inheritDoc */ - public function map(callable $c): CollectionInterface + public function map(callable $callback): CollectionInterface { - return new ReplaceIterator($this->unwrap(), $c); + return new ReplaceIterator($this->unwrap(), $callback); } /** * @inheritDoc */ - public function reduce(callable $c, $zero = null) + public function reduce(callable $callback, $initial = null) { $isFirst = false; if (func_num_args() < 2) { $isFirst = true; } - $result = $zero; + $result = $initial; foreach ($this->optimizeUnwrap() as $k => $value) { if ($isFirst) { $result = $value; $isFirst = false; continue; } - $result = $c($result, $value, $k); + $result = $callback($result, $value, $k); } return $result; @@ -172,10 +172,10 @@ public function reduce(callable $c, $zero = null) /** * @inheritDoc */ - public function extract($matcher): CollectionInterface + public function extract($path): CollectionInterface { - $extractor = new ExtractIterator($this->unwrap(), $matcher); - if (is_string($matcher) && strpos($matcher, '{*}') !== false) { + $extractor = new ExtractIterator($this->unwrap(), $path); + if (is_string($path) && strpos($path, '{*}') !== false) { $extractor = $extractor ->filter(function ($data) { return $data !== null && ($data instanceof Traversable || is_array($data)); @@ -189,27 +189,27 @@ public function extract($matcher): CollectionInterface /** * @inheritDoc */ - public function max($callback, int $type = \SORT_NUMERIC) + public function max($path, int $sort = \SORT_NUMERIC) { - return (new SortIterator($this->unwrap(), $callback, \SORT_DESC, $type))->first(); + return (new SortIterator($this->unwrap(), $path, \SORT_DESC, $sort))->first(); } /** * @inheritDoc */ - public function min($callback, int $type = \SORT_NUMERIC) + public function min($path, int $sort = \SORT_NUMERIC) { - return (new SortIterator($this->unwrap(), $callback, \SORT_ASC, $type))->first(); + return (new SortIterator($this->unwrap(), $path, \SORT_ASC, $sort))->first(); } /** * @inheritDoc */ - public function avg($matcher = null) + public function avg($path = null) { $result = $this; - if ($matcher !== null) { - $result = $result->extract($matcher); + if ($path !== null) { + $result = $result->extract($path); } $result = $result ->reduce(function ($acc, $current) { @@ -228,13 +228,13 @@ public function avg($matcher = null) /** * @inheritDoc */ - public function median($matcher = null) + public function median($path = null) { - $elements = $this; - if ($matcher !== null) { - $elements = $elements->extract($matcher); + $items = $this; + if ($path !== null) { + $items = $items->extract($path); } - $values = $elements->toList(); + $values = $items->toList(); sort($values); $count = count($values); @@ -254,20 +254,27 @@ public function median($matcher = null) /** * @inheritDoc */ - public function sortBy($callback, int $dir = \SORT_DESC, int $type = \SORT_NUMERIC): CollectionInterface + public function sortBy($path, int $order = \SORT_DESC, int $sort = \SORT_NUMERIC): CollectionInterface { - return new SortIterator($this->unwrap(), $callback, $dir, $type); + return new SortIterator($this->unwrap(), $path, $order, $sort); } /** * @inheritDoc */ - public function groupBy($callback): CollectionInterface + public function groupBy($path): CollectionInterface { - $callback = $this->_propertyExtractor($callback); + $callback = $this->_propertyExtractor($path); $group = []; foreach ($this->optimizeUnwrap() as $value) { - $group[$callback($value)][] = $value; + $pathValue = $callback($value); + if ($pathValue === null) { + throw new InvalidArgumentException( + 'Cannot group by path that does not exist or contains a null value. ' . + 'Use a callback to return a default value for that path.' + ); + } + $group[$pathValue][] = $value; } return $this->newCollection($group); @@ -276,12 +283,19 @@ public function groupBy($callback): CollectionInterface /** * @inheritDoc */ - public function indexBy($callback): CollectionInterface + public function indexBy($path): CollectionInterface { - $callback = $this->_propertyExtractor($callback); + $callback = $this->_propertyExtractor($path); $group = []; foreach ($this->optimizeUnwrap() as $value) { - $group[$callback($value)] = $value; + $pathValue = $callback($value); + if ($pathValue === null) { + throw new InvalidArgumentException( + 'Cannot index by path that does not exist or contains a null value. ' . + 'Use a callback to return a default value for that path.' + ); + } + $group[$pathValue] = $value; } return $this->newCollection($group); @@ -290,9 +304,9 @@ public function indexBy($callback): CollectionInterface /** * @inheritDoc */ - public function countBy($callback): CollectionInterface + public function countBy($path): CollectionInterface { - $callback = $this->_propertyExtractor($callback); + $callback = $this->_propertyExtractor($path); $mapper = function ($value, $key, $mr) use ($callback): void { /** @var \Cake\Collection\Iterator\MapReduce $mr */ @@ -310,13 +324,13 @@ public function countBy($callback): CollectionInterface /** * @inheritDoc */ - public function sumOf($matcher = null) + public function sumOf($path = null) { - if ($matcher === null) { + if ($path === null) { return array_sum($this->toList()); } - $callback = $this->_propertyExtractor($matcher); + $callback = $this->_propertyExtractor($path); $sum = 0; foreach ($this->optimizeUnwrap() as $k => $v) { $sum += $callback($v, $k); @@ -330,34 +344,34 @@ public function sumOf($matcher = null) */ public function shuffle(): CollectionInterface { - $elements = $this->toArray(); - shuffle($elements); + $items = $this->toList(); + shuffle($items); - return $this->newCollection($elements); + return $this->newCollection($items); } /** * @inheritDoc */ - public function sample(int $size = 10): CollectionInterface + public function sample(int $length = 10): CollectionInterface { - return $this->newCollection(new LimitIterator($this->shuffle(), 0, $size)); + return $this->newCollection(new LimitIterator($this->shuffle(), 0, $length)); } /** * @inheritDoc */ - public function take(int $size = 1, int $from = 0): CollectionInterface + public function take(int $length = 1, int $offset = 0): CollectionInterface { - return $this->newCollection(new LimitIterator($this, $from, $size)); + return $this->newCollection(new LimitIterator($this, $offset, $length)); } /** * @inheritDoc */ - public function skip(int $howMany): CollectionInterface + public function skip(int $length): CollectionInterface { - return $this->newCollection(new LimitIterator($this, $howMany)); + return $this->newCollection(new LimitIterator($this, $length)); } /** @@ -417,15 +431,15 @@ public function last() /** * @inheritDoc */ - public function takeLast(int $howMany): CollectionInterface + public function takeLast(int $length): CollectionInterface { - if ($howMany < 1) { + if ($length < 1) { throw new InvalidArgumentException('The takeLast method requires a number greater than 0.'); } $iterator = $this->optimizeUnwrap(); if (is_array($iterator)) { - return $this->newCollection(array_slice($iterator, $howMany * -1)); + return $this->newCollection(array_slice($iterator, $length * -1)); } if ($iterator instanceof Countable) { @@ -435,12 +449,12 @@ public function takeLast(int $howMany): CollectionInterface return $this->newCollection([]); } - $iterator = new LimitIterator($iterator, max(0, $count - $howMany), $howMany); + $iterator = new LimitIterator($iterator, max(0, $count - $length), $length); return $this->newCollection($iterator); } - $generator = function ($iterator, $howMany) { + $generator = function ($iterator, $length) { $result = []; $bucket = 0; $offset = 0; @@ -493,11 +507,11 @@ public function takeLast(int $howMany): CollectionInterface foreach ($iterator as $k => $item) { $result[$bucket] = [$k, $item]; - $bucket = (++$bucket) % $howMany; + $bucket = (++$bucket) % $length; $offset++; } - $offset = $offset % $howMany; + $offset = $offset % $length; $head = array_slice($result, $offset); $tail = array_slice($result, 0, $offset); @@ -510,7 +524,7 @@ public function takeLast(int $howMany): CollectionInterface } }; - return $this->newCollection($generator($iterator, $howMany)); + return $this->newCollection($generator($iterator, $length)); } /** @@ -626,6 +640,7 @@ public function nest($idPath, $parentPath, string $nestingKey = 'children'): Col } if (empty($key) || !isset($parents[$key])) { foreach ($values as $id) { + /** @psalm-suppress PossiblyInvalidArgument */ $parents[$id] = $isObject ? $parents[$id] : new ArrayIterator($parents[$id], 1); $mapReduce->emit($parents[$id]); } @@ -724,28 +739,28 @@ public function buffered(): CollectionInterface /** * @inheritDoc */ - public function listNested($dir = 'desc', $nestingKey = 'children'): CollectionInterface + public function listNested($order = 'desc', $nestingKey = 'children'): CollectionInterface { - if (is_string($dir)) { - $dir = strtolower($dir); + if (is_string($order)) { + $order = strtolower($order); $modes = [ 'desc' => RecursiveIteratorIterator::SELF_FIRST, 'asc' => RecursiveIteratorIterator::CHILD_FIRST, 'leaves' => RecursiveIteratorIterator::LEAVES_ONLY, ]; - if (!isset($modes[$dir])) { + if (!isset($modes[$order])) { throw new RuntimeException(sprintf( "Invalid direction `%s` provided. Must be one of: 'desc', 'asc', 'leaves'", - $dir + $order )); } - $dir = $modes[$dir]; + $order = $modes[$order]; } return new TreeIterator( new NestIterator($this, $nestingKey), - $dir + $order ); } @@ -764,17 +779,17 @@ public function stopWhen($condition): CollectionInterface /** * @inheritDoc */ - public function unfold(?callable $transformer = null): CollectionInterface + public function unfold(?callable $callback = null): CollectionInterface { - if ($transformer === null) { - $transformer = function ($item) { + if ($callback === null) { + $callback = function ($item) { return $item; }; } return $this->newCollection( new RecursiveIteratorIterator( - new UnfoldIterator($this->unwrap(), $transformer), + new UnfoldIterator($this->unwrap(), $callback), RecursiveIteratorIterator::LEAVES_ONLY ) ); @@ -783,9 +798,9 @@ public function unfold(?callable $transformer = null): CollectionInterface /** * @inheritDoc */ - public function through(callable $handler): CollectionInterface + public function through(callable $callback): CollectionInterface { - $result = $handler($this); + $result = $callback($this); return $result instanceof CollectionInterface ? $result : $this->newCollection($result); } @@ -801,16 +816,16 @@ public function zip(iterable $items): CollectionInterface /** * @inheritDoc */ - public function zipWith(iterable $items, $callable): CollectionInterface + public function zipWith(iterable $items, $callback): CollectionInterface { if (func_num_args() > 2) { $items = func_get_args(); - $callable = array_pop($items); + $callback = array_pop($items); } else { $items = [$items]; } - return new ZipIterator(array_merge([$this->unwrap()], $items), $callable); + return new ZipIterator(array_merge([$this->unwrap()], $items), $callback); } /** @@ -939,10 +954,8 @@ public function cartesianProduct(?callable $operation = null, ?callable $filter $currentIndexes[$lastIndex]++; - // phpcs:ignore Squiz.ControlStructures.ForLoopDeclaration.SpacingAfterFirst for ( $changeIndex = $lastIndex; - // phpcs:ignore Squiz.ControlStructures.ForLoopDeclaration.SpacingAfterSecond $currentIndexes[$changeIndex] === $collectionArraysCounts[$changeIndex] && $changeIndex > 0; $changeIndex-- ) { diff --git a/app/vendor/cakephp/cakephp/src/Collection/ExtractTrait.php b/app/vendor/cakephp/cakephp/src/Collection/ExtractTrait.php index 2c6eb767c..81cc8d142 100644 --- a/app/vendor/cakephp/cakephp/src/Collection/ExtractTrait.php +++ b/app/vendor/cakephp/cakephp/src/Collection/ExtractTrait.php @@ -29,27 +29,27 @@ trait ExtractTrait * Returns a callable that can be used to extract a property or column from * an array or object based on a dot separated path. * - * @param string|callable $callback A dot separated path of column to follow + * @param string|callable $path A dot separated path of column to follow * so that the final one can be returned or a callable that will take care * of doing that. * @return callable */ - protected function _propertyExtractor($callback): callable + protected function _propertyExtractor($path): callable { - if (!is_string($callback)) { - return $callback; + if (!is_string($path)) { + return $path; } - $path = explode('.', $callback); + $parts = explode('.', $path); - if (strpos($callback, '{*}') !== false) { - return function ($element) use ($path) { - return $this->_extract($element, $path); + if (strpos($path, '{*}') !== false) { + return function ($element) use ($parts) { + return $this->_extract($element, $parts); }; } - return function ($element) use ($path) { - return $this->_simpleExtract($element, $path); + return function ($element) use ($parts) { + return $this->_simpleExtract($element, $parts); }; } @@ -59,15 +59,15 @@ protected function _propertyExtractor($callback): callable * It will return arrays for elements in represented with `{*}` * * @param array|\ArrayAccess $data Data. - * @param string[] $path Path to extract from. + * @param string[] $parts Path to extract from. * @return mixed */ - protected function _extract($data, array $path) + protected function _extract($data, array $parts) { $value = null; $collectionTransform = false; - foreach ($path as $i => $column) { + foreach ($parts as $i => $column) { if ($column === '{*}') { $collectionTransform = true; continue; @@ -84,7 +84,7 @@ protected function _extract($data, array $path) } if ($collectionTransform) { - $rest = implode('.', array_slice($path, $i)); + $rest = implode('.', array_slice($parts, $i)); return (new Collection($data))->extract($rest); } @@ -105,13 +105,13 @@ protected function _extract($data, array $path) * by iterating over the column names contained in $path * * @param array|\ArrayAccess $data Data. - * @param string[] $path Path to extract from. + * @param string[] $parts Path to extract from. * @return mixed */ - protected function _simpleExtract($data, array $path) + protected function _simpleExtract($data, array $parts) { $value = null; - foreach ($path as $column) { + foreach ($parts as $column) { if (!isset($data[$column])) { return null; } diff --git a/app/vendor/cakephp/cakephp/src/Collection/Iterator/BufferedIterator.php b/app/vendor/cakephp/cakephp/src/Collection/Iterator/BufferedIterator.php index fc1590621..5c46c9198 100644 --- a/app/vendor/cakephp/cakephp/src/Collection/Iterator/BufferedIterator.php +++ b/app/vendor/cakephp/cakephp/src/Collection/Iterator/BufferedIterator.php @@ -159,6 +159,10 @@ public function next(): void { $this->_index++; + // Don't move inner iterator if we have more buffer + if ($this->_buffer->offsetExists($this->_index)) { + return; + } if (!$this->_finished) { parent::next(); } @@ -200,13 +204,13 @@ public function serialize(): string /** * Unserializes the passed string and rebuilds the BufferedIterator instance * - * @param string $buffer The serialized buffer iterator + * @param string $collection The serialized buffer iterator * @return void */ - public function unserialize($buffer): void + public function unserialize($collection): void { $this->__construct([]); - $this->_buffer = unserialize($buffer); + $this->_buffer = unserialize($collection); $this->_started = true; $this->_finished = true; } diff --git a/app/vendor/cakephp/cakephp/src/Collection/Iterator/ZipIterator.php b/app/vendor/cakephp/cakephp/src/Collection/Iterator/ZipIterator.php index 4a550b7bc..3ab17e834 100644 --- a/app/vendor/cakephp/cakephp/src/Collection/Iterator/ZipIterator.php +++ b/app/vendor/cakephp/cakephp/src/Collection/Iterator/ZipIterator.php @@ -88,7 +88,7 @@ public function __construct(array $sets, ?callable $callable = null) * Returns the value resulting out of zipping all the elements for all the * iterators with the same positional index. * - * @return array|false + * @return array */ public function current() { diff --git a/app/vendor/cakephp/cakephp/src/Command/I18nExtractCommand.php b/app/vendor/cakephp/cakephp/src/Command/I18nExtractCommand.php index 590401423..a90815bd1 100644 --- a/app/vendor/cakephp/cakephp/src/Command/I18nExtractCommand.php +++ b/app/vendor/cakephp/cakephp/src/Command/I18nExtractCommand.php @@ -207,7 +207,7 @@ public function execute(Arguments $args, ConsoleIo $io): ?int ['y', 'n'], 'n' ); - $this->_extractCore = strtolower((string)$response) === 'y'; + $this->_extractCore = strtolower($response) === 'y'; } if ($args->hasOption('exclude-plugins') && $this->_isExtractingApp()) { @@ -263,7 +263,7 @@ public function execute(Arguments $args, ConsoleIo $io): ?int ['y', 'n'], 'n' ); - $this->_merge = strtolower((string)$response) === 'y'; + $this->_merge = strtolower($response) === 'y'; } $this->_markerError = (bool)$args->getOption('marker-error'); @@ -504,7 +504,8 @@ protected function _parse(ConsoleIo $io, string $functionName, array $map): void if ($mapCount === count($strings)) { $singular = ''; $plural = $context = null; - extract(array_combine($map, $strings)); + $vars = array_combine($map, $strings); + extract($vars); $domain = $domain ?? 'default'; $details = [ 'file' => $this->_file, diff --git a/app/vendor/cakephp/cakephp/src/Command/PluginAssetsTrait.php b/app/vendor/cakephp/cakephp/src/Command/PluginAssetsTrait.php index 4770fe718..9470ff939 100644 --- a/app/vendor/cakephp/cakephp/src/Command/PluginAssetsTrait.php +++ b/app/vendor/cakephp/cakephp/src/Command/PluginAssetsTrait.php @@ -180,7 +180,8 @@ protected function _remove(array $config): bool if (is_link($dest)) { // phpcs:ignore - if (@unlink($dest)) { + $success = DS === '\\' ? @rmdir($dest) : @unlink($dest); + if ($success) { $this->io->out('Unlinked ' . $dest); return true; diff --git a/app/vendor/cakephp/cakephp/src/Command/PluginLoadCommand.php b/app/vendor/cakephp/cakephp/src/Command/PluginLoadCommand.php index e371436da..4897ccae0 100644 --- a/app/vendor/cakephp/cakephp/src/Command/PluginLoadCommand.php +++ b/app/vendor/cakephp/cakephp/src/Command/PluginLoadCommand.php @@ -19,6 +19,8 @@ use Cake\Console\Arguments; use Cake\Console\ConsoleIo; use Cake\Console\ConsoleOptionParser; +use Cake\Core\Exception\MissingPluginException; +use Cake\Core\Plugin; /** * Command for loading plugins. @@ -69,6 +71,15 @@ public function execute(Arguments $args, ConsoleIo $io): ?int return static::CODE_ERROR; } + try { + Plugin::getCollection()->findPath($plugin); + } catch (MissingPluginException $e) { + $this->io->err($e->getMessage()); + $this->io->err('Ensure you have the correct spelling and casing.'); + + return static::CODE_ERROR; + } + $app = APP . 'Application.php'; if (file_exists($app)) { $this->modifyApplication($app, $plugin); diff --git a/app/vendor/cakephp/cakephp/src/Command/RoutesCheckCommand.php b/app/vendor/cakephp/cakephp/src/Command/RoutesCheckCommand.php index b3f396b27..b660e4d68 100644 --- a/app/vendor/cakephp/cakephp/src/Command/RoutesCheckCommand.php +++ b/app/vendor/cakephp/cakephp/src/Command/RoutesCheckCommand.php @@ -19,6 +19,7 @@ use Cake\Console\Arguments; use Cake\Console\ConsoleIo; use Cake\Console\ConsoleOptionParser; +use Cake\Http\Exception\RedirectException; use Cake\Http\ServerRequest; use Cake\Routing\Exception\MissingRouteException; use Cake\Routing\Router; @@ -66,6 +67,13 @@ public function execute(Arguments $args, ConsoleIo $io): ?int ]; $io->helper('table')->output($output); $io->out(); + } catch (RedirectException $e) { + $output = [ + ['URI template', 'Redirect'], + [$url, $e->getMessage()], + ]; + $io->helper('table')->output($output); + $io->out(); } catch (MissingRouteException $e) { $io->warning("'$url' did not match any routes."); $io->out(); diff --git a/app/vendor/cakephp/cakephp/src/Command/ServerCommand.php b/app/vendor/cakephp/cakephp/src/Command/ServerCommand.php index f0b53c4d5..fb32a3ec7 100644 --- a/app/vendor/cakephp/cakephp/src/Command/ServerCommand.php +++ b/app/vendor/cakephp/cakephp/src/Command/ServerCommand.php @@ -126,8 +126,10 @@ protected function startup(Arguments $args, ConsoleIo $io): void public function execute(Arguments $args, ConsoleIo $io): ?int { $this->startup($args, $io); + $phpBinary = (string)env('PHP', 'php'); $command = sprintf( - 'php -S %s:%d -t %s', + '%s -S %s:%d -t %s', + $phpBinary, $this->_host, $this->_port, escapeshellarg($this->_documentRoot) diff --git a/app/vendor/cakephp/cakephp/src/Console/BaseCommand.php b/app/vendor/cakephp/cakephp/src/Console/BaseCommand.php index 2ef795153..18b5ad38a 100644 --- a/app/vendor/cakephp/cakephp/src/Console/BaseCommand.php +++ b/app/vendor/cakephp/cakephp/src/Console/BaseCommand.php @@ -172,6 +172,10 @@ public function run(array $argv, ConsoleIo $io): ?int return static::CODE_SUCCESS; } + if ($args->getOption('quiet')) { + $io->setInteractive(false); + } + return $this->execute($args, $io); } @@ -238,6 +242,10 @@ public function abort(int $code = self::CODE_ERROR): void /** * Execute another command with the provided set of arguments. * + * If you are using a string command name, that command's dependencies + * will not be resolved with the application container. Instead you will + * need to pass the command as an object with all of its dependencies. + * * @param string|\Cake\Console\CommandInterface $command The command class name or command instance. * @param array $args The arguments to invoke the command with. * @param \Cake\Console\ConsoleIo $io The ConsoleIo instance to use for the executed command. diff --git a/app/vendor/cakephp/cakephp/src/Console/CommandCollection.php b/app/vendor/cakephp/cakephp/src/Console/CommandCollection.php index 11a6048b3..e65dcc039 100644 --- a/app/vendor/cakephp/cakephp/src/Console/CommandCollection.php +++ b/app/vendor/cakephp/cakephp/src/Console/CommandCollection.php @@ -25,7 +25,7 @@ /** * Collection for Commands. * - * Used by Applications to whitelist their console commands. + * Used by Applications to specify their console commands. * CakePHP will use the mapped commands to construct and dispatch * shell commands. */ @@ -36,6 +36,7 @@ class CommandCollection implements IteratorAggregate, Countable * * @var array * @psalm-var (\Cake\Console\Shell|\Cake\Console\CommandInterface|class-string)[] + * @psalm-suppress DeprecatedClass */ protected $commands = []; @@ -66,7 +67,7 @@ public function add(string $name, $command) $class = is_string($command) ? $command : get_class($command); throw new InvalidArgumentException(sprintf( "Cannot use '%s' for command '%s'. " . - "It is not a subclass of Cake\Console\Shell or Cake\Command\CommandInterface.", + "It is not a subclass of Cake\Console\Shell or Cake\Command\Command.", $class, $name )); @@ -219,7 +220,7 @@ protected function resolveNames(array $input): array * - CakePHP provided commands * - Application commands * - * Commands defined in the application will ovewrite commands with + * Commands defined in the application will overwrite commands with * the same name provided by CakePHP. * * @return string[] An array of command names and their classes. diff --git a/app/vendor/cakephp/cakephp/src/Console/CommandFactory.php b/app/vendor/cakephp/cakephp/src/Console/CommandFactory.php index 730dfbc3c..20c8367a2 100644 --- a/app/vendor/cakephp/cakephp/src/Console/CommandFactory.php +++ b/app/vendor/cakephp/cakephp/src/Console/CommandFactory.php @@ -14,6 +14,7 @@ */ namespace Cake\Console; +use Cake\Core\ContainerInterface; use InvalidArgumentException; /** @@ -24,12 +25,32 @@ */ class CommandFactory implements CommandFactoryInterface { + /** + * @var \Cake\Core\ContainerInterface|null + */ + protected $container; + + /** + * Constructor + * + * @param \Cake\Core\ContainerInterface|null $container The container to use if available. + */ + public function __construct(?ContainerInterface $container = null) + { + $this->container = $container; + } + /** * @inheritDoc */ public function create(string $className) { - $command = new $className(); + if ($this->container && $this->container->has($className)) { + $command = $this->container->get($className); + } else { + $command = new $className(); + } + if (!($command instanceof CommandInterface) && !($command instanceof Shell)) { /** @psalm-suppress DeprecatedClass */ $valid = implode('` or `', [Shell::class, CommandInterface::class]); diff --git a/app/vendor/cakephp/cakephp/src/Console/CommandRunner.php b/app/vendor/cakephp/cakephp/src/Console/CommandRunner.php index 904adc0a4..1892eff42 100644 --- a/app/vendor/cakephp/cakephp/src/Console/CommandRunner.php +++ b/app/vendor/cakephp/cakephp/src/Console/CommandRunner.php @@ -21,6 +21,7 @@ use Cake\Console\Exception\MissingOptionException; use Cake\Console\Exception\StopException; use Cake\Core\ConsoleApplicationInterface; +use Cake\Core\ContainerApplicationInterface; use Cake\Core\PluginApplicationInterface; use Cake\Event\EventDispatcherInterface; use Cake\Event\EventDispatcherTrait; @@ -49,7 +50,7 @@ class CommandRunner implements EventDispatcherInterface /** * The application console commands are being run for. * - * @var \Cake\Console\CommandFactoryInterface + * @var \Cake\Console\CommandFactoryInterface|null */ protected $factory; @@ -81,7 +82,7 @@ public function __construct( ) { $this->app = $app; $this->root = $root; - $this->factory = $factory ?: new CommandFactory(); + $this->factory = $factory; $this->aliases = [ '--version' => 'version', '--help' => 'help', @@ -184,9 +185,8 @@ public function run(array $argv, ?ConsoleIo $io = null): int /** * Application bootstrap wrapper. * - * Calls `bootstrap()` and `events()` if application implements `EventApplicationInterface`. - * After the application is bootstrapped and events are attached, plugins are bootstrapped - * and have their events attached. + * Calls the application's `bootstrap()` hook. After the application the + * plugins are bootstrapped. * * @return void */ @@ -218,14 +218,14 @@ public function getEventManager(): EventManagerInterface * If the application does not support events and this method is used as * a setter, an exception will be raised. * - * @param \Cake\Event\EventManagerInterface $events The event manager to set. + * @param \Cake\Event\EventManagerInterface $eventManager The event manager to set. * @return $this * @throws \InvalidArgumentException */ - public function setEventManager(EventManagerInterface $events) + public function setEventManager(EventManagerInterface $eventManager) { if ($this->app instanceof PluginApplicationInterface) { - $this->app->setEventManager($events); + $this->app->setEventManager($eventManager); return $this; } @@ -353,9 +353,7 @@ protected function runShell(Shell $shell, array $argv) return $shell->runCommand($argv, true); } catch (StopException $e) { - $code = $e->getCode(); - - return $code === null ? $code : (int)$code; + return $e->getCode(); } } @@ -368,6 +366,14 @@ protected function runShell(Shell $shell, array $argv) */ protected function createCommand(string $className, ConsoleIo $io) { + if (!$this->factory) { + $container = null; + if ($this->app instanceof ContainerApplicationInterface) { + $container = $this->app->getContainer(); + } + $this->factory = new CommandFactory($container); + } + $shell = $this->factory->create($className); if ($shell instanceof Shell) { $shell->setIo($io); diff --git a/app/vendor/cakephp/cakephp/src/Console/ConsoleInput.php b/app/vendor/cakephp/cakephp/src/Console/ConsoleInput.php index dae71feb9..9e4ef7a6a 100644 --- a/app/vendor/cakephp/cakephp/src/Console/ConsoleInput.php +++ b/app/vendor/cakephp/cakephp/src/Console/ConsoleInput.php @@ -60,7 +60,6 @@ public function __construct(string $handle = 'php://stdin') public function read(): ?string { if ($this->_canReadline) { - /** @var string|false $line */ $line = readline(''); if ($line !== false && strlen($line) > 0) { @@ -93,6 +92,8 @@ public function dataAvailable(int $timeout = 0): bool $error = null; set_error_handler(function (int $code, string $message) use (&$error) { $error = "stream_select failed with code={$code} message={$message}."; + + return true; }); $readyFds = stream_select($readFds, $writeFds, $errorFds, $timeout); restore_error_handler(); diff --git a/app/vendor/cakephp/cakephp/src/Console/ConsoleInputArgument.php b/app/vendor/cakephp/cakephp/src/Console/ConsoleInputArgument.php index 4b0034434..e76cfccc1 100644 --- a/app/vendor/cakephp/cakephp/src/Console/ConsoleInputArgument.php +++ b/app/vendor/cakephp/cakephp/src/Console/ConsoleInputArgument.php @@ -148,7 +148,7 @@ public function usage(): string */ public function isRequired(): bool { - return (bool)$this->_required; + return $this->_required; } /** diff --git a/app/vendor/cakephp/cakephp/src/Console/ConsoleInputOption.php b/app/vendor/cakephp/cakephp/src/Console/ConsoleInputOption.php index db108b815..fe29594eb 100644 --- a/app/vendor/cakephp/cakephp/src/Console/ConsoleInputOption.php +++ b/app/vendor/cakephp/cakephp/src/Console/ConsoleInputOption.php @@ -76,6 +76,13 @@ class ConsoleInputOption */ protected $_choices; + /** + * Is the option required. + * + * @var bool + */ + protected $required; + /** * Make a new Input Option * @@ -86,6 +93,7 @@ class ConsoleInputOption * @param string|bool|null $default The default value for this option. * @param string[] $choices Valid choices for this option. * @param bool $multiple Whether this option can accept multiple value definition. + * @param bool $required Whether this option is required or not. * @throws \Cake\Console\Exception\ConsoleException */ public function __construct( @@ -95,7 +103,8 @@ public function __construct( bool $isBoolean = false, $default = null, array $choices = [], - bool $multiple = false + bool $multiple = false, + bool $required = false ) { $this->_name = $name; $this->_short = $short; @@ -103,6 +112,7 @@ public function __construct( $this->_boolean = $isBoolean; $this->_choices = $choices; $this->_multiple = $multiple; + $this->required = $required; if ($isBoolean) { $this->_default = (bool)$default; @@ -134,7 +144,7 @@ public function name(): string */ public function short(): string { - return (string)$this->_short; + return $this->_short; } /** @@ -159,8 +169,12 @@ public function help(int $width = 0): string if (strlen($name) < $width) { $name = str_pad($name, $width, ' '); } + $required = ''; + if ($this->isRequired()) { + $required = ' (required)'; + } - return sprintf('%s%s%s', $name, $this->_help, $default); + return sprintf('%s%s%s%s', $name, $this->_help, $default, $required); } /** @@ -178,8 +192,12 @@ public function usage(): string if ($this->_choices) { $default = ' ' . implode('|', $this->_choices); } + $template = '[%s%s]'; + if ($this->isRequired()) { + $template = '%s%s'; + } - return sprintf('[%s%s]', $name, $default); + return sprintf($template, $name, $default); } /** @@ -192,6 +210,16 @@ public function defaultValue() return $this->_default; } + /** + * Check if this option is required + * + * @return bool + */ + public function isRequired(): bool + { + return $this->required; + } + /** * Check if this option is a boolean option * @@ -239,7 +267,7 @@ public function validChoice($value): bool } /** - * Append the option's xml into the parent. + * Append the option's XML into the parent. * * @param \SimpleXMLElement $parent The parent element. * @return \SimpleXMLElement The parent with this option appended. @@ -261,6 +289,7 @@ public function xml(SimpleXMLElement $parent): SimpleXMLElement $option->addAttribute('short', $short); $option->addAttribute('help', $this->_help); $option->addAttribute('boolean', (string)(int)$this->_boolean); + $option->addAttribute('required', (string)(int)$this->required); $option->addChild('default', (string)$default); $choices = $option->addChild('choices'); foreach ($this->_choices as $valid) { diff --git a/app/vendor/cakephp/cakephp/src/Console/ConsoleIo.php b/app/vendor/cakephp/cakephp/src/Console/ConsoleIo.php index 9fc816ad8..a0141c7bb 100644 --- a/app/vendor/cakephp/cakephp/src/Console/ConsoleIo.php +++ b/app/vendor/cakephp/cakephp/src/Console/ConsoleIo.php @@ -32,53 +32,53 @@ class ConsoleIo { /** - * The output stream + * Output constant making verbose shells. * - * @var \Cake\Console\ConsoleOutput + * @var int */ - protected $_out; + public const VERBOSE = 2; /** - * The error stream + * Output constant for making normal shells. * - * @var \Cake\Console\ConsoleOutput + * @var int */ - protected $_err; + public const NORMAL = 1; /** - * The input stream + * Output constants for making quiet shells. * - * @var \Cake\Console\ConsoleInput + * @var int */ - protected $_in; + public const QUIET = 0; /** - * The helper registry. + * The output stream * - * @var \Cake\Console\HelperRegistry + * @var \Cake\Console\ConsoleOutput */ - protected $_helpers; + protected $_out; /** - * Output constant making verbose shells. + * The error stream * - * @var int + * @var \Cake\Console\ConsoleOutput */ - public const VERBOSE = 2; + protected $_err; /** - * Output constant for making normal shells. + * The input stream * - * @var int + * @var \Cake\Console\ConsoleInput */ - public const NORMAL = 1; + protected $_in; /** - * Output constants for making quiet shells. + * The helper registry. * - * @var int + * @var \Cake\Console\HelperRegistry */ - public const QUIET = 0; + protected $_helpers; /** * The current output level. @@ -102,6 +102,11 @@ class ConsoleIo */ protected $forceOverwrite = false; + /** + * @var bool + */ + protected $interactive = true; + /** * Constructor * @@ -123,6 +128,15 @@ public function __construct( $this->_helpers->setIo($this); } + /** + * @param bool $value Value + * @return void + */ + public function setInteractive(bool $value): void + { + $this->interactive = $value; + } + /** * Get/set the current output level. * @@ -184,7 +198,7 @@ public function quiet($message, int $newlines = 1): ?int public function out($message = '', int $newlines = 1, int $level = self::NORMAL): ?int { if ($level <= $this->_level) { - $this->_lastWritten = (int)$this->_out->write($message, $newlines); + $this->_lastWritten = $this->_out->write($message, $newlines); return $this->_lastWritten; } @@ -210,6 +224,24 @@ public function info($message, int $newlines = 1, int $level = self::NORMAL): ?i return $this->out($message, $newlines, $level); } + /** + * Convenience method for out() that wraps message between tag + * + * @param string|string[] $message A string or an array of strings to output + * @param int $newlines Number of newlines to append + * @param int $level The message's output level, see above. + * @return int|null The number of bytes returned from writing to stdout + * or null if provided $level is greater than current level. + * @see https://book.cakephp.org/4/en/console-and-shells.html#ConsoleIo::out + */ + public function comment($message, int $newlines = 1, int $level = self::NORMAL): ?int + { + $messageType = 'comment'; + $message = $this->wrapMessageWithType($messageType, $message); + + return $this->out($message, $newlines, $level); + } + /** * Convenience method for err() that wraps message between tag * @@ -477,6 +509,10 @@ public function askChoice(string $prompt, $options, ?string $default = null): st */ protected function _getInput(string $prompt, ?string $options, ?string $default): string { + if (!$this->interactive) { + return (string)$default; + } + $optionsText = ''; if (isset($options)) { $optionsText = " $options "; diff --git a/app/vendor/cakephp/cakephp/src/Console/ConsoleOptionParser.php b/app/vendor/cakephp/cakephp/src/Console/ConsoleOptionParser.php index 3f656a90e..1951d2bdb 100644 --- a/app/vendor/cakephp/cakephp/src/Console/ConsoleOptionParser.php +++ b/app/vendor/cakephp/cakephp/src/Console/ConsoleOptionParser.php @@ -373,7 +373,7 @@ public function getEpilog(): string */ public function enableSubcommandSort(bool $value = true) { - $this->_subcommandSort = (bool)$value; + $this->_subcommandSort = $value; return $this; } @@ -425,6 +425,7 @@ public function addOption($name, array $options = []) 'boolean' => false, 'multiple' => false, 'choices' => [], + 'required' => false, ]; $options += $defaults; $option = new ConsoleInputOption( @@ -434,7 +435,8 @@ public function addOption($name, array $options = []) $options['boolean'], $options['default'], $options['choices'], - $options['multiple'] + $options['multiple'], + $options['required'] ); } $this->_options[$name] = $option; @@ -685,6 +687,7 @@ public function parse(array $argv): array array_shift($argv); } if (isset($this->_subcommands[$command]) && $this->_subcommands[$command]->parser()) { + /** @psalm-suppress PossiblyNullReference */ return $this->_subcommands[$command]->parser()->parse($argv); } $params = $args = []; @@ -705,7 +708,7 @@ public function parse(array $argv): array foreach ($this->_args as $i => $arg) { if ($arg->isRequired() && !isset($args[$i]) && empty($params['help'])) { throw new ConsoleException( - sprintf('Missing required arguments. The `%s` argument is required.', $arg->name()) + sprintf('Missing required argument. The `%s` argument is required.', $arg->name()) ); } } @@ -720,6 +723,11 @@ public function parse(array $argv): array if ($isBoolean && !isset($params[$name])) { $params[$name] = false; } + if ($option->isRequired() && !isset($params[$name])) { + throw new ConsoleException( + sprintf('Missing required option. The `%s` option is required and has no default value.', $name) + ); + } } return [$params, $args]; @@ -733,7 +741,7 @@ public function parse(array $argv): array * * @param string|null $subcommand If present and a valid subcommand that has a linked parser. * That subcommands help will be shown instead. - * @param string $format Define the output format, can be text or xml + * @param string $format Define the output format, can be text or XML * @param int $width The width to format user content to. Defaults to 72 * @return string Generated help. */ @@ -785,7 +793,7 @@ public function help(?string $subcommand = null, string $format = 'text', int $w throw new MissingOptionException( $message, $subcommand, - array_keys((array)$this->subcommands()) + array_keys($this->subcommands()) ); } @@ -797,7 +805,7 @@ public function help(?string $subcommand = null, string $format = 'text', int $w */ public function setRootName(string $name) { - $this->rootName = (string)$name; + $this->rootName = $name; return $this; } diff --git a/app/vendor/cakephp/cakephp/src/Console/ConsoleOutput.php b/app/vendor/cakephp/cakephp/src/Console/ConsoleOutput.php index a86d102af..e009583f6 100644 --- a/app/vendor/cakephp/cakephp/src/Console/ConsoleOutput.php +++ b/app/vendor/cakephp/cakephp/src/Console/ConsoleOutput.php @@ -169,12 +169,17 @@ public function __construct(string $stream = 'php://stdout') if ( ( DIRECTORY_SEPARATOR === '\\' && + strpos(strtolower(php_uname('v')), 'windows 10') === false && + strpos(strtolower((string)env('SHELL')), 'bash.exe') === false && !(bool)env('ANSICON') && env('ConEmuANSI') !== 'ON' ) || ( function_exists('posix_isatty') && !posix_isatty($this->_output) + ) || + ( + env('NO_COLOR') !== null ) ) { $this->_outputAs = self::PLAIN; diff --git a/app/vendor/cakephp/cakephp/src/Console/Exception/ConsoleException.php b/app/vendor/cakephp/cakephp/src/Console/Exception/ConsoleException.php index 988197093..28be512ec 100644 --- a/app/vendor/cakephp/cakephp/src/Console/Exception/ConsoleException.php +++ b/app/vendor/cakephp/cakephp/src/Console/Exception/ConsoleException.php @@ -16,13 +16,13 @@ namespace Cake\Console\Exception; use Cake\Console\CommandInterface; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Exception class for Console libraries. This exception will be thrown from Console library * classes when they encounter an error. */ -class ConsoleException extends Exception +class ConsoleException extends CakeException { /** * Default exception code diff --git a/app/vendor/cakephp/cakephp/src/Console/HelpFormatter.php b/app/vendor/cakephp/cakephp/src/Console/HelpFormatter.php index 55c7c380e..275972b49 100644 --- a/app/vendor/cakephp/cakephp/src/Console/HelpFormatter.php +++ b/app/vendor/cakephp/cakephp/src/Console/HelpFormatter.php @@ -206,7 +206,7 @@ protected function _getMaxLength(array $collection): int } /** - * Get the help as an xml string. + * Get the help as an XML string. * * @param bool $string Return the SimpleXml object or a string. Defaults to true. * @return string|\SimpleXMLElement See $string diff --git a/app/vendor/cakephp/cakephp/src/Console/HelperRegistry.php b/app/vendor/cakephp/cakephp/src/Console/HelperRegistry.php index 9c791ddb0..900bbc317 100644 --- a/app/vendor/cakephp/cakephp/src/Console/HelperRegistry.php +++ b/app/vendor/cakephp/cakephp/src/Console/HelperRegistry.php @@ -94,14 +94,13 @@ protected function _throwMissingClassError(string $class, ?string $plugin): void * * @param string $class The classname to create. * @param string $alias The alias of the helper. - * @param array $settings An array of settings to use for the helper. + * @param array $config An array of settings to use for the helper. * @return \Cake\Console\Helper The constructed helper class. * @psalm-suppress MoreSpecificImplementedParamType */ - protected function _create($class, string $alias, array $settings): Helper + protected function _create($class, string $alias, array $config): Helper { - // phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.InvalidFormat /** @var \Cake\Console\Helper */ - return new $class($this->_io, $settings); + return new $class($this->_io, $config); } } diff --git a/app/vendor/cakephp/cakephp/src/Console/LICENSE.txt b/app/vendor/cakephp/cakephp/src/Console/LICENSE.txt new file mode 100644 index 000000000..b938c9e8e --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Console/LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License (MIT) + +CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) +Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/app/vendor/cakephp/cakephp/src/Console/README.md b/app/vendor/cakephp/cakephp/src/Console/README.md index 4aef2b7d1..f2fd21504 100644 --- a/app/vendor/cakephp/cakephp/src/Console/README.md +++ b/app/vendor/cakephp/cakephp/src/Console/README.md @@ -7,9 +7,17 @@ This library provides a framework for building command line applications from a set of commands. It provides abstractions for defining option and argument parsers, and dispatching commands. +# installation + +You can install it from Composer. In your project: + +``` +composer require cakephp/console +``` + # Getting Started -To start, define an an entry point script and Application class that defines +To start, define an entry point script and Application class which defines bootstrap logic, and binds your commands. Lets put our entrypoint script in `bin/tool.php`: @@ -35,6 +43,7 @@ namespace App; use App\Command\HelloCommand; use Cake\Core\ConsoleApplicationInterface; +use Cake\Console\CommandCollection; class Application implements ConsoleApplicationInterface { diff --git a/app/vendor/cakephp/cakephp/src/Console/Shell.php b/app/vendor/cakephp/cakephp/src/Console/Shell.php index 4e5378f1a..f196e36c5 100644 --- a/app/vendor/cakephp/cakephp/src/Console/Shell.php +++ b/app/vendor/cakephp/cakephp/src/Console/Shell.php @@ -19,7 +19,7 @@ use Cake\Console\Exception\ConsoleException; use Cake\Console\Exception\StopException; use Cake\Core\App; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Datasource\ModelAwareTrait; use Cake\Filesystem\Filesystem; use Cake\Log\LogTrait; @@ -284,10 +284,10 @@ protected function _welcome(): void */ public function loadTasks(): bool { - if ($this->tasks === true || empty($this->tasks) || empty($this->Tasks)) { + if ($this->tasks === true || empty($this->tasks)) { return true; } - $this->_taskMap = $this->Tasks->normalizeArray((array)$this->tasks); + $this->_taskMap = $this->Tasks->normalizeArray($this->tasks); $this->taskNames = array_merge($this->taskNames, array_keys($this->_taskMap)); $this->_validateTasks(); @@ -387,7 +387,7 @@ public function hasMethod(string $name): bool * 'extra' => ['param' => 'value'] * ]);` * - * @return int The cli command exit code. 0 is success. + * @return int The CLI command exit code. 0 is success. * @link https://book.cakephp.org/4/en/console-and-shells.html#invoking-other-shells-from-your-shell */ public function dispatchShell(): int @@ -874,7 +874,7 @@ public function createFile(string $path, string $contents): bool $fs->dumpFile($path, $contents); $this->_io->out(sprintf('Wrote `%s`', $path)); - } catch (Exception $e) { + } catch (CakeException $e) { $this->_io->err(sprintf('Could not write to `%s`.', $path), 2); return false; diff --git a/app/vendor/cakephp/cakephp/src/Console/ShellDispatcher.php b/app/vendor/cakephp/cakephp/src/Console/ShellDispatcher.php index 529994b15..482b4800c 100644 --- a/app/vendor/cakephp/cakephp/src/Console/ShellDispatcher.php +++ b/app/vendor/cakephp/cakephp/src/Console/ShellDispatcher.php @@ -26,7 +26,7 @@ use Cake\Utility\Inflector; /** - * Shell dispatcher handles dispatching cli commands. + * Shell dispatcher handles dispatching CLI commands. * * Consult /bin/cake.php for how this class is used in practice. * @@ -60,7 +60,7 @@ class ShellDispatcher public function __construct(array $args = [], bool $bootstrap = true) { set_time_limit(0); - $this->args = (array)$args; + $this->args = $args; $this->addShortPluginAliases(); @@ -134,7 +134,7 @@ public static function run(array $argv, array $extra = []): int * Defines current working environment. * * @return void - * @throws \Cake\Core\Exception\Exception + * @throws \Cake\Core\Exception\CakeException */ protected function _initEnvironment(): void { @@ -172,7 +172,7 @@ protected function _bootstrap() * Built-in extra parameter is : * * - `requested` : if used, will prevent the Shell welcome message to be displayed - * @return int The cli command exit code. 0 is success. + * @return int The CLI command exit code. 0 is success. */ public function dispatch(array $extra = []): int { @@ -181,7 +181,7 @@ public function dispatch(array $extra = []): int } catch (StopException $e) { $code = $e->getCode(); - return (int)$code; + return $code; } if ($result === null || $result === true) { /** @psalm-suppress DeprecatedClass */ diff --git a/app/vendor/cakephp/cakephp/src/Console/TaskRegistry.php b/app/vendor/cakephp/cakephp/src/Console/TaskRegistry.php index 1c242177a..e4863e7b2 100644 --- a/app/vendor/cakephp/cakephp/src/Console/TaskRegistry.php +++ b/app/vendor/cakephp/cakephp/src/Console/TaskRegistry.php @@ -85,13 +85,12 @@ protected function _throwMissingClassError(string $class, ?string $plugin): void * * @param string $class The classname to create. * @param string $alias The alias of the task. - * @param array $settings An array of settings to use for the task. + * @param array $config An array of settings to use for the task. * @return \Cake\Console\Shell The constructed task class. * @psalm-suppress MoreSpecificImplementedParamType */ - protected function _create($class, string $alias, array $settings): Shell + protected function _create($class, string $alias, array $config): Shell { - // phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.InvalidFormat /** @var \Cake\Console\Shell */ return new $class($this->_Shell->getIo()); } diff --git a/app/vendor/cakephp/cakephp/src/Controller/Component.php b/app/vendor/cakephp/cakephp/src/Controller/Component.php index 1fb5b2a53..54362df63 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Component.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Component.php @@ -75,7 +75,7 @@ class Component implements EventListenerInterface * * @var array */ - public $components = []; + protected $components = []; /** * Default config diff --git a/app/vendor/cakephp/cakephp/src/Controller/Component/AuthComponent.php b/app/vendor/cakephp/cakephp/src/Controller/Component/AuthComponent.php index be0c9fa93..a9d859b2c 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Component/AuthComponent.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Component/AuthComponent.php @@ -22,7 +22,7 @@ use Cake\Controller\Component; use Cake\Controller\Controller; use Cake\Core\App; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Event\EventDispatcherInterface; use Cake\Event\EventDispatcherTrait; use Cake\Event\EventInterface; @@ -49,7 +49,7 @@ class AuthComponent extends Component implements EventDispatcherInterface use EventDispatcherTrait; /** - * The query string key used for remembering the referrered page when getting + * The query string key used for remembering the referred page when getting * redirected to login. * * @var string @@ -176,7 +176,7 @@ class AuthComponent extends Component implements EventDispatcherInterface * * @var array */ - public $components = ['RequestHandler', 'Flash']; + protected $components = ['RequestHandler', 'Flash']; /** * Objects that will be used for authentication checks. @@ -268,7 +268,7 @@ public function authCheck(EventInterface $event): ?Response $controller = $event->getSubject(); $action = $controller->getRequest()->getParam('action'); - if (!$controller->isAction($action)) { + if ($action === null || !$controller->isAction($action)) { return null; } @@ -327,7 +327,7 @@ public function implementedEvents(): array */ protected function _isAllowed(Controller $controller): bool { - $action = strtolower($controller->getRequest()->getParam('action')); + $action = strtolower($controller->getRequest()->getParam('action', '')); return in_array($action, array_map('strtolower', $this->allowedActions), true); } @@ -342,7 +342,7 @@ protected function _isAllowed(Controller $controller): bool * @param \Cake\Controller\Controller $controller A reference to the controller object. * @return \Cake\Http\Response|null Null if current action is login action * else response object returned by authenticate object or Controller::redirect(). - * @throws \Cake\Core\Exception\Exception + * @throws \Cake\Core\Exception\CakeException */ protected function _unauthenticated(Controller $controller): ?Response { @@ -352,7 +352,7 @@ protected function _unauthenticated(Controller $controller): ?Response $response = $controller->getResponse(); $auth = end($this->_authenticateObjects); if ($auth === false) { - throw new Exception('At least one authenticate object must be available.'); + throw new CakeException('At least one authenticate object must be available.'); } $result = $auth->unauthenticated($controller->getRequest(), $response); if ($result !== null) { @@ -513,7 +513,7 @@ public function isAuthorized($user = null, ?ServerRequest $request = null): bool * Loads the authorization objects configured. * * @return array|null The loaded authorization objects, or null when authorize is empty. - * @throws \Cake\Core\Exception\Exception + * @throws \Cake\Core\Exception\CakeException */ public function constructAuthorize(): ?array { @@ -536,10 +536,10 @@ public function constructAuthorize(): ?array } $className = App::className($class, 'Auth', 'Authorize'); if ($className === null) { - throw new Exception(sprintf('Authorization adapter "%s" was not found.', $class)); + throw new CakeException(sprintf('Authorization adapter "%s" was not found.', $class)); } if (!method_exists($className, 'authorize')) { - throw new Exception('Authorization objects must implement an authorize() method.'); + throw new CakeException('Authorization objects must implement an authorize() method.'); } $config = (array)$config + $global; $this->_authorizeObjects[$alias] = new $className($this->_registry, $config); @@ -808,7 +808,7 @@ public function identify() * Loads the configured authentication objects. * * @return array|null The loaded authorization objects, or null on empty authenticate value. - * @throws \Cake\Core\Exception\Exception + * @throws \Cake\Core\Exception\CakeException */ public function constructAuthenticate(): ?array { @@ -831,10 +831,10 @@ public function constructAuthenticate(): ?array } $className = App::className($class, 'Auth', 'Authenticate'); if ($className === null) { - throw new Exception(sprintf('Authentication adapter "%s" was not found.', $class)); + throw new CakeException(sprintf('Authentication adapter "%s" was not found.', $class)); } if (!method_exists($className, 'authenticate')) { - throw new Exception('Authentication objects must implement an authenticate() method.'); + throw new CakeException('Authentication objects must implement an authenticate() method.'); } $config = array_merge($global, (array)$config); $this->_authenticateObjects[$alias] = new $className($this->_registry, $config); @@ -873,7 +873,7 @@ public function storage(?StorageInterface $storage = null): ?StorageInterface } $className = App::className($class, 'Auth/Storage', 'Storage'); if ($className === null) { - throw new Exception(sprintf('Auth storage adapter "%s" was not found.', $class)); + throw new CakeException(sprintf('Auth storage adapter "%s" was not found.', $class)); } $request = $this->getController()->getRequest(); $response = $this->getController()->getResponse(); diff --git a/app/vendor/cakephp/cakephp/src/Controller/Component/FlashComponent.php b/app/vendor/cakephp/cakephp/src/Controller/Component/FlashComponent.php index 5f409fa06..93b120bfd 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Component/FlashComponent.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Component/FlashComponent.php @@ -18,9 +18,9 @@ use Cake\Controller\Component; use Cake\Http\Exception\InternalErrorException; -use Cake\Http\Session; +use Cake\Http\FlashMessage; use Cake\Utility\Inflector; -use Exception; +use Throwable; /** * The CakePHP FlashComponent provides a way for you to write a flash variable @@ -60,63 +60,90 @@ class FlashComponent extends Component * - `clear` A bool stating if the current stack should be cleared to start a new one * - `escape` Set to false to allow templates to print out HTML content * - * @param string|\Exception $message Message to be flashed. If an instance - * of \Exception the exception message will be used and code will be set + * @param string|\Throwable $message Message to be flashed. If an instance + * of \Throwable the throwable message will be used and code will be set * in params. * @param array $options An array of options * @return void */ public function set($message, array $options = []): void { - $options += (array)$this->getConfig(); - - if ($message instanceof Exception) { - if (!isset($options['params']['code'])) { - $options['params']['code'] = $message->getCode(); - } - $message = $message->getMessage(); + if ($message instanceof Throwable) { + $this->flash()->setExceptionMessage($message, $options); + } else { + $this->flash()->set($message, $options); } + } - if (isset($options['escape']) && !isset($options['params']['escape'])) { - $options['params']['escape'] = $options['escape']; - } + /** + * Get flash message utility instance. + * + * @return \Cake\Http\FlashMessage + */ + protected function flash(): FlashMessage + { + return $this->getController()->getRequest()->getFlash(); + } - [$plugin, $element] = pluginSplit($options['element']); + /** + * Proxy method to FlashMessage instance. + * + * @param string|array $key The key to set, or a complete array of configs. + * @param mixed|null $value The value to set. + * @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true. + * @return $this + * @throws \Cake\Core\Exception\CakeException When trying to set a key that is invalid. + */ + public function setConfig($key, $value = null, $merge = true) + { + $this->flash()->setConfig($key, $value, $merge); - if ($plugin) { - $options['element'] = $plugin . '.flash/' . $element; - } else { - $options['element'] = 'flash/' . $element; - } + return $this; + } - $messages = []; - if (!$options['clear']) { - $messages = (array)$this->getSession()->read('Flash.' . $options['key']); - } + /** + * Proxy method to FlashMessage instance. + * + * @param string|null $key The key to get or null for the whole config. + * @param mixed $default The return value when the key does not exist. + * @return mixed Configuration data at the named key or null if the key does not exist. + */ + public function getConfig(?string $key = null, $default = null) + { + return $this->flash()->getConfig($key, $default); + } - if (!$options['duplicate']) { - foreach ($messages as $existingMessage) { - if ($existingMessage['message'] === $message) { - return; - } - } - } + /** + * Proxy method to FlashMessage instance. + * + * @param string $key The key to get. + * @return mixed Configuration data at the named key + * @throws \InvalidArgumentException + */ + public function getConfigOrFail(string $key) + { + return $this->flash()->getConfigOrFail($key); + } - $messages[] = [ - 'message' => $message, - 'key' => $options['key'], - 'element' => $options['element'], - 'params' => $options['params'], - ]; + /** + * Proxy method to FlashMessage instance. + * + * @param string|array $key The key to set, or a complete array of configs. + * @param mixed|null $value The value to set. + * @return $this + */ + public function configShallow($key, $value = null) + { + $this->flash()->configShallow($key, $value); - $this->getSession()->write('Flash.' . $options['key'], $messages); + return $this; } /** * Magic method for verbose flash methods based on element names. * * For example: $this->Flash->success('My message') would use the - * `success.php` element under `templates/Element/Flash` for rendering the + * `success.php` element under `templates/element/flash/` for rendering the * flash message. * * If you make consecutive calls to this method, the messages will stack (if they are @@ -126,7 +153,7 @@ public function set($message, array $options = []): void * specific element from a plugin, you should set the `plugin` option in $args. * * For example: `$this->Flash->warning('My message', ['plugin' => 'PluginName'])` would - * use the `warning.php` element under `plugins/PluginName/templates/Element/Flash` for + * use the `warning.php` element under `plugins/PluginName/templates/element/flash/` for * rendering the flash message. * * @param string $name Element name to use. @@ -154,14 +181,4 @@ public function __call(string $name, array $args) $this->set($args[0], $options); } - - /** - * Returns current session object from a controller request. - * - * @return \Cake\Http\Session - */ - protected function getSession(): Session - { - return $this->getController()->getRequest()->getSession(); - } } diff --git a/app/vendor/cakephp/cakephp/src/Controller/Component/PaginatorComponent.php b/app/vendor/cakephp/cakephp/src/Controller/Component/PaginatorComponent.php index d520a7399..8391cdb14 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Component/PaginatorComponent.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Component/PaginatorComponent.php @@ -46,7 +46,7 @@ class PaginatorComponent extends Component * - `maxLimit` - The maximum limit users can choose to view. Defaults to 100 * - `limit` - The initial number of items per page. Defaults to 20. * - `page` - The starting page, defaults to 1. - * - `whitelist` - A list of parameters users are allowed to set using request + * - `allowedParameters` - A list of parameters users are allowed to set using request * parameters. Modifying this list will allow users to have more influence * over pagination, be careful with what you permit. * @@ -56,7 +56,7 @@ class PaginatorComponent extends Component 'page' => 1, 'limit' => 20, 'maxLimit' => 100, - 'whitelist' => ['limit', 'sort', 'page', 'direction'], + 'allowedParameters' => ['limit', 'sort', 'page', 'direction'], ]; /** @@ -133,19 +133,19 @@ public function implementedEvents(): array * * By default CakePHP will automatically allow sorting on any column on the table object being * paginated. Often times you will want to allow sorting on either associated columns or calculated - * fields. In these cases you will need to define a whitelist of all the columns you wish to allow - * sorting on. You can define the whitelist in the `$settings` parameter: + * fields. In these cases you will need to define an allowed list of fields you wish to allow + * sorting on. You can define the allowed fields in the `$settings` parameter: * * ``` * $settings = [ * 'Articles' => [ * 'finder' => 'custom', - * 'sortWhitelist' => ['title', 'author_id', 'comment_count'], + * 'sortableFields' => ['title', 'author_id', 'comment_count'], * ] * ]; * ``` * - * Passing an empty array as whitelist disallows sorting altogether. + * Passing an empty array as allowed list disallows sorting altogether. * * ### Paginating with custom finders * @@ -222,7 +222,7 @@ public function paginate(object $object, array $settings = []): ResultSetInterfa * - Request parameters * * The result of this method is the aggregate of all the option sets combined together. You can change - * config value `whitelist` to modify which options/values can be set using request parameters. + * config value `allowedParameters` to modify which options/values can be set using request parameters. * * @param string $alias Model alias being paginated, if the general settings has a key with this value * that key's settings will be used for pagination instead of the general ones. @@ -326,6 +326,6 @@ public function configShallow($key, $value = null) */ public function __call(string $method, array $args) { - return call_user_func_array([$this->_paginator, $method], $args); + return $this->_paginator->{$method}(...$args); } } diff --git a/app/vendor/cakephp/cakephp/src/Controller/Component/RequestHandlerComponent.php b/app/vendor/cakephp/cakephp/src/Controller/Component/RequestHandlerComponent.php index 3316084a6..fa2223c70 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Component/RequestHandlerComponent.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Component/RequestHandlerComponent.php @@ -63,7 +63,7 @@ class RequestHandlerComponent extends Component * * - `checkHttpCache` - Whether to check for HTTP cache. Default `true`. * - `viewClassMap` - Mapping between type and view classes. If undefined - * json, xml, and ajax will be mapped. Defining any types will omit the defaults. + * JSON, XML, and AJAX will be mapped. Defining any types will omit the defaults. * * @var array */ @@ -188,7 +188,7 @@ public function startup(EventInterface $event): void * path will become `templates/Controller/xml/action.php`. Layout and template * types will only switch to mime-types recognized by Cake\Http\Response. * If you need to declare additional mime-types, you can do so using - * Cake\Http\Response::type() in your controller's beforeFilter() method. + * Cake\Http\Response::setTypeMap() in your controller's beforeFilter() method. * - If a helper with the same name as the extension exists, it is added to * the controller. * - If the extension is of a type that RequestHandler understands, it will @@ -244,7 +244,7 @@ public function beforeRender(EventInterface $event): void * $this->RequestHandler->accepts('xml'); * ``` * - * Returns true if the client accepts xml. + * Returns true if the client accepts XML. * * @param string|array|null $type Can be null (or no parameter), a string type name, or an * array of types @@ -386,7 +386,7 @@ public function prefers($type = null) * $this->RequestHandler->renderAs($this, 'ajax'); * ``` * - * Render the response as an xml file and force the result as a file download. + * Render the response as an XML file and force the result as a file download. * * ``` * $this->RequestHandler->renderAs($this, 'xml', ['attachment' => 'myfile.xml']; diff --git a/app/vendor/cakephp/cakephp/src/Controller/Component/SecurityComponent.php b/app/vendor/cakephp/cakephp/src/Controller/Component/SecurityComponent.php index 73419ab52..b8f44c145 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Component/SecurityComponent.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Component/SecurityComponent.php @@ -510,11 +510,13 @@ public function generateToken(ServerRequest $request): ServerRequest */ protected function _callback(Controller $controller, string $method, array $params = []) { - if (!is_callable([$controller, $method])) { + $callable = [$controller, $method]; + + if (!is_callable($callable)) { throw new BadRequestException('The request has been black-holed'); } - return call_user_func_array([&$controller, $method], $params); + return $callable(...$params); } /** @@ -537,7 +539,7 @@ protected function _matchExistingFields( $messages = []; foreach ($dataFields as $key => $value) { if (is_int($key)) { - $foundKey = array_search($value, (array)$expectedFields, true); + $foundKey = array_search($value, $expectedFields, true); if ($foundKey === false) { $messages[] = sprintf($intKeyMessage, $value); } else { @@ -568,7 +570,7 @@ protected function _debugExpectedFields(array $expectedFields = [], string $miss } $expectedFieldNames = []; - foreach ((array)$expectedFields as $key => $expectedField) { + foreach ($expectedFields as $key => $expectedField) { if (is_int($key)) { $expectedFieldNames[] = $expectedField; } else { diff --git a/app/vendor/cakephp/cakephp/src/Controller/ComponentRegistry.php b/app/vendor/cakephp/cakephp/src/Controller/ComponentRegistry.php index 05259b094..3e4142544 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/ComponentRegistry.php +++ b/app/vendor/cakephp/cakephp/src/Controller/ComponentRegistry.php @@ -18,7 +18,7 @@ use Cake\Controller\Exception\MissingComponentException; use Cake\Core\App; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Core\ObjectRegistry; use Cake\Event\EventDispatcherInterface; use Cake\Event\EventDispatcherTrait; @@ -61,7 +61,7 @@ public function __construct(?Controller $controller = null) public function getController(): Controller { if ($this->_Controller === null) { - throw new Exception('Controller not set for ComponentRegistry'); + throw new CakeException('Controller not set for ComponentRegistry'); } return $this->_Controller; @@ -125,7 +125,7 @@ protected function _throwMissingClassError(string $class, ?string $plugin): void * @param array $config An array of config to use for the component. * @return \Cake\Controller\Component The constructed component class. * @psalm-suppress MoreSpecificImplementedParamType - * @psalm-var class-string $class + * @psalm-param class-string $class */ protected function _create($class, string $alias, array $config): Component { diff --git a/app/vendor/cakephp/cakephp/src/Controller/Controller.php b/app/vendor/cakephp/cakephp/src/Controller/Controller.php index 52c65c5a9..3bf1327d0 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Controller.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Controller.php @@ -17,6 +17,7 @@ namespace Cake\Controller; use Cake\Controller\Exception\MissingActionException; +use Cake\Core\App; use Cake\Datasource\ModelAwareTrait; use Cake\Event\EventDispatcherInterface; use Cake\Event\EventDispatcherTrait; @@ -81,6 +82,8 @@ * @property \Cake\Controller\Component\FormProtectionComponent $FormProtection * @property \Cake\Controller\Component\PaginatorComponent $Paginator * @property \Cake\Controller\Component\RequestHandlerComponent $RequestHandler + * @property \Cake\Controller\Component\SecurityComponent $Security + * @property \Cake\Controller\Component\AuthComponent $Auth * @link https://book.cakephp.org/4/en/controllers.html */ class Controller implements EventListenerInterface, EventDispatcherInterface @@ -147,7 +150,7 @@ class Controller implements EventListenerInterface, EventDispatcherInterface /** * Instance of ComponentRegistry used to create Components * - * @var \Cake\Controller\ComponentRegistry + * @var \Cake\Controller\ComponentRegistry|null */ protected $_components; @@ -248,12 +251,14 @@ public function initialize(): void */ public function components(?ComponentRegistry $components = null): ComponentRegistry { - if ($components === null && $this->_components === null) { - $this->_components = new ComponentRegistry($this); - } if ($components !== null) { $components->setController($this); - $this->_components = $components; + + return $this->_components = $components; + } + + if ($this->_components === null) { + $this->_components = new ComponentRegistry($this); } return $this->_components; @@ -266,10 +271,10 @@ public function components(?ComponentRegistry $components = null): ComponentRegi * For example: * * ``` - * $this->loadComponent('Acl.Acl'); + * $this->loadComponent('Authentication.Authentication'); * ``` * - * Will result in a `Toolbar` property being set. + * Will result in a `Authentication` property being set. * * @param string $name The name of the component to load. * @param array $config The config for the component. @@ -292,9 +297,14 @@ public function loadComponent(string $name, array $config = []): Component public function __get(string $name) { if (!empty($this->modelClass)) { - [$plugin, $class] = pluginSplit($this->modelClass, true); + if (strpos($this->modelClass, '\\') === false) { + [, $class] = pluginSplit($this->modelClass, true); + } else { + $class = App::shortName($this->modelClass, 'Model/Table', 'Table'); + } + if ($class === $name) { - return $this->loadModel((string)$plugin . $class); + return $this->loadModel(); } } @@ -639,6 +649,7 @@ public function redirect($url, int $status = 302): ?Response * Any other parameters passed to this method will be passed as parameters to the new action. * @param mixed ...$args Arguments passed to the action * @return mixed Returns the return value of the called action + * @deprecated 4.2.0 Refactor your code use `redirect()` instead of forwarding actions. */ public function setAction(string $action, ...$args) { diff --git a/app/vendor/cakephp/cakephp/src/Controller/ControllerFactory.php b/app/vendor/cakephp/cakephp/src/Controller/ControllerFactory.php index c2c69cee0..b877977e7 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/ControllerFactory.php +++ b/app/vendor/cakephp/cakephp/src/Controller/ControllerFactory.php @@ -17,13 +17,17 @@ namespace Cake\Controller; use Cake\Core\App; +use Cake\Core\ContainerInterface; use Cake\Http\ControllerFactoryInterface; use Cake\Http\Exception\MissingControllerException; use Cake\Http\ServerRequest; use Cake\Utility\Inflector; +use InvalidArgumentException; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use ReflectionClass; +use ReflectionFunction; +use ReflectionNamedType; /** * Factory method for building controllers for request. @@ -32,6 +36,21 @@ */ class ControllerFactory implements ControllerFactoryInterface { + /** + * @var \Cake\Core\ContainerInterface + */ + protected $container; + + /** + * Constructor + * + * @param \Cake\Core\ContainerInterface $container The container to build controllers with. + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + /** * Create a controller for a given request. * @@ -43,17 +62,22 @@ public function create(ServerRequestInterface $request): Controller { $className = $this->getControllerClass($request); if ($className === null) { - $this->missingController($request); + throw $this->missingController($request); } - /** @psalm-suppress PossiblyNullArgument */ $reflection = new ReflectionClass($className); if ($reflection->isAbstract()) { - $this->missingController($request); + throw $this->missingController($request); } - /** @var \Cake\Controller\Controller $controller */ - $controller = $reflection->newInstance($request); + // If the controller has a container definition + // add the request as a service. + if ($this->container->has($className)) { + $this->container->add(ServerRequest::class, $request); + $controller = $this->container->get($className); + } else { + $controller = $reflection->newInstance($request); + } return $controller; } @@ -73,9 +97,55 @@ public function invoke($controller): ResponseInterface if ($result instanceof ResponseInterface) { return $result; } - $action = $controller->getAction(); - $args = array_values($controller->getRequest()->getParam('pass')); + + $args = []; + $reflection = new ReflectionFunction($action); + $passed = array_values((array)$controller->getRequest()->getParam('pass')); + foreach ($reflection->getParameters() as $i => $parameter) { + $position = $parameter->getPosition(); + $hasDefault = $parameter->isDefaultValueAvailable(); + + // If there is no type we can't look in the container + // assume the parameter is a passed param + $type = $parameter->getType(); + if (!$type) { + if (count($passed)) { + $args[$position] = array_shift($passed); + } elseif ($hasDefault) { + $args[$position] = $parameter->getDefaultValue(); + } + continue; + } + $typeName = $type instanceof ReflectionNamedType ? ltrim($type->getName(), '?') : null; + + // Primitive types are passed args as they can't be looked up in the container. + // We only handle strings currently. + if ($typeName === 'string') { + if (count($passed)) { + $args[$position] = array_shift($passed); + } elseif ($hasDefault) { + $args[$position] = $parameter->getDefaultValue(); + } + continue; + } + + // Check the container and parameter default value. + if ($typeName && $this->container->has($typeName)) { + $args[$position] = $this->container->get($typeName); + } elseif ($hasDefault) { + $args[$position] = $parameter->getDefaultValue(); + } + if (!array_key_exists($position, $args)) { + throw new InvalidArgumentException( + "Could not resolve action argument `{$parameter->getName()}`. " . + 'It has no definition in the container, no passed parameter, and no default value.' + ); + } + } + if (count($passed)) { + $args = array_merge($args, $passed); + } $controller->invokeAction($action, $args); $result = $controller->shutdownProcess(); @@ -138,10 +208,9 @@ function ($val) { strpos($controller, '.') !== false || $firstChar === strtolower($firstChar) ) { - $this->missingController($request); + throw $this->missingController($request); } - // phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.InvalidFormat /** @var class-string<\Cake\Controller\Controller>|null */ return App::className($pluginPath . $controller, $namespace, 'Controller'); } @@ -150,12 +219,11 @@ function ($val) { * Throws an exception when a controller is missing. * * @param \Cake\Http\ServerRequest $request The request. - * @throws \Cake\Http\Exception\MissingControllerException - * @return void + * @return \Cake\Http\Exception\MissingControllerException */ - protected function missingController(ServerRequest $request): void + protected function missingController(ServerRequest $request) { - throw new MissingControllerException([ + return new MissingControllerException([ 'class' => $request->getParam('controller'), 'plugin' => $request->getParam('plugin'), 'prefix' => $request->getParam('prefix'), diff --git a/app/vendor/cakephp/cakephp/src/Controller/Exception/MissingActionException.php b/app/vendor/cakephp/cakephp/src/Controller/Exception/MissingActionException.php index 1b89f79ff..3ff7fb464 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Exception/MissingActionException.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Exception/MissingActionException.php @@ -14,21 +14,16 @@ */ namespace Cake\Controller\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Missing Action exception - used when a controller action * cannot be found, or when the controller's isAction() method returns false. */ -class MissingActionException extends Exception +class MissingActionException extends CakeException { /** * @inheritDoc */ protected $_messageTemplate = 'Action %s::%s() could not be found, or is not accessible.'; - - /** - * @inheritDoc - */ - protected $_defaultCode = 404; } diff --git a/app/vendor/cakephp/cakephp/src/Controller/Exception/MissingComponentException.php b/app/vendor/cakephp/cakephp/src/Controller/Exception/MissingComponentException.php index 2ce20fe1f..7f13621f7 100644 --- a/app/vendor/cakephp/cakephp/src/Controller/Exception/MissingComponentException.php +++ b/app/vendor/cakephp/cakephp/src/Controller/Exception/MissingComponentException.php @@ -14,12 +14,12 @@ */ namespace Cake\Controller\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Used when a component cannot be found. */ -class MissingComponentException extends Exception +class MissingComponentException extends CakeException { /** * @inheritDoc diff --git a/app/vendor/cakephp/cakephp/src/Core/App.php b/app/vendor/cakephp/cakephp/src/Core/App.php index 9a4f3cf5a..9330b5ca2 100644 --- a/app/vendor/cakephp/cakephp/src/Core/App.php +++ b/app/vendor/cakephp/cakephp/src/Core/App.php @@ -51,7 +51,6 @@ class App * @param string $suffix Class name suffix * @return string|null Namespaced class name, null if the class is not found. * @psalm-return class-string|null - * @psalm-var class-string $class */ public static function className(string $class, string $type = '', string $suffix = ''): ?string { @@ -65,7 +64,6 @@ public static function className(string $class, string $type = '', string $suffi $fullname = '\\' . str_replace('/', '\\', $type . '\\' . $name) . $suffix; if (static::_classExistsInBase($fullname, $base)) { - // phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.InvalidFormat /** @var class-string */ return $base . $fullname; } @@ -74,7 +72,6 @@ public static function className(string $class, string $type = '', string $suffi return null; } - // phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.InvalidFormat /** @var class-string */ return 'Cake' . $fullname; } @@ -138,7 +135,7 @@ public static function shortName(string $class, string $type, string $suffix = ' $nonPluginNamespaces = [ 'Cake', - str_replace('\\', '/', Configure::read('App.namespace')), + str_replace('\\', '/', (string)Configure::read('App.namespace')), ]; if (in_array($pluginName, $nonPluginNamespaces, true)) { return $name; diff --git a/app/vendor/cakephp/cakephp/src/Core/BasePlugin.php b/app/vendor/cakephp/cakephp/src/Core/BasePlugin.php index f52cb2842..0ab41df2c 100644 --- a/app/vendor/cakephp/cakephp/src/Core/BasePlugin.php +++ b/app/vendor/cakephp/cakephp/src/Core/BasePlugin.php @@ -37,11 +37,11 @@ class BasePlugin implements PluginInterface protected $bootstrapEnabled = true; /** - * Load routes or not + * Console middleware * * @var bool */ - protected $routesEnabled = true; + protected $consoleEnabled = true; /** * Enable middleware @@ -51,11 +51,18 @@ class BasePlugin implements PluginInterface protected $middlewareEnabled = true; /** - * Console middleware + * Register container services * * @var bool */ - protected $consoleEnabled = true; + protected $servicesEnabled = true; + + /** + * Load routes or not + * + * @var bool + */ + protected $routesEnabled = true; /** * The path to this plugin. @@ -250,7 +257,7 @@ protected function checkHook(string $hook): void public function routes(RouteBuilder $routes): void { $path = $this->getConfigPath() . 'routes.php'; - if (file_exists($path)) { + if (is_file($path)) { require $path; } } @@ -261,7 +268,7 @@ public function routes(RouteBuilder $routes): void public function bootstrap(PluginApplicationInterface $app): void { $bootstrap = $this->getConfigPath() . 'bootstrap.php'; - if (file_exists($bootstrap)) { + if (is_file($bootstrap)) { require $bootstrap; } } @@ -281,4 +288,14 @@ public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { return $middlewareQueue; } + + /** + * Register container services for this plugin. + * + * @param \Cake\Core\ContainerInterface $container The container to add services to. + * @return void + */ + public function services(ContainerInterface $container): void + { + } } diff --git a/app/vendor/cakephp/cakephp/src/Core/Configure.php b/app/vendor/cakephp/cakephp/src/Core/Configure.php index ad6608cbc..3d3d28485 100644 --- a/app/vendor/cakephp/cakephp/src/Core/Configure.php +++ b/app/vendor/cakephp/cakephp/src/Core/Configure.php @@ -19,7 +19,7 @@ use Cake\Cache\Cache; use Cake\Core\Configure\ConfigEngineInterface; use Cake\Core\Configure\Engine\PhpConfig; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Utility\Hash; use RuntimeException; @@ -162,7 +162,7 @@ public static function check(string $var): bool */ public static function readOrFail(string $var) { - if (static::check($var) === false) { + if (!static::check($var)) { throw new RuntimeException(sprintf('Expected configuration key "%s" not found.', $var)); } @@ -202,7 +202,7 @@ public static function delete(string $var): void */ public static function consumeOrFail(string $var) { - if (static::check($var) === false) { + if (!static::check($var)) { throw new RuntimeException(sprintf('Expected configuration key "%s" not found.', $var)); } @@ -368,13 +368,13 @@ public static function load(string $key, string $config = 'default', bool $merge * @param string[] $keys The name of the top-level keys you want to dump. * This allows you save only some data stored in Configure. * @return bool Success - * @throws \Cake\Core\Exception\Exception if the adapter does not implement a `dump` method. + * @throws \Cake\Core\Exception\CakeException if the adapter does not implement a `dump` method. */ public static function dump(string $key, string $config = 'default', array $keys = []): bool { $engine = static::_getEngine($config); if (!$engine) { - throw new Exception(sprintf('There is no "%s" config engine.', $config)); + throw new CakeException(sprintf('There is no "%s" config engine.', $config)); } $values = static::$_values; if (!empty($keys)) { @@ -421,7 +421,7 @@ public static function version(): string } $path = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'config/config.php'; - if (file_exists($path)) { + if (is_file($path)) { $config = require $path; static::write($config); diff --git a/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/IniConfig.php b/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/IniConfig.php index 4c0a6e417..54a49a4b3 100644 --- a/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/IniConfig.php +++ b/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/IniConfig.php @@ -94,7 +94,7 @@ public function __construct(?string $path = null, ?string $section = null) * @param string $key The identifier to read from. If the key has a . it will be treated * as a plugin prefix. The chosen file must be on the engine's path. * @return array Parsed configuration values. - * @throws \Cake\Core\Exception\Exception when files don't exist. + * @throws \Cake\Core\Exception\CakeException when files don't exist. * Or when files contain '..' as this could lead to abusive reads. */ public function read(string $key): array diff --git a/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/JsonConfig.php b/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/JsonConfig.php index 4816788a7..66e8a902b 100644 --- a/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/JsonConfig.php +++ b/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/JsonConfig.php @@ -18,7 +18,7 @@ use Cake\Core\Configure\ConfigEngineInterface; use Cake\Core\Configure\FileConfigTrait; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * JSON engine allows Configure to load configuration values from @@ -71,7 +71,7 @@ public function __construct(?string $path = null) * @param string $key The identifier to read from. If the key has a . it will be treated * as a plugin prefix. * @return array Parsed configuration values. - * @throws \Cake\Core\Exception\Exception When files don't exist or when + * @throws \Cake\Core\Exception\CakeException When files don't exist or when * files contain '..' (as this could lead to abusive reads) or when there * is an error parsing the JSON string. */ @@ -81,14 +81,14 @@ public function read(string $key): array $values = json_decode(file_get_contents($file), true); if (json_last_error() !== JSON_ERROR_NONE) { - throw new Exception(sprintf( + throw new CakeException(sprintf( 'Error parsing JSON string fetched from config file "%s.json": %s', $key, json_last_error_msg() )); } if (!is_array($values)) { - throw new Exception(sprintf( + throw new CakeException(sprintf( 'Decoding JSON config file "%s.json" did not return an array', $key )); diff --git a/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/PhpConfig.php b/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/PhpConfig.php index cd05427b3..581a11b26 100644 --- a/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/PhpConfig.php +++ b/app/vendor/cakephp/cakephp/src/Core/Configure/Engine/PhpConfig.php @@ -18,7 +18,7 @@ use Cake\Core\Configure\ConfigEngineInterface; use Cake\Core\Configure\FileConfigTrait; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * PHP engine allows Configure to load configuration values from @@ -77,7 +77,7 @@ public function __construct(?string $path = null) * @param string $key The identifier to read from. If the key has a . it will be treated * as a plugin prefix. * @return array Parsed configuration values. - * @throws \Cake\Core\Exception\Exception when files don't exist or they don't contain `$config`. + * @throws \Cake\Core\Exception\CakeException when files don't exist or they don't contain `$config`. * Or when files contain '..' as this could lead to abusive reads. */ public function read(string $key): array @@ -91,7 +91,7 @@ public function read(string $key): array return $return; } - throw new Exception(sprintf('Config file "%s" did not return an array', $key . '.php')); + throw new CakeException(sprintf('Config file "%s" did not return an array', $key . '.php')); } /** diff --git a/app/vendor/cakephp/cakephp/src/Core/Configure/FileConfigTrait.php b/app/vendor/cakephp/cakephp/src/Core/Configure/FileConfigTrait.php index 8741b8239..34931c895 100644 --- a/app/vendor/cakephp/cakephp/src/Core/Configure/FileConfigTrait.php +++ b/app/vendor/cakephp/cakephp/src/Core/Configure/FileConfigTrait.php @@ -16,7 +16,7 @@ */ namespace Cake\Core\Configure; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Core\Plugin; /** @@ -38,13 +38,13 @@ trait FileConfigTrait * as a plugin prefix. * @param bool $checkExists Whether to check if file exists. Defaults to false. * @return string Full file path - * @throws \Cake\Core\Exception\Exception When files don't exist or when + * @throws \Cake\Core\Exception\CakeException When files don't exist or when * files contain '..' as this could lead to abusive reads. */ protected function _getFilePath(string $key, bool $checkExists = false): string { if (strpos($key, '..') !== false) { - throw new Exception('Cannot load/dump configuration files with ../ in them.'); + throw new CakeException('Cannot load/dump configuration files with ../ in them.'); } [$plugin, $key] = pluginSplit($key); @@ -66,6 +66,6 @@ protected function _getFilePath(string $key, bool $checkExists = false): string return $realPath; } - throw new Exception(sprintf('Could not load configuration file: %s', $file)); + throw new CakeException(sprintf('Could not load configuration file: %s', $file)); } } diff --git a/app/vendor/cakephp/cakephp/src/Core/Container.php b/app/vendor/cakephp/cakephp/src/Core/Container.php new file mode 100644 index 000000000..27ccafbb5 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Core/Container.php @@ -0,0 +1,31 @@ +_attributes = $message; + $message = vsprintf($this->_messageTemplate, $message); + } + parent::__construct($message, $code ?? $this->_defaultCode, $previous); + } + + /** + * Get the passed in attributes + * + * @return array + */ + public function getAttributes(): array + { + return $this->_attributes; + } + + /** + * Get/set the response header to be used + * + * See also Cake\Http\Response::withHeader() + * + * @param string|array|null $header A single header string or an associative + * array of "header name" => "header value" + * @param string|null $value The header value. + * @return array|null + * @deprecated 4.2.0 Use `HttpException::setHeaders()` instead. Response headers + * should be set for HttpException only. + */ + public function responseHeader($header = null, $value = null): ?array + { + if ($header === null) { + return $this->_responseHeaders; + } + + deprecationWarning( + 'Setting HTTP response headers from Exception directly is deprecated. ' . + 'If your exceptions extend Exception, they must now extend HttpException. ' . + 'You should only set HTTP headers on HttpException instances via the `setHeaders()` method.' + ); + if (is_array($header)) { + return $this->_responseHeaders = $header; + } + + return $this->_responseHeaders = [$header => $value]; + } +} + +// phpcs:disable +class_exists('Cake\Core\Exception\Exception'); +// phpcs:enable diff --git a/app/vendor/cakephp/cakephp/src/Core/Exception/Exception.php b/app/vendor/cakephp/cakephp/src/Core/Exception/Exception.php index 44a8b9a6b..fb5d49fb2 100644 --- a/app/vendor/cakephp/cakephp/src/Core/Exception/Exception.php +++ b/app/vendor/cakephp/cakephp/src/Core/Exception/Exception.php @@ -9,103 +9,8 @@ * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) - * @since 3.0.0 + * @since 4.2.0 * @license https://opensource.org/licenses/mit-license.php MIT License */ -namespace Cake\Core\Exception; -use RuntimeException; -use Throwable; - -/** - * Base class that all CakePHP Exceptions extend. - * - * @method int getCode() Gets the Exception code. - */ -class Exception extends RuntimeException -{ - /** - * Array of attributes that are passed in from the constructor, and - * made available in the view when a development error is displayed. - * - * @var array - */ - protected $_attributes = []; - - /** - * Template string that has attributes sprintf()'ed into it. - * - * @var string - */ - protected $_messageTemplate = ''; - - /** - * Array of headers to be passed to Cake\Http\Response::header() - * - * @var array|null - */ - protected $_responseHeaders; - - /** - * Default exception code - * - * @var int - */ - protected $_defaultCode = 500; - - /** - * Constructor. - * - * Allows you to create exceptions that are treated as framework errors and disabled - * when debug mode is off. - * - * @param string|array $message Either the string of the error message, or an array of attributes - * that are made available in the view, and sprintf()'d into Exception::$_messageTemplate - * @param int|null $code The code of the error, is also the HTTP status code for the error. - * @param \Throwable|null $previous the previous exception. - */ - public function __construct($message = '', ?int $code = null, ?Throwable $previous = null) - { - if ($code === null) { - $code = $this->_defaultCode; - } - - if (is_array($message)) { - $this->_attributes = $message; - $message = vsprintf($this->_messageTemplate, $message); - } - parent::__construct($message, $code, $previous); - } - - /** - * Get the passed in attributes - * - * @return array - */ - public function getAttributes(): array - { - return $this->_attributes; - } - - /** - * Get/set the response header to be used - * - * See also Cake\Http\Response::withHeader() - * - * @param string|array|null $header A single header string or an associative - * array of "header name" => "header value" - * @param string|null $value The header value. - * @return array|null - */ - public function responseHeader($header = null, $value = null): ?array - { - if ($header === null) { - return $this->_responseHeaders; - } - if (is_array($header)) { - return $this->_responseHeaders = $header; - } - - return $this->_responseHeaders = [$header => $value]; - } -} +class_alias('Cake\Core\Exception\CakeException', 'Cake\Core\Exception\Exception'); diff --git a/app/vendor/cakephp/cakephp/src/Core/Exception/MissingPluginException.php b/app/vendor/cakephp/cakephp/src/Core/Exception/MissingPluginException.php index d2a1f7236..de43f74c6 100644 --- a/app/vendor/cakephp/cakephp/src/Core/Exception/MissingPluginException.php +++ b/app/vendor/cakephp/cakephp/src/Core/Exception/MissingPluginException.php @@ -17,7 +17,7 @@ /** * Exception raised when a plugin could not be found */ -class MissingPluginException extends Exception +class MissingPluginException extends CakeException { /** * @inheritDoc diff --git a/app/vendor/cakephp/cakephp/src/Core/InstanceConfigTrait.php b/app/vendor/cakephp/cakephp/src/Core/InstanceConfigTrait.php index 6c3335dac..d5a2e1efa 100644 --- a/app/vendor/cakephp/cakephp/src/Core/InstanceConfigTrait.php +++ b/app/vendor/cakephp/cakephp/src/Core/InstanceConfigTrait.php @@ -16,7 +16,7 @@ */ namespace Cake\Core; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Utility\Hash; use InvalidArgumentException; @@ -68,7 +68,7 @@ trait InstanceConfigTrait * @param mixed|null $value The value to set. * @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true. * @return $this - * @throws \Cake\Core\Exception\Exception When trying to set a key that is invalid. + * @throws \Cake\Core\Exception\CakeException When trying to set a key that is invalid. */ public function setConfig($key, $value = null, $merge = true) { @@ -222,7 +222,7 @@ protected function _configRead(?string $key) * @param bool|string $merge True to merge recursively, 'shallow' for simple merge, * false to overwrite, defaults to false. * @return void - * @throws \Cake\Core\Exception\Exception if attempting to clobber existing config + * @throws \Cake\Core\Exception\CakeException if attempting to clobber existing config */ protected function _configWrite($key, $value, $merge = false): void { @@ -262,7 +262,7 @@ protected function _configWrite($key, $value, $merge = false): void foreach ($stack as $k) { if (!is_array($update)) { - throw new Exception(sprintf('Cannot set %s value', $key)); + throw new CakeException(sprintf('Cannot set %s value', $key)); } if (!isset($update[$k])) { @@ -280,7 +280,7 @@ protected function _configWrite($key, $value, $merge = false): void * * @param string $key Key to delete. * @return void - * @throws \Cake\Core\Exception\Exception if attempting to clobber existing config + * @throws \Cake\Core\Exception\CakeException if attempting to clobber existing config */ protected function _configDelete(string $key): void { @@ -296,7 +296,7 @@ protected function _configDelete(string $key): void foreach ($stack as $i => $k) { if (!is_array($update)) { - throw new Exception(sprintf('Cannot unset %s value', $key)); + throw new CakeException(sprintf('Cannot unset %s value', $key)); } if (!isset($update[$k])) { diff --git a/app/vendor/cakephp/cakephp/src/Core/LICENSE.txt b/app/vendor/cakephp/cakephp/src/Core/LICENSE.txt index 0b3b94303..b938c9e8e 100644 --- a/app/vendor/cakephp/cakephp/src/Core/LICENSE.txt +++ b/app/vendor/cakephp/cakephp/src/Core/LICENSE.txt @@ -1,7 +1,7 @@ The MIT License (MIT) CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) -Copyright (c) 2005-2019, Cake Software Foundation, Inc. (https://cakefoundation.org) +Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/vendor/cakephp/cakephp/src/Core/ObjectRegistry.php b/app/vendor/cakephp/cakephp/src/Core/ObjectRegistry.php index 7adad7708..3ff380a72 100644 --- a/app/vendor/cakephp/cakephp/src/Core/ObjectRegistry.php +++ b/app/vendor/cakephp/cakephp/src/Core/ObjectRegistry.php @@ -47,7 +47,7 @@ abstract class ObjectRegistry implements Countable, IteratorAggregate * Map of loaded objects. * * @var object[] - * @psalm-var array + * @psalm-var array */ protected $_loaded = []; @@ -62,51 +62,53 @@ abstract class ObjectRegistry implements Countable, IteratorAggregate * an object by setting the 'className' key, i.e., * * ``` - * public $components = [ + * protected $components = [ * 'Email' => [ - * 'className' => '\App\Controller\Component\AliasedEmailComponent' + * 'className' => 'App\Controller\Component\AliasedEmailComponent' * ]; * ]; * ``` * * All calls to the `Email` component would use `AliasedEmail` instead. * - * @param string $objectName The name/class of the object to load. + * @param string $name The name/class of the object to load. * @param array $config Additional settings to use when loading the object. * @return mixed * @psalm-return TObject * @throws \Exception If the class cannot be found. */ - public function load(string $objectName, array $config = []) + public function load(string $name, array $config = []) { if (isset($config['className'])) { - $name = $objectName; - $objectName = $config['className']; + $objName = $name; + $name = $config['className']; } else { - [, $name] = pluginSplit($objectName); + [, $objName] = pluginSplit($name); } - $loaded = isset($this->_loaded[$name]); + $loaded = isset($this->_loaded[$objName]); if ($loaded && !empty($config)) { - $this->_checkDuplicate($name, $config); + $this->_checkDuplicate($objName, $config); } if ($loaded) { - return $this->_loaded[$name]; + return $this->_loaded[$objName]; } - $className = $objectName; - if (is_string($objectName)) { - $className = $this->_resolveClassName($objectName); + $className = $name; + if (is_string($name)) { + $className = $this->_resolveClassName($name); if ($className === null) { - [$plugin, $objectName] = pluginSplit($objectName); - $this->_throwMissingClassError($objectName, $plugin); + [$plugin, $name] = pluginSplit($name); + $this->_throwMissingClassError($name, $plugin); } } - // phpcs:ignore SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation.NonFullyQualifiedClassName - /** @psalm-var TObject $instance */ - $instance = $this->_create($className, $name, $config); - $this->_loaded[$name] = $instance; + /** + * @psalm-var TObject $instance + * @psalm-suppress PossiblyNullArgument + **/ + $instance = $this->_create($className, $objName, $config); + $this->_loaded[$objName] = $instance; return $instance; } @@ -299,7 +301,7 @@ public function normalizeArray(array $objects): array } [, $name] = pluginSplit($objectName); if (isset($config['class'])) { - $normal[$name] = $config; + $normal[$name] = $config + ['config' => []]; } else { $normal[$name] = ['class' => $objectName, 'config' => $config]; } @@ -330,24 +332,24 @@ public function reset() * If this collection implements events, the passed object will * be attached into the event manager * - * @param string $objectName The name of the object to set in the registry. + * @param string $name The name of the object to set in the registry. * @param object $object instance to store in the registry * @return $this * @psalm-param TObject $object * @psalm-suppress MoreSpecificReturnType */ - public function set(string $objectName, object $object) + public function set(string $name, object $object) { - [, $name] = pluginSplit($objectName); + [, $objName] = pluginSplit($name); // Just call unload if the object was loaded before - if (array_key_exists($objectName, $this->_loaded)) { - $this->unload($objectName); + if (array_key_exists($name, $this->_loaded)) { + $this->unload($name); } if ($this instanceof EventDispatcherInterface && $object instanceof EventListenerInterface) { $this->getEventManager()->on($object); } - $this->_loaded[$name] = $object; + $this->_loaded[$objName] = $object; /** @psalm-suppress LessSpecificReturnStatement */ return $this; @@ -358,22 +360,22 @@ public function set(string $objectName, object $object) * * If this registry has an event manager, the object will be detached from any events as well. * - * @param string $objectName The name of the object to remove from the registry. + * @param string $name The name of the object to remove from the registry. * @return $this * @psalm-suppress MoreSpecificReturnType */ - public function unload(string $objectName) + public function unload(string $name) { - if (empty($this->_loaded[$objectName])) { - [$plugin, $objectName] = pluginSplit($objectName); - $this->_throwMissingClassError($objectName, $plugin); + if (empty($this->_loaded[$name])) { + [$plugin, $name] = pluginSplit($name); + $this->_throwMissingClassError($name, $plugin); } - $object = $this->_loaded[$objectName]; + $object = $this->_loaded[$name]; if ($this instanceof EventDispatcherInterface && $object instanceof EventListenerInterface) { $this->getEventManager()->off($object); } - unset($this->_loaded[$objectName]); + unset($this->_loaded[$name]); /** @psalm-suppress LessSpecificReturnStatement */ return $this; diff --git a/app/vendor/cakephp/cakephp/src/Core/PluginCollection.php b/app/vendor/cakephp/cakephp/src/Core/PluginCollection.php index 261264ba8..793e8422d 100644 --- a/app/vendor/cakephp/cakephp/src/Core/PluginCollection.php +++ b/app/vendor/cakephp/cakephp/src/Core/PluginCollection.php @@ -94,9 +94,9 @@ protected function loadConfig(): void return; } $vendorFile = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'cakephp-plugins.php'; - if (!file_exists($vendorFile)) { + if (!is_file($vendorFile)) { $vendorFile = dirname(dirname(dirname(dirname(__DIR__)))) . DIRECTORY_SEPARATOR . 'cakephp-plugins.php'; - if (!file_exists($vendorFile)) { + if (!is_file($vendorFile)) { Configure::write(['plugins' => []]); return; @@ -122,6 +122,9 @@ protected function loadConfig(): void */ public function findPath(string $name): string { + // Ensure plugin config is loaded each time. This is necessary primarily + // for testing because the Configure::clear() call in TestCase::tearDown() + // wipes out all configuration including plugin paths config. $this->loadConfig(); $path = Configure::read('plugins.' . $name); @@ -226,12 +229,10 @@ public function get(string $name): PluginInterface * @param array $config Configuration options for the plugin. * @return \Cake\Core\PluginInterface * @throws \Cake\Core\Exception\MissingPluginException When plugin instance could not be created. - * @psalm-var class-string<\Cake\Core\PluginInterface> $name */ public function create(string $name, array $config = []): PluginInterface { if (strpos($name, '\\') !== false) { - // phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.InvalidFormat /** @var \Cake\Core\PluginInterface */ return new $name($config); } diff --git a/app/vendor/cakephp/cakephp/src/Core/PluginInterface.php b/app/vendor/cakephp/cakephp/src/Core/PluginInterface.php index 7c08ee33b..e0cea8df1 100644 --- a/app/vendor/cakephp/cakephp/src/Core/PluginInterface.php +++ b/app/vendor/cakephp/cakephp/src/Core/PluginInterface.php @@ -20,6 +20,9 @@ /** * Plugin Interface + * + * @method void services(\Cake\Core\ContainerInterface $container) Register plugin services to + * the application's container */ interface PluginInterface { @@ -28,7 +31,7 @@ interface PluginInterface * * @var string[] */ - public const VALID_HOOKS = ['routes', 'bootstrap', 'console', 'middleware']; + public const VALID_HOOKS = ['bootstrap', 'console', 'middleware', 'routes', 'services']; /** * Get the name of this plugin. diff --git a/app/vendor/cakephp/cakephp/src/Core/README.md b/app/vendor/cakephp/cakephp/src/Core/README.md index 48769be1e..f26cba696 100644 --- a/app/vendor/cakephp/cakephp/src/Core/README.md +++ b/app/vendor/cakephp/cakephp/src/Core/README.md @@ -26,7 +26,7 @@ Configure::load('app', 'default', false); Configure::load('other_config', 'default'); ``` -And Write the configuration back into files: +And write the configuration back into files: ```php Configure::dump('my_config', 'default'); diff --git a/app/vendor/cakephp/cakephp/src/Core/Retry/CommandRetry.php b/app/vendor/cakephp/cakephp/src/Core/Retry/CommandRetry.php index 116b22e04..ac65c57c1 100644 --- a/app/vendor/cakephp/cakephp/src/Core/Retry/CommandRetry.php +++ b/app/vendor/cakephp/cakephp/src/Core/Retry/CommandRetry.php @@ -34,22 +34,25 @@ class CommandRetry protected $strategy; /** - * The number of retries to perform in case of failure. - * * @var int */ - protected $retries; + protected $maxRetries; + + /** + * @var int + */ + protected $numRetries; /** * Creates the CommandRetry object with the given strategy and retry count * * @param \Cake\Core\Retry\RetryStrategyInterface $strategy The strategy to follow should the action fail - * @param int $retries The number of times the action has been already called + * @param int $maxRetries The maximum number of retry attempts allowed */ - public function __construct(RetryStrategyInterface $strategy, int $retries = 1) + public function __construct(RetryStrategyInterface $strategy, int $maxRetries = 1) { $this->strategy = $strategy; - $this->retries = $retries; + $this->maxRetries = $maxRetries; } /** @@ -61,23 +64,31 @@ public function __construct(RetryStrategyInterface $strategy, int $retries = 1) */ public function run(callable $action) { - $retryCount = 0; - $lastException = null; - - do { + $this->numRetries = 0; + while (true) { try { return $action(); } catch (Exception $e) { - $lastException = $e; - if (!$this->strategy->shouldRetry($e, $retryCount)) { - throw $e; + if ( + $this->numRetries < $this->maxRetries && + $this->strategy->shouldRetry($e, $this->numRetries) + ) { + $this->numRetries++; + continue; } - } - } while ($this->retries > $retryCount++); - /** @psalm-suppress RedundantCondition */ - if ($lastException !== null) { - throw $lastException; + throw $e; + } } } + + /** + * Returns the last number of retry attemps. + * + * @return int + */ + public function getRetries(): int + { + return $this->numRetries; + } } diff --git a/app/vendor/cakephp/cakephp/src/Core/Retry/RetryStrategyInterface.php b/app/vendor/cakephp/cakephp/src/Core/Retry/RetryStrategyInterface.php index 7b37a9808..7a62b9ca5 100644 --- a/app/vendor/cakephp/cakephp/src/Core/Retry/RetryStrategyInterface.php +++ b/app/vendor/cakephp/cakephp/src/Core/Retry/RetryStrategyInterface.php @@ -28,7 +28,7 @@ interface RetryStrategyInterface * Returns true if the action can be retried, false otherwise. * * @param \Exception $exception The exception that caused the action to fail - * @param int $retryCount The number of times the action has been already called + * @param int $retryCount The number of times action has been retried * @return bool Whether or not it is OK to retry the action */ public function shouldRetry(Exception $exception, int $retryCount): bool; diff --git a/app/vendor/cakephp/cakephp/src/Core/ServiceConfig.php b/app/vendor/cakephp/cakephp/src/Core/ServiceConfig.php new file mode 100644 index 000000000..f4b8a392a --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Core/ServiceConfig.php @@ -0,0 +1,50 @@ +bootstrap($this->getContainer()); + } + + /** + * Bootstrap hook for ServiceProviders + * + * This hook should be implemented if your service provider + * needs to register additional service providers, load configuration + * files or do any other work when the service provider is added to the + * container. + * + * @param \Cake\Core\ContainerInterface $container The container to add services to. + * @return void + */ + public function bootstrap(ContainerInterface $container): void + { + } + + /** + * Call the abstract services() method. + * + * This method primarily exists as a shim between the interface + * that league/container has and the one we want to offer in CakePHP. + * + * @return void + */ + public function register() + { + $this->services($this->getContainer()); + } + + /** + * Register the services in a provider. + * + * All services registered in this method should also be included in the $provides + * property so that services can be located. + * + * @param \Cake\Core\ContainerInterface $container The container to add services to. + * @return void + */ + abstract public function services(ContainerInterface $container): void; +} diff --git a/app/vendor/cakephp/cakephp/src/Core/StaticConfigTrait.php b/app/vendor/cakephp/cakephp/src/Core/StaticConfigTrait.php index 6f39fe830..2e9ffd53e 100644 --- a/app/vendor/cakephp/cakephp/src/Core/StaticConfigTrait.php +++ b/app/vendor/cakephp/cakephp/src/Core/StaticConfigTrait.php @@ -155,6 +155,7 @@ public static function drop(string $config): bool if (!isset(static::$_config[$config])) { return false; } + /** @psalm-suppress RedundantPropertyInitializationCheck */ if (isset(static::$_registry)) { static::$_registry->unload($config); } diff --git a/app/vendor/cakephp/cakephp/src/Core/composer.json b/app/vendor/cakephp/cakephp/src/Core/composer.json index a59fd08d0..ffd17ba7a 100644 --- a/app/vendor/cakephp/cakephp/src/Core/composer.json +++ b/app/vendor/cakephp/cakephp/src/Core/composer.json @@ -27,7 +27,8 @@ }, "suggest": { "cakephp/event": "To use PluginApplicationInterface or plugin applications.", - "cakephp/cache": "To use Configure::store() and restore()." + "cakephp/cache": "To use Configure::store() and restore().", + "league/container": "To use Container and ServiceProvider classes" }, "autoload": { "psr-4": { diff --git a/app/vendor/cakephp/cakephp/src/Core/functions.php b/app/vendor/cakephp/cakephp/src/Core/functions.php index 330fc39f3..2a25c9909 100644 --- a/app/vendor/cakephp/cakephp/src/Core/functions.php +++ b/app/vendor/cakephp/cakephp/src/Core/functions.php @@ -128,8 +128,8 @@ function namespaceSplit(string $class): array /** * print_r() convenience function. * - * In terminals this will act similar to using print_r() directly, when not run on cli - * print_r() will also wrap
 tags around the output of given variable. Similar to debug().
+     * In terminals this will act similar to using print_r() directly, when not run on CLI
+     * print_r() will also wrap `
` tags around the output of given variable. Similar to debug().
      *
      * This function returns the same variable that was passed.
      *
@@ -154,10 +154,10 @@ function pr($var)
 
 if (!function_exists('pj')) {
     /**
-     * json pretty print convenience function.
+     * JSON pretty print convenience function.
      *
-     * In terminals this will act similar to using json_encode() with JSON_PRETTY_PRINT directly, when not run on cli
-     * will also wrap 
 tags around the output of given variable. Similar to pr().
+     * In terminals this will act similar to using json_encode() with JSON_PRETTY_PRINT directly, when not run on CLI
+     * will also wrap `
` tags around the output of given variable. Similar to pr().
      *
      * This function returns the same variable that was passed.
      *
@@ -173,7 +173,7 @@ function pj($var)
         }
 
         $template = PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' ? '
%s
' : "\n%s\n\n"; - printf($template, trim(json_encode($var, JSON_PRETTY_PRINT))); + printf($template, trim(json_encode($var, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES))); return $var; } @@ -292,13 +292,24 @@ function deprecationWarning(string $message, int $stackFrame = 1): void $frame = $trace[$stackFrame]; $frame += ['file' => '[internal]', 'line' => '??']; + $relative = str_replace(DIRECTORY_SEPARATOR, '/', substr($frame['file'], strlen(ROOT) + 1)); + $patterns = (array)Configure::read('Error.ignoredDeprecationPaths'); + foreach ($patterns as $pattern) { + $pattern = str_replace(DIRECTORY_SEPARATOR, '/', $pattern); + if (fnmatch($pattern, $relative)) { + return; + } + } + $message = sprintf( '%s - %s, line: %s' . "\n" . - ' You can disable deprecation warnings by setting `Error.errorLevel` to' . - ' `E_ALL & ~E_USER_DEPRECATED` in your config/app.php.', + ' You can disable all deprecation warnings by setting `Error.errorLevel` to' . + ' `E_ALL & ~E_USER_DEPRECATED`, or add `%s` to ' . + ' `Error.ignoredDeprecationPaths` in your `config/app.php` to mute deprecations from only this file.', $message, $frame['file'], - $frame['line'] + $frame['line'], + $relative ); } diff --git a/app/vendor/cakephp/cakephp/src/Database/Connection.php b/app/vendor/cakephp/cakephp/src/Database/Connection.php index e5c597d92..c2646c2ce 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Connection.php +++ b/app/vendor/cakephp/cakephp/src/Database/Connection.php @@ -171,11 +171,7 @@ public function config(): array */ public function configName(): string { - if (empty($this->_config['name'])) { - return ''; - } - - return $this->_config['name']; + return $this->_config['name'] ?? ''; } /** @@ -275,13 +271,13 @@ public function isConnected(): bool /** * Prepares a SQL statement to be executed. * - * @param string|\Cake\Database\Query $sql The SQL to convert into a prepared statement. + * @param string|\Cake\Database\Query $query The SQL to convert into a prepared statement. * @return \Cake\Database\StatementInterface */ - public function prepare($sql): StatementInterface + public function prepare($query): StatementInterface { - return $this->getDisconnectRetry()->run(function () use ($sql) { - $statement = $this->_driver->prepare($sql); + return $this->getDisconnectRetry()->run(function () use ($query) { + $statement = $this->_driver->prepare($query); if ($this->_logQueries) { $statement = $this->_newLogger($statement); @@ -295,21 +291,19 @@ public function prepare($sql): StatementInterface * Executes a query using $params for interpolating values and $types as a hint for each * those params. * - * @param string $query SQL to be executed and interpolated with $params - * @param array $params list or associative array of params to be interpolated in $query as values + * @param string $sql SQL to be executed and interpolated with $params + * @param array $params list or associative array of params to be interpolated in $sql as values * @param array $types list or associative array of types to be used for casting values in query * @return \Cake\Database\StatementInterface executed statement */ - public function execute(string $query, array $params = [], array $types = []): StatementInterface + public function execute(string $sql, array $params = [], array $types = []): StatementInterface { - return $this->getDisconnectRetry()->run(function () use ($query, $params, $types) { + return $this->getDisconnectRetry()->run(function () use ($sql, $params, $types) { + $statement = $this->prepare($sql); if (!empty($params)) { - $statement = $this->prepare($query); $statement->bind($params, $types); - $statement->execute(); - } else { - $statement = $this->query($query); } + $statement->execute(); return $statement; }); @@ -320,12 +314,12 @@ public function execute(string $query, array $params = [], array $types = []): S * connection's driver * * @param \Cake\Database\Query $query The query to be compiled - * @param \Cake\Database\ValueBinder $generator The placeholder generator to use + * @param \Cake\Database\ValueBinder $binder Value binder * @return string */ - public function compileQuery(Query $query, ValueBinder $generator): string + public function compileQuery(Query $query, ValueBinder $binder): string { - return $this->getDriver()->compileQuery($query, $generator)[1]; + return $this->getDriver()->compileQuery($query, $binder)[1]; } /** @@ -411,18 +405,18 @@ public function getSchemaCollection(): SchemaCollectionInterface * Executes an INSERT query on the specified table. * * @param string $table the table to insert values in - * @param array $data values to be inserted + * @param array $values values to be inserted * @param array $types list of associative array containing the types to be used for casting * @return \Cake\Database\StatementInterface */ - public function insert(string $table, array $data, array $types = []): StatementInterface + public function insert(string $table, array $values, array $types = []): StatementInterface { - return $this->getDisconnectRetry()->run(function () use ($table, $data, $types) { - $columns = array_keys($data); + return $this->getDisconnectRetry()->run(function () use ($table, $values, $types) { + $columns = array_keys($values); return $this->newQuery()->insert($columns, $types) ->into($table) - ->values($data) + ->values($values) ->execute(); }); } @@ -431,16 +425,16 @@ public function insert(string $table, array $data, array $types = []): Statement * Executes an UPDATE statement on the specified table. * * @param string $table the table to update rows from - * @param array $data values to be updated + * @param array $values values to be updated * @param array $conditions conditions to be set for update statement * @param array $types list of associative array containing the types to be used for casting * @return \Cake\Database\StatementInterface */ - public function update(string $table, array $data, array $conditions = [], array $types = []): StatementInterface + public function update(string $table, array $values, array $conditions = [], array $types = []): StatementInterface { - return $this->getDisconnectRetry()->run(function () use ($table, $data, $conditions, $types) { + return $this->getDisconnectRetry()->run(function () use ($table, $values, $conditions, $types) { return $this->newQuery()->update($table) - ->set($data, $types) + ->set($values, $types) ->where($conditions, $types) ->execute(); }); @@ -570,13 +564,8 @@ public function rollback(?bool $toBeginning = null): bool /** * Enables/disables the usage of savepoints, enables only if driver the allows it. * - * If you are trying to enable this feature, make sure you check the return value of this - * function to verify it was enabled successfully. - * - * ### Example: - * - * `$connection->enableSavePoints(true)` Returns true if drivers supports save points, false otherwise - * `$connection->enableSavePoints(false)` Disables usage of savepoints and returns false + * If you are trying to enable this feature, make sure you check + * `isSavePointsEnabled()` to verify that savepoints were enabled successfully. * * @param bool $enable Whether or not save points should be used. * @return $this @@ -617,7 +606,7 @@ public function isSavePointsEnabled(): bool /** * Creates a new save point for nested transactions. * - * @param string|int $name The save point name. + * @param string|int $name Save point name or id * @return void */ public function createSavePoint($name): void @@ -628,7 +617,7 @@ public function createSavePoint($name): void /** * Releases a save point by its name. * - * @param string|int $name The save point name. + * @param string|int $name Save point name or id * @return void */ public function releaseSavePoint($name): void @@ -639,7 +628,7 @@ public function releaseSavePoint($name): void /** * Rollback a save point by its name. * - * @param string|int $name The save point name. + * @param string|int $name Save point name or id * @return void */ public function rollbackSavepoint($name): void @@ -685,12 +674,12 @@ public function supportsDynamicConstraints(): bool /** * @inheritDoc */ - public function transactional(callable $transaction) + public function transactional(callable $callback) { $this->begin(); try { - $result = $transaction($this); + $result = $callback($this); } catch (Throwable $e) { $this->rollback(false); throw $e; @@ -725,13 +714,13 @@ protected function wasNestedTransactionRolledback(): bool /** * @inheritDoc */ - public function disableConstraints(callable $operation) + public function disableConstraints(callable $callback) { - return $this->getDisconnectRetry()->run(function () use ($operation) { + return $this->getDisconnectRetry()->run(function () use ($callback) { $this->disableForeignKeys(); try { - $result = $operation($this); + $result = $callback($this); } finally { $this->enableForeignKeys(); } @@ -841,12 +830,12 @@ public function getCacher(): CacheInterface /** * Enable/disable query logging * - * @param bool $value Enable/disable query logging + * @param bool $enable Enable/disable query logging * @return $this */ - public function enableQueryLogging(bool $value = true) + public function enableQueryLogging(bool $enable = true) { - $this->_logQueries = $value; + $this->_logQueries = $enable; return $this; } @@ -905,7 +894,7 @@ public function getLogger(): LoggerInterface ); } - return $this->_logger = new QueryLogger(); + return $this->_logger = new QueryLogger(['connection' => $this->configName()]); } /** diff --git a/app/vendor/cakephp/cakephp/src/Database/ConstraintsInterface.php b/app/vendor/cakephp/cakephp/src/Database/ConstraintsInterface.php index dafd045d0..f1fe3c161 100644 --- a/app/vendor/cakephp/cakephp/src/Database/ConstraintsInterface.php +++ b/app/vendor/cakephp/cakephp/src/Database/ConstraintsInterface.php @@ -31,19 +31,19 @@ interface ConstraintsInterface * Build and execute SQL queries necessary to create the constraints for the * fixture * - * @param \Cake\Datasource\ConnectionInterface $db An instance of the database + * @param \Cake\Datasource\ConnectionInterface $connection An instance of the database * into which the constraints will be created. * @return bool on success or if there are no constraints to create, or false on failure */ - public function createConstraints(ConnectionInterface $db): bool; + public function createConstraints(ConnectionInterface $connection): bool; /** * Build and execute SQL queries necessary to drop the constraints for the * fixture * - * @param \Cake\Datasource\ConnectionInterface $db An instance of the database + * @param \Cake\Datasource\ConnectionInterface $connection An instance of the database * into which the constraints will be dropped. * @return bool on success or if there are no constraints to drop, or false on failure */ - public function dropConstraints(ConnectionInterface $db): bool; + public function dropConstraints(ConnectionInterface $connection): bool; } diff --git a/app/vendor/cakephp/cakephp/src/Database/Dialect/MysqlDialectTrait.php b/app/vendor/cakephp/cakephp/src/Database/Dialect/MysqlDialectTrait.php deleted file mode 100644 index 87b4513aa..000000000 --- a/app/vendor/cakephp/cakephp/src/Database/Dialect/MysqlDialectTrait.php +++ /dev/null @@ -1,86 +0,0 @@ -_schemaDialect === null) { - $this->_schemaDialect = new MysqlSchema($this); - } - - return $this->_schemaDialect; - } - - /** - * @inheritDoc - */ - public function disableForeignKeySQL(): string - { - return 'SET foreign_key_checks = 0'; - } - - /** - * @inheritDoc - */ - public function enableForeignKeySQL(): string - { - return 'SET foreign_key_checks = 1'; - } -} diff --git a/app/vendor/cakephp/cakephp/src/Database/Dialect/PostgresDialectTrait.php b/app/vendor/cakephp/cakephp/src/Database/Dialect/PostgresDialectTrait.php deleted file mode 100644 index 6064e0d44..000000000 --- a/app/vendor/cakephp/cakephp/src/Database/Dialect/PostgresDialectTrait.php +++ /dev/null @@ -1,195 +0,0 @@ -clause('epilog')) { - $query->epilog('RETURNING *'); - } - - return $query; - } - - /** - * Returns a dictionary of expressions to be transformed when compiling a Query - * to SQL. Array keys are method names to be called in this class - * - * @return array - */ - protected function _expressionTranslators(): array - { - $namespace = 'Cake\Database\Expression'; - - return [ - $namespace . '\FunctionExpression' => '_transformFunctionExpression', - ]; - } - - /** - * Receives a FunctionExpression and changes it so that it conforms to this - * SQL dialect. - * - * @param \Cake\Database\Expression\FunctionExpression $expression The function expression to convert - * to postgres SQL. - * @return void - */ - protected function _transformFunctionExpression(FunctionExpression $expression): void - { - switch ($expression->getName()) { - case 'CONCAT': - // CONCAT function is expressed as exp1 || exp2 - $expression->setName('')->setConjunction(' ||'); - break; - case 'DATEDIFF': - $expression - ->setName('') - ->setConjunction('-') - ->iterateParts(function ($p) { - if (is_string($p)) { - $p = ['value' => [$p => 'literal'], 'type' => null]; - } else { - $p['value'] = [$p['value']]; - } - - return new FunctionExpression('DATE', $p['value'], [$p['type']]); - }); - break; - case 'CURRENT_DATE': - $time = new FunctionExpression('LOCALTIMESTAMP', [' 0 ' => 'literal']); - $expression->setName('CAST')->setConjunction(' AS ')->add([$time, 'date' => 'literal']); - break; - case 'CURRENT_TIME': - $time = new FunctionExpression('LOCALTIMESTAMP', [' 0 ' => 'literal']); - $expression->setName('CAST')->setConjunction(' AS ')->add([$time, 'time' => 'literal']); - break; - case 'NOW': - $expression->setName('LOCALTIMESTAMP')->add([' 0 ' => 'literal']); - break; - case 'RAND': - $expression->setName('RANDOM'); - break; - case 'DATE_ADD': - $expression - ->setName('') - ->setConjunction(' + INTERVAL') - ->iterateParts(function ($p, $key) { - if ($key === 1) { - $p = sprintf("'%s'", $p); - } - - return $p; - }); - break; - case 'DAYOFWEEK': - $expression - ->setName('EXTRACT') - ->setConjunction(' ') - ->add(['DOW FROM' => 'literal'], [], true) - ->add([') + (1' => 'literal']); // Postgres starts on index 0 but Sunday should be 1 - break; - } - } - - /** - * Get the schema dialect. - * - * Used by Cake\Database\Schema package to reflect schema and - * generate schema. - * - * @return \Cake\Database\Schema\BaseSchema - */ - public function schemaDialect(): BaseSchema - { - if ($this->_schemaDialect === null) { - $this->_schemaDialect = new PostgresSchema($this); - } - - return $this->_schemaDialect; - } - - /** - * @inheritDoc - */ - public function disableForeignKeySQL(): string - { - return 'SET CONSTRAINTS ALL DEFERRED'; - } - - /** - * @inheritDoc - */ - public function enableForeignKeySQL(): string - { - return 'SET CONSTRAINTS ALL IMMEDIATE'; - } -} diff --git a/app/vendor/cakephp/cakephp/src/Database/Dialect/SqliteDialectTrait.php b/app/vendor/cakephp/cakephp/src/Database/Dialect/SqliteDialectTrait.php deleted file mode 100644 index 6d3127642..000000000 --- a/app/vendor/cakephp/cakephp/src/Database/Dialect/SqliteDialectTrait.php +++ /dev/null @@ -1,204 +0,0 @@ - 'd', - 'hour' => 'H', - 'month' => 'm', - 'minute' => 'M', - 'second' => 'S', - 'week' => 'W', - 'year' => 'Y', - ]; - - /** - * Returns a dictionary of expressions to be transformed when compiling a Query - * to SQL. Array keys are method names to be called in this class - * - * @return array - */ - protected function _expressionTranslators(): array - { - $namespace = 'Cake\Database\Expression'; - - return [ - $namespace . '\FunctionExpression' => '_transformFunctionExpression', - $namespace . '\TupleComparison' => '_transformTupleComparison', - ]; - } - - /** - * Receives a FunctionExpression and changes it so that it conforms to this - * SQL dialect. - * - * @param \Cake\Database\Expression\FunctionExpression $expression The function expression - * to translate for SQLite. - * @return void - */ - protected function _transformFunctionExpression(FunctionExpression $expression): void - { - switch ($expression->getName()) { - case 'CONCAT': - // CONCAT function is expressed as exp1 || exp2 - $expression->setName('')->setConjunction(' ||'); - break; - case 'DATEDIFF': - $expression - ->setName('ROUND') - ->setConjunction('-') - ->iterateParts(function ($p) { - return new FunctionExpression('JULIANDAY', [$p['value']], [$p['type']]); - }); - break; - case 'NOW': - $expression->setName('DATETIME')->add(["'now'" => 'literal']); - break; - case 'RAND': - $expression - ->setName('ABS') - ->add(['RANDOM() % 1' => 'literal'], [], true); - break; - case 'CURRENT_DATE': - $expression->setName('DATE')->add(["'now'" => 'literal']); - break; - case 'CURRENT_TIME': - $expression->setName('TIME')->add(["'now'" => 'literal']); - break; - case 'EXTRACT': - $expression - ->setName('STRFTIME') - ->setConjunction(' ,') - ->iterateParts(function ($p, $key) { - if ($key === 0) { - $value = rtrim(strtolower($p), 's'); - if (isset($this->_dateParts[$value])) { - $p = ['value' => '%' . $this->_dateParts[$value], 'type' => null]; - } - } - - return $p; - }); - break; - case 'DATE_ADD': - $expression - ->setName('DATE') - ->setConjunction(',') - ->iterateParts(function ($p, $key) { - if ($key === 1) { - $p = ['value' => $p, 'type' => null]; - } - - return $p; - }); - break; - case 'DAYOFWEEK': - $expression - ->setName('STRFTIME') - ->setConjunction(' ') - ->add(["'%w', " => 'literal'], [], true) - ->add([') + (1' => 'literal']); // Sqlite starts on index 0 but Sunday should be 1 - break; - } - } - - /** - * Get the schema dialect. - * - * Used by Cake\Database\Schema package to reflect schema and - * generate schema. - * - * @return \Cake\Database\Schema\BaseSchema - */ - public function schemaDialect(): BaseSchema - { - if ($this->_schemaDialect === null) { - $this->_schemaDialect = new SqliteSchema($this); - } - - return $this->_schemaDialect; - } - - /** - * @inheritDoc - */ - public function disableForeignKeySQL(): string - { - return 'PRAGMA foreign_keys = OFF'; - } - - /** - * @inheritDoc - */ - public function enableForeignKeySQL(): string - { - return 'PRAGMA foreign_keys = ON'; - } - - /** - * {@inheritDoc} - * - * @return \Cake\Database\SqliteCompiler - */ - public function newCompiler(): QueryCompiler - { - return new SqliteCompiler(); - } -} diff --git a/app/vendor/cakephp/cakephp/src/Database/Dialect/SqlserverDialectTrait.php b/app/vendor/cakephp/cakephp/src/Database/Dialect/SqlserverDialectTrait.php deleted file mode 100644 index d125f9d4d..000000000 --- a/app/vendor/cakephp/cakephp/src/Database/Dialect/SqlserverDialectTrait.php +++ /dev/null @@ -1,397 +0,0 @@ -clause('limit'); - $offset = $query->clause('offset'); - - if ($limit && $offset === null) { - $query->modifier(['_auto_top_' => sprintf('TOP %d', $limit)]); - } - - if ($offset !== null && !$query->clause('order')) { - $query->order($query->newExpr()->add('(SELECT NULL)')); - } - - if ($this->version() < 11 && $offset !== null) { - return $this->_pagingSubquery($query, $limit, $offset); - } - - return $this->_transformDistinct($query); - } - - /** - * Get the version of SQLserver we are connected to. - * - * @return string - */ - protected function version(): string - { - $this->connect(); - - return $this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION); - } - - /** - * Generate a paging subquery for older versions of SQLserver. - * - * Prior to SQLServer 2012 there was no equivalent to LIMIT OFFSET, so a subquery must - * be used. - * - * @param \Cake\Database\Query $original The query to wrap in a subquery. - * @param int|null $limit The number of rows to fetch. - * @param int|null $offset The number of rows to offset. - * @return \Cake\Database\Query Modified query object. - */ - protected function _pagingSubquery(Query $original, ?int $limit, ?int $offset): Query - { - $field = '_cake_paging_._cake_page_rownum_'; - - if ($original->clause('order')) { - // SQL server does not support column aliases in OVER clauses. But - // the only practical way to specify the use of calculated columns - // is with their alias. So substitute the select SQL in place of - // any column aliases for those entries in the order clause. - $select = $original->clause('select'); - $order = new OrderByExpression(); - $original - ->clause('order') - ->iterateParts(function ($direction, $orderBy) use ($select, $order) { - $key = $orderBy; - if ( - isset($select[$orderBy]) && - $select[$orderBy] instanceof ExpressionInterface - ) { - $key = $select[$orderBy]->sql(new ValueBinder()); - } - $order->add([$key => $direction]); - - // Leave original order clause unchanged. - return $orderBy; - }); - } else { - $order = new OrderByExpression('(SELECT NULL)'); - } - - $query = clone $original; - $query->select([ - '_cake_page_rownum_' => new UnaryExpression('ROW_NUMBER() OVER', $order), - ])->limit(null) - ->offset(null) - ->order([], true); - - $outer = new Query($query->getConnection()); - $outer->select('*') - ->from(['_cake_paging_' => $query]); - - if ($offset) { - $outer->where(["$field > " . (int)$offset]); - } - if ($limit) { - $value = (int)$offset + (int)$limit; - $outer->where(["$field <= $value"]); - } - - // Decorate the original query as that is what the - // end developer will be calling execute() on originally. - $original->decorateResults(function ($row) { - if (isset($row['_cake_page_rownum_'])) { - unset($row['_cake_page_rownum_']); - } - - return $row; - }); - - return $outer; - } - - /** - * Returns the passed query after rewriting the DISTINCT clause, so that drivers - * that do not support the "ON" part can provide the actual way it should be done - * - * @param \Cake\Database\Query $original The query to be transformed - * @return \Cake\Database\Query - */ - protected function _transformDistinct(Query $original): Query - { - if (!is_array($original->clause('distinct'))) { - return $original; - } - - $query = clone $original; - $distinct = $query->clause('distinct'); - $query->distinct(false); - - $order = new OrderByExpression($distinct); - $query - ->select(function ($q) use ($distinct, $order) { - $over = $q->newExpr('ROW_NUMBER() OVER') - ->add('(PARTITION BY') - ->add($q->newExpr()->add($distinct)->setConjunction(',')) - ->add($order) - ->add(')') - ->setConjunction(' '); - - return [ - '_cake_distinct_pivot_' => $over, - ]; - }) - ->limit(null) - ->offset(null) - ->order([], true); - - $outer = new Query($query->getConnection()); - $outer->select('*') - ->from(['_cake_distinct_' => $query]) - ->where(['_cake_distinct_pivot_' => 1]); - - // Decorate the original query as that is what the - // end developer will be calling execute() on originally. - $original->decorateResults(function ($row) { - if (isset($row['_cake_distinct_pivot_'])) { - unset($row['_cake_distinct_pivot_']); - } - - return $row; - }); - - return $outer; - } - - /** - * Returns a dictionary of expressions to be transformed when compiling a Query - * to SQL. Array keys are method names to be called in this class - * - * @return array - */ - protected function _expressionTranslators(): array - { - $namespace = 'Cake\Database\Expression'; - - return [ - $namespace . '\FunctionExpression' => '_transformFunctionExpression', - $namespace . '\TupleComparison' => '_transformTupleComparison', - ]; - } - - /** - * Receives a FunctionExpression and changes it so that it conforms to this - * SQL dialect. - * - * @param \Cake\Database\Expression\FunctionExpression $expression The function expression to convert to TSQL. - * @return void - */ - protected function _transformFunctionExpression(FunctionExpression $expression): void - { - switch ($expression->getName()) { - case 'CONCAT': - // CONCAT function is expressed as exp1 + exp2 - $expression->setName('')->setConjunction(' +'); - break; - case 'DATEDIFF': - /** @var bool $hasDay */ - $hasDay = false; - $visitor = function ($value) use (&$hasDay) { - if ($value === 'day') { - $hasDay = true; - } - - return $value; - }; - $expression->iterateParts($visitor); - - if (!$hasDay) { - $expression->add(['day' => 'literal'], [], true); - } - break; - case 'CURRENT_DATE': - $time = new FunctionExpression('GETUTCDATE'); - $expression->setName('CONVERT')->add(['date' => 'literal', $time]); - break; - case 'CURRENT_TIME': - $time = new FunctionExpression('GETUTCDATE'); - $expression->setName('CONVERT')->add(['time' => 'literal', $time]); - break; - case 'NOW': - $expression->setName('GETUTCDATE'); - break; - case 'EXTRACT': - $expression->setName('DATEPART')->setConjunction(' ,'); - break; - case 'DATE_ADD': - $params = []; - $visitor = function ($p, $key) use (&$params) { - if ($key === 0) { - $params[2] = $p; - } else { - $valueUnit = explode(' ', $p); - $params[0] = rtrim($valueUnit[1], 's'); - $params[1] = $valueUnit[0]; - } - - return $p; - }; - $manipulator = function ($p, $key) use (&$params) { - return $params[$key]; - }; - - $expression - ->setName('DATEADD') - ->setConjunction(',') - ->iterateParts($visitor) - ->iterateParts($manipulator) - ->add([$params[2] => 'literal']); - break; - case 'DAYOFWEEK': - $expression - ->setName('DATEPART') - ->setConjunction(' ') - ->add(['weekday, ' => 'literal'], [], true); - break; - case 'SUBSTR': - $expression->setName('SUBSTRING'); - if (count($expression) < 4) { - $params = []; - $expression - ->iterateParts(function ($p) use (&$params) { - return $params[] = $p; - }) - ->add([new FunctionExpression('LEN', [$params[0]]), ['string']]); - } - - break; - } - } - - /** - * Get the schema dialect. - * - * Used by Cake\Schema package to reflect schema and - * generate schema. - * - * @return \Cake\Database\Schema\BaseSchema - */ - public function schemaDialect(): BaseSchema - { - return new SqlserverSchema($this); - } - - /** - * Returns a SQL snippet for creating a new transaction savepoint - * - * @param string|int $name save point name - * @return string - */ - public function savePointSQL($name): string - { - return 'SAVE TRANSACTION t' . $name; - } - - /** - * Returns a SQL snippet for releasing a previously created save point - * - * @param string|int $name save point name - * @return string - */ - public function releaseSavePointSQL($name): string - { - return 'COMMIT TRANSACTION t' . $name; - } - - /** - * Returns a SQL snippet for rollbacking a previously created save point - * - * @param string|int $name save point name - * @return string - */ - public function rollbackSavePointSQL($name): string - { - return 'ROLLBACK TRANSACTION t' . $name; - } - - /** - * {@inheritDoc} - * - * @return \Cake\Database\SqlserverCompiler - */ - public function newCompiler(): QueryCompiler - { - return new SqlserverCompiler(); - } - - /** - * @inheritDoc - */ - public function disableForeignKeySQL(): string - { - return 'EXEC sp_MSforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"'; - } - - /** - * @inheritDoc - */ - public function enableForeignKeySQL(): string - { - return 'EXEC sp_MSforeachtable "ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"'; - } -} diff --git a/app/vendor/cakephp/cakephp/src/Database/Driver.php b/app/vendor/cakephp/cakephp/src/Database/Driver.php index 9ca0cec8c..6b56a8ea1 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Driver.php +++ b/app/vendor/cakephp/cakephp/src/Database/Driver.php @@ -17,8 +17,10 @@ namespace Cake\Database; use Cake\Core\App; +use Cake\Core\Retry\CommandRetry; use Cake\Database\Exception\MissingConnectionException; -use Cake\Database\Schema\BaseSchema; +use Cake\Database\Retry\ErrorCodeWaitStrategy; +use Cake\Database\Schema\SchemaDialect; use Cake\Database\Schema\TableSchema; use Cake\Database\Statement\PDOStatement; use Closure; @@ -37,6 +39,11 @@ abstract class Driver implements DriverInterface */ protected const MAX_ALIAS_LENGTH = null; + /** + * @var int[] DB-specific error codes that allow connect retry + */ + protected const RETRY_ERROR_CODES = []; + /** * Instance of PDO. * @@ -67,6 +74,27 @@ abstract class Driver implements DriverInterface */ protected $_autoQuoting = false; + /** + * Whether or not the server supports common table expressions. + * + * @var bool|null + */ + protected $supportsCTEs = null; + + /** + * The server version + * + * @var string|null + */ + protected $_version; + + /** + * The last number of connection retry attempts. + * + * @var int + */ + protected $connectRetries = 0; + /** * Constructor * @@ -96,13 +124,18 @@ public function __construct(array $config = []) */ protected function _connect(string $dsn, array $config): bool { - try { - $connection = new PDO( + $action = function () use ($dsn, $config) { + $this->setConnection(new PDO( $dsn, $config['username'] ?: null, $config['password'] ?: null, $config['flags'] - ); + )); + }; + + $retry = new CommandRetry(new ErrorCodeWaitStrategy(static::RETRY_ERROR_CODES, 5), 4); + try { + $retry->run($action); } catch (PDOException $e) { throw new MissingConnectionException( [ @@ -112,8 +145,9 @@ protected function _connect(string $dsn, array $config): bool null, $e ); + } finally { + $this->connectRetries = $retry->getRetries(); } - $this->setConnection($connection); return true; } @@ -130,6 +164,22 @@ public function disconnect(): void { /** @psalm-suppress PossiblyNullPropertyAssignmentValue */ $this->_connection = null; + $this->_version = null; + } + + /** + * Returns connected server version. + * + * @return string + */ + public function version(): string + { + if ($this->_version === null) { + $this->connect(); + $this->_version = (string)$this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION); + } + + return $this->_version; } /** @@ -218,36 +268,6 @@ public function rollbackTransaction(): bool return $this->_connection->rollBack(); } - /** - * @inheritDoc - */ - abstract public function releaseSavePointSQL($name): string; - - /** - * @inheritDoc - */ - abstract public function savePointSQL($name): string; - - /** - * @inheritDoc - */ - abstract public function rollbackSavePointSQL($name): string; - - /** - * @inheritDoc - */ - abstract public function disableForeignKeySQL(): string; - - /** - * @inheritDoc - */ - abstract public function enableForeignKeySQL(): string; - - /** - * @inheritDoc - */ - abstract public function supportsDynamicConstraints(): bool; - /** * @inheritDoc */ @@ -257,17 +277,23 @@ public function supportsSavePoints(): bool } /** - * {@inheritDoc} + * Returns true if the server supports common table expressions. * - * @param mixed $value The value to quote. - * @param int $type Type to be used for determining kind of quoting to perform. - * @return string + * @return bool + */ + public function supportsCTEs(): bool + { + return $this->supportsCTEs === true; + } + + /** + * @inheritDoc */ public function quote($value, $type = PDO::PARAM_STR): string { $this->connect(); - return $this->_connection->quote($value, $type); + return $this->_connection->quote((string)$value, $type); } /** @@ -290,7 +316,7 @@ abstract public function queryTranslator(string $type): Closure; /** * @inheritDoc */ - abstract public function schemaDialect(): BaseSchema; + abstract public function schemaDialect(): SchemaDialect; /** * @inheritDoc @@ -330,7 +356,7 @@ public function schemaValue($value): string return (string)$value; } - return $this->_connection->quote($value, PDO::PARAM_STR); + return $this->_connection->quote((string)$value, PDO::PARAM_STR); } /** @@ -404,13 +430,13 @@ public function isAutoQuotingEnabled(): bool /** * @inheritDoc */ - public function compileQuery(Query $query, ValueBinder $generator): array + public function compileQuery(Query $query, ValueBinder $binder): array { $processor = $this->newCompiler(); $translator = $this->queryTranslator($query->type()); $query = $translator($query); - return [$query, $processor->compile($query, $generator)]; + return [$query, $processor->compile($query, $binder)]; } /** @@ -446,6 +472,16 @@ public function getMaxAliasLength(): ?int return static::MAX_ALIAS_LENGTH; } + /** + * Returns the number of connection retry attempts made. + * + * @return int + */ + public function getConnectRetries(): int + { + return $this->connectRetries; + } + /** * Destructor */ diff --git a/app/vendor/cakephp/cakephp/src/Database/Driver/Mysql.php b/app/vendor/cakephp/cakephp/src/Database/Driver/Mysql.php index c7dcacde6..8afe34a85 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Driver/Mysql.php +++ b/app/vendor/cakephp/cakephp/src/Database/Driver/Mysql.php @@ -16,25 +16,40 @@ */ namespace Cake\Database\Driver; -use Cake\Database\Dialect\MysqlDialectTrait; use Cake\Database\Driver; use Cake\Database\Query; +use Cake\Database\Schema\MysqlSchemaDialect; +use Cake\Database\Schema\SchemaDialect; use Cake\Database\Statement\MysqlStatement; use Cake\Database\StatementInterface; use PDO; /** - * Class Mysql + * MySQL Driver */ class Mysql extends Driver { - use MysqlDialectTrait; + use SqlDialectTrait; /** * @inheritDoc */ protected const MAX_ALIAS_LENGTH = 256; + /** + * Server type MySQL + * + * @var string + */ + protected const SERVER_TYPE_MYSQL = 'mysql'; + + /** + * Server type MariaDB + * + * @var string + */ + protected const SERVER_TYPE_MARIADB = 'mariadb'; + /** * Base configuration settings for MySQL driver * @@ -54,19 +69,68 @@ class Mysql extends Driver ]; /** - * The server version + * The schema dialect for this driver * - * @var string + * @var \Cake\Database\Schema\MysqlSchemaDialect|null */ - protected $_version; + protected $_schemaDialect; /** * Whether or not the server supports native JSON * - * @var bool + * @var bool|null */ protected $_supportsNativeJson; + /** + * Whether or not the connected server supports window functions. + * + * @var bool|null + */ + protected $_supportsWindowFunctions; + + /** + * String used to start a database identifier quoting to make it safe + * + * @var string + */ + protected $_startQuote = '`'; + + /** + * String used to end a database identifier quoting to make it safe + * + * @var string + */ + protected $_endQuote = '`'; + + /** + * Server type. + * + * If the underlying server is MariaDB, its value will get set to `'mariadb'` + * after `version()` method is called. + * + * @var string + */ + protected $serverType = self::SERVER_TYPE_MYSQL; + + /** + * Mapping of feature to db server version for feature availability checks. + * + * @var array + */ + protected $featuresToVersionMap = [ + 'mysql' => [ + 'json' => '5.7.0', + 'cte' => '8.0.0', + 'window' => '8.0.0', + ], + 'mariadb' => [ + 'json' => '10.2.7', + 'cte' => '10.2.1', + 'window' => '10.2.0', + ], + ]; + /** * Establishes a connection to the database server * @@ -157,6 +221,18 @@ public function prepare($query): StatementInterface return $result; } + /** + * @inheritDoc + */ + public function schemaDialect(): SchemaDialect + { + if ($this->_schemaDialect === null) { + $this->_schemaDialect = new MysqlSchemaDialect($this); + } + + return $this->_schemaDialect; + } + /** * @inheritDoc */ @@ -165,6 +241,22 @@ public function schema(): string return $this->_config['database']; } + /** + * @inheritDoc + */ + public function disableForeignKeySQL(): string + { + return 'SET foreign_key_checks = 0'; + } + + /** + * @inheritDoc + */ + public function enableForeignKeySQL(): string + { + return 'SET foreign_key_checks = 1'; + } + /** * @inheritDoc */ @@ -173,6 +265,57 @@ public function supportsDynamicConstraints(): bool return true; } + /** + * Returns true if the connected server is MariaDB. + * + * @return bool + */ + public function isMariadb(): bool + { + $this->version(); + + return $this->serverType === static::SERVER_TYPE_MARIADB; + } + + /** + * Returns connected server version. + * + * @return string + */ + public function version(): string + { + if ($this->_version === null) { + $this->connect(); + $this->_version = (string)$this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION); + + if (strpos($this->_version, 'MariaDB') !== false) { + $this->serverType = static::SERVER_TYPE_MARIADB; + preg_match('/^(?:5\.5\.5-)?(\d+\.\d+\.\d+.*-MariaDB[^:]*)/', $this->_version, $matches); + $this->_version = $matches[1]; + } + } + + return $this->_version; + } + + /** + * Returns true if the server supports common table expressions. + * + * @return bool + */ + public function supportsCTEs(): bool + { + if ($this->supportsCTEs === null) { + $this->supportsCTEs = version_compare( + $this->version(), + $this->featuresToVersionMap[$this->serverType]['cte'], + '>=' + ); + } + + return $this->supportsCTEs; + } + /** * Returns true if the server supports native JSON columns * @@ -180,14 +323,32 @@ public function supportsDynamicConstraints(): bool */ public function supportsNativeJson(): bool { - if ($this->_supportsNativeJson !== null) { - return $this->_supportsNativeJson; + if ($this->_supportsNativeJson === null) { + $this->_supportsNativeJson = version_compare( + $this->version(), + $this->featuresToVersionMap[$this->serverType]['json'], + '>=' + ); } - if ($this->_version === null) { - $this->_version = (string)$this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION); + return $this->_supportsNativeJson; + } + + /** + * Returns true if the connected server supports window functions. + * + * @return bool + */ + public function supportsWindowFunctions(): bool + { + if ($this->_supportsWindowFunctions === null) { + $this->_supportsWindowFunctions = version_compare( + $this->version(), + $this->featuresToVersionMap[$this->serverType]['window'], + '>=' + ); } - return $this->_supportsNativeJson = version_compare($this->_version, '5.7.0', '>='); + return $this->_supportsWindowFunctions; } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Driver/Postgres.php b/app/vendor/cakephp/cakephp/src/Database/Driver/Postgres.php index abbdd65d8..51ca030b0 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Driver/Postgres.php +++ b/app/vendor/cakephp/cakephp/src/Database/Driver/Postgres.php @@ -16,10 +16,15 @@ */ namespace Cake\Database\Driver; -use Cake\Database\Dialect\PostgresDialectTrait; use Cake\Database\Driver; +use Cake\Database\Expression\FunctionExpression; +use Cake\Database\Expression\IdentifierExpression; +use Cake\Database\Expression\StringExpression; use Cake\Database\PostgresCompiler; +use Cake\Database\Query; use Cake\Database\QueryCompiler; +use Cake\Database\Schema\PostgresSchemaDialect; +use Cake\Database\Schema\SchemaDialect; use PDO; /** @@ -27,7 +32,7 @@ */ class Postgres extends Driver { - use PostgresDialectTrait; + use SqlDialectTrait; /** * @inheritDoc @@ -53,6 +58,32 @@ class Postgres extends Driver 'init' => [], ]; + /** + * The schema dialect class for this driver + * + * @var \Cake\Database\Schema\PostgresSchemaDialect|null + */ + protected $_schemaDialect; + + /** + * String used to start a database identifier quoting to make it safe + * + * @var string + */ + protected $_startQuote = '"'; + + /** + * String used to end a database identifier quoting to make it safe + * + * @var string + */ + protected $_endQuote = '"'; + + /** + * @inheritDoc + */ + protected $supportsCTEs = true; + /** * Establishes a connection to the database server * @@ -106,6 +137,18 @@ public function enabled(): bool return in_array('pgsql', PDO::getAvailableDrivers(), true); } + /** + * @inheritDoc + */ + public function schemaDialect(): SchemaDialect + { + if ($this->_schemaDialect === null) { + $this->_schemaDialect = new PostgresSchemaDialect($this); + } + + return $this->_schemaDialect; + } + /** * Sets connection encoding * @@ -131,6 +174,22 @@ public function setSchema(string $schema): void $this->_connection->exec('SET search_path TO ' . $this->_connection->quote($schema)); } + /** + * @inheritDoc + */ + public function disableForeignKeySQL(): string + { + return 'SET CONSTRAINTS ALL DEFERRED'; + } + + /** + * @inheritDoc + */ + public function enableForeignKeySQL(): string + { + return 'SET CONSTRAINTS ALL IMMEDIATE'; + } + /** * @inheritDoc */ @@ -139,6 +198,130 @@ public function supportsDynamicConstraints(): bool return true; } + /** + * @inheritDoc + */ + protected function _transformDistinct(Query $query): Query + { + return $query; + } + + /** + * @inheritDoc + */ + protected function _insertQueryTranslator(Query $query): Query + { + if (!$query->clause('epilog')) { + $query->epilog('RETURNING *'); + } + + return $query; + } + + /** + * @inheritDoc + */ + protected function _expressionTranslators(): array + { + return [ + IdentifierExpression::class => '_transformIdentifierExpression', + FunctionExpression::class => '_transformFunctionExpression', + StringExpression::class => '_transformStringExpression', + ]; + } + + /** + * Changes identifer expression into postgresql format. + * + * @param \Cake\Database\Expression\IdentifierExpression $expression The expression to tranform. + * @return void + */ + protected function _transformIdentifierExpression(IdentifierExpression $expression): void + { + $collation = $expression->getCollation(); + if ($collation) { + // use trim() to work around expression being transformed multiple times + $expression->setCollation('"' . trim($collation, '"') . '"'); + } + } + + /** + * Receives a FunctionExpression and changes it so that it conforms to this + * SQL dialect. + * + * @param \Cake\Database\Expression\FunctionExpression $expression The function expression to convert + * to postgres SQL. + * @return void + */ + protected function _transformFunctionExpression(FunctionExpression $expression): void + { + switch ($expression->getName()) { + case 'CONCAT': + // CONCAT function is expressed as exp1 || exp2 + $expression->setName('')->setConjunction(' ||'); + break; + case 'DATEDIFF': + $expression + ->setName('') + ->setConjunction('-') + ->iterateParts(function ($p) { + if (is_string($p)) { + $p = ['value' => [$p => 'literal'], 'type' => null]; + } else { + $p['value'] = [$p['value']]; + } + + return new FunctionExpression('DATE', $p['value'], [$p['type']]); + }); + break; + case 'CURRENT_DATE': + $time = new FunctionExpression('LOCALTIMESTAMP', [' 0 ' => 'literal']); + $expression->setName('CAST')->setConjunction(' AS ')->add([$time, 'date' => 'literal']); + break; + case 'CURRENT_TIME': + $time = new FunctionExpression('LOCALTIMESTAMP', [' 0 ' => 'literal']); + $expression->setName('CAST')->setConjunction(' AS ')->add([$time, 'time' => 'literal']); + break; + case 'NOW': + $expression->setName('LOCALTIMESTAMP')->add([' 0 ' => 'literal']); + break; + case 'RAND': + $expression->setName('RANDOM'); + break; + case 'DATE_ADD': + $expression + ->setName('') + ->setConjunction(' + INTERVAL') + ->iterateParts(function ($p, $key) { + if ($key === 1) { + $p = sprintf("'%s'", $p); + } + + return $p; + }); + break; + case 'DAYOFWEEK': + $expression + ->setName('EXTRACT') + ->setConjunction(' ') + ->add(['DOW FROM' => 'literal'], [], true) + ->add([') + (1' => 'literal']); // Postgres starts on index 0 but Sunday should be 1 + break; + } + } + + /** + * Changes string expression into postgresql format. + * + * @param \Cake\Database\Expression\StringExpression $expression The string expression to tranform. + * @return void + */ + protected function _transformStringExpression(StringExpression $expression): void + { + // use trim() to work around expression being transformed multiple times + $expression->setCollation('"' . trim($expression->getCollation(), '"') . '"'); + } + /** * {@inheritDoc} * diff --git a/app/vendor/cakephp/cakephp/src/Database/Driver/SqlDialectTrait.php b/app/vendor/cakephp/cakephp/src/Database/Driver/SqlDialectTrait.php new file mode 100644 index 000000000..03827321d --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Driver/SqlDialectTrait.php @@ -0,0 +1,308 @@ +_startQuote . $identifier . $this->_endQuote; + } + + // string.string + if (preg_match('/^[\w-]+\.[^ \*]*$/u', $identifier)) { + $items = explode('.', $identifier); + + return $this->_startQuote . implode($this->_endQuote . '.' . $this->_startQuote, $items) . $this->_endQuote; + } + + // string.* + if (preg_match('/^[\w-]+\.\*$/u', $identifier)) { + return $this->_startQuote . str_replace('.*', $this->_endQuote . '.*', $identifier); + } + + // Functions + if (preg_match('/^([\w-]+)\((.*)\)$/', $identifier, $matches)) { + return $matches[1] . '(' . $this->quoteIdentifier($matches[2]) . ')'; + } + + // Alias.field AS thing + if (preg_match('/^([\w-]+(\.[\w\s-]+|\(.*\))*)\s+AS\s*([\w-]+)$/ui', $identifier, $matches)) { + return $this->quoteIdentifier($matches[1]) . ' AS ' . $this->quoteIdentifier($matches[3]); + } + + // string.string with spaces + if (preg_match('/^([\w-]+\.[\w][\w\s\-]*[\w])(.*)/u', $identifier, $matches)) { + $items = explode('.', $matches[1]); + $field = implode($this->_endQuote . '.' . $this->_startQuote, $items); + + return $this->_startQuote . $field . $this->_endQuote . $matches[2]; + } + + if (preg_match('/^[\w_\s-]*[\w_-]+/u', $identifier)) { + return $this->_startQuote . $identifier . $this->_endQuote; + } + + return $identifier; + } + + /** + * Returns a callable function that will be used to transform a passed Query object. + * This function, in turn, will return an instance of a Query object that has been + * transformed to accommodate any specificities of the SQL dialect in use. + * + * @param string $type the type of query to be transformed + * (select, insert, update, delete) + * @return \Closure + */ + public function queryTranslator(string $type): Closure + { + return function ($query) use ($type) { + if ($this->isAutoQuotingEnabled()) { + $query = (new IdentifierQuoter($this))->quote($query); + } + + /** @var \Cake\ORM\Query $query */ + $query = $this->{'_' . $type . 'QueryTranslator'}($query); + $translators = $this->_expressionTranslators(); + if (!$translators) { + return $query; + } + + $query->traverseExpressions(function ($expression) use ($translators, $query): void { + foreach ($translators as $class => $method) { + if ($expression instanceof $class) { + $this->{$method}($expression, $query); + } + } + }); + + return $query; + }; + } + + /** + * Returns an associative array of methods that will transform Expression + * objects to conform with the specific SQL dialect. Keys are class names + * and values a method in this class. + * + * @psalm-return array + * @return string[] + */ + protected function _expressionTranslators(): array + { + return []; + } + + /** + * Apply translation steps to select queries. + * + * @param \Cake\Database\Query $query The query to translate + * @return \Cake\Database\Query The modified query + */ + protected function _selectQueryTranslator(Query $query): Query + { + return $this->_transformDistinct($query); + } + + /** + * Returns the passed query after rewriting the DISTINCT clause, so that drivers + * that do not support the "ON" part can provide the actual way it should be done + * + * @param \Cake\Database\Query $query The query to be transformed + * @return \Cake\Database\Query + */ + protected function _transformDistinct(Query $query): Query + { + if (is_array($query->clause('distinct'))) { + $query->group($query->clause('distinct'), true); + $query->distinct(false); + } + + return $query; + } + + /** + * Apply translation steps to delete queries. + * + * Chops out aliases on delete query conditions as most database dialects do not + * support aliases in delete queries. This also removes aliases + * in table names as they frequently don't work either. + * + * We are intentionally not supporting deletes with joins as they have even poorer support. + * + * @param \Cake\Database\Query $query The query to translate + * @return \Cake\Database\Query The modified query + */ + protected function _deleteQueryTranslator(Query $query): Query + { + $hadAlias = false; + $tables = []; + foreach ($query->clause('from') as $alias => $table) { + if (is_string($alias)) { + $hadAlias = true; + } + $tables[] = $table; + } + if ($hadAlias) { + $query->from($tables, true); + } + + if (!$hadAlias) { + return $query; + } + + return $this->_removeAliasesFromConditions($query); + } + + /** + * Apply translation steps to update queries. + * + * Chops out aliases on update query conditions as not all database dialects do support + * aliases in update queries. + * + * Just like for delete queries, joins are currently not supported for update queries. + * + * @param \Cake\Database\Query $query The query to translate + * @return \Cake\Database\Query The modified query + */ + protected function _updateQueryTranslator(Query $query): Query + { + return $this->_removeAliasesFromConditions($query); + } + + /** + * Removes aliases from the `WHERE` clause of a query. + * + * @param \Cake\Database\Query $query The query to process. + * @return \Cake\Database\Query The modified query. + * @throws \RuntimeException In case the processed query contains any joins, as removing + * aliases from the conditions can break references to the joined tables. + */ + protected function _removeAliasesFromConditions(Query $query): Query + { + if ($query->clause('join')) { + throw new RuntimeException( + 'Aliases are being removed from conditions for UPDATE/DELETE queries, ' . + 'this can break references to joined tables.' + ); + } + + $conditions = $query->clause('where'); + if ($conditions) { + $conditions->traverse(function ($expression) { + if ($expression instanceof ComparisonExpression) { + $field = $expression->getField(); + if ( + is_string($field) && + strpos($field, '.') !== false + ) { + [, $unaliasedField] = explode('.', $field, 2); + $expression->setField($unaliasedField); + } + + return $expression; + } + + if ($expression instanceof IdentifierExpression) { + $identifier = $expression->getIdentifier(); + if (strpos($identifier, '.') !== false) { + [, $unaliasedIdentifier] = explode('.', $identifier, 2); + $expression->setIdentifier($unaliasedIdentifier); + } + + return $expression; + } + + return $expression; + }); + } + + return $query; + } + + /** + * Apply translation steps to insert queries. + * + * @param \Cake\Database\Query $query The query to translate + * @return \Cake\Database\Query The modified query + */ + protected function _insertQueryTranslator(Query $query): Query + { + return $query; + } + + /** + * Returns a SQL snippet for creating a new transaction savepoint + * + * @param string|int $name save point name + * @return string + */ + public function savePointSQL($name): string + { + return 'SAVEPOINT LEVEL' . $name; + } + + /** + * Returns a SQL snippet for releasing a previously created save point + * + * @param string|int $name save point name + * @return string + */ + public function releaseSavePointSQL($name): string + { + return 'RELEASE SAVEPOINT LEVEL' . $name; + } + + /** + * Returns a SQL snippet for rollbacking a previously created save point + * + * @param string|int $name save point name + * @return string + */ + public function rollbackSavePointSQL($name): string + { + return 'ROLLBACK TO SAVEPOINT LEVEL' . $name; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlite.php b/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlite.php index 385b4e4bf..d81748e54 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlite.php +++ b/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlite.php @@ -16,9 +16,14 @@ */ namespace Cake\Database\Driver; -use Cake\Database\Dialect\SqliteDialectTrait; use Cake\Database\Driver; +use Cake\Database\Expression\FunctionExpression; +use Cake\Database\Expression\TupleComparison; use Cake\Database\Query; +use Cake\Database\QueryCompiler; +use Cake\Database\Schema\SchemaDialect; +use Cake\Database\Schema\SqliteSchemaDialect; +use Cake\Database\SqliteCompiler; use Cake\Database\Statement\PDOStatement; use Cake\Database\Statement\SqliteStatement; use Cake\Database\StatementInterface; @@ -30,7 +35,8 @@ */ class Sqlite extends Driver { - use SqliteDialectTrait; + use SqlDialectTrait; + use TupleComparisonTranslatorTrait; /** * Base configuration settings for Sqlite driver @@ -50,6 +56,49 @@ class Sqlite extends Driver 'init' => [], ]; + /** + * The schema dialect class for this driver + * + * @var \Cake\Database\Schema\SqliteSchemaDialect|null + */ + protected $_schemaDialect; + + /** + * Whether or not the connected server supports window functions. + * + * @var bool|null + */ + protected $_supportsWindowFunctions; + + /** + * String used to start a database identifier quoting to make it safe + * + * @var string + */ + protected $_startQuote = '"'; + + /** + * String used to end a database identifier quoting to make it safe + * + * @var string + */ + protected $_endQuote = '"'; + + /** + * Mapping of date parts. + * + * @var array + */ + protected $_dateParts = [ + 'day' => 'd', + 'hour' => 'H', + 'month' => 'm', + 'minute' => 'M', + 'second' => 'S', + 'week' => 'W', + 'year' => 'Y', + ]; + /** * Establishes a connection to the database server * @@ -127,6 +176,22 @@ public function prepare($query): StatementInterface return $result; } + /** + * @inheritDoc + */ + public function disableForeignKeySQL(): string + { + return 'PRAGMA foreign_keys = OFF'; + } + + /** + * @inheritDoc + */ + public function enableForeignKeySQL(): string + { + return 'PRAGMA foreign_keys = ON'; + } + /** * @inheritDoc */ @@ -134,4 +199,136 @@ public function supportsDynamicConstraints(): bool { return false; } + + /** + * @inheritDoc + */ + public function schemaDialect(): SchemaDialect + { + if ($this->_schemaDialect === null) { + $this->_schemaDialect = new SqliteSchemaDialect($this); + } + + return $this->_schemaDialect; + } + + /** + * @inheritDoc + */ + public function newCompiler(): QueryCompiler + { + return new SqliteCompiler(); + } + + /** + * @inheritDoc + */ + protected function _expressionTranslators(): array + { + return [ + FunctionExpression::class => '_transformFunctionExpression', + TupleComparison::class => '_transformTupleComparison', + ]; + } + + /** + * Receives a FunctionExpression and changes it so that it conforms to this + * SQL dialect. + * + * @param \Cake\Database\Expression\FunctionExpression $expression The function expression to convert to TSQL. + * @return void + */ + protected function _transformFunctionExpression(FunctionExpression $expression): void + { + switch ($expression->getName()) { + case 'CONCAT': + // CONCAT function is expressed as exp1 || exp2 + $expression->setName('')->setConjunction(' ||'); + break; + case 'DATEDIFF': + $expression + ->setName('ROUND') + ->setConjunction('-') + ->iterateParts(function ($p) { + return new FunctionExpression('JULIANDAY', [$p['value']], [$p['type']]); + }); + break; + case 'NOW': + $expression->setName('DATETIME')->add(["'now'" => 'literal']); + break; + case 'RAND': + $expression + ->setName('ABS') + ->add(['RANDOM() % 1' => 'literal'], [], true); + break; + case 'CURRENT_DATE': + $expression->setName('DATE')->add(["'now'" => 'literal']); + break; + case 'CURRENT_TIME': + $expression->setName('TIME')->add(["'now'" => 'literal']); + break; + case 'EXTRACT': + $expression + ->setName('STRFTIME') + ->setConjunction(' ,') + ->iterateParts(function ($p, $key) { + if ($key === 0) { + $value = rtrim(strtolower($p), 's'); + if (isset($this->_dateParts[$value])) { + $p = ['value' => '%' . $this->_dateParts[$value], 'type' => null]; + } + } + + return $p; + }); + break; + case 'DATE_ADD': + $expression + ->setName('DATE') + ->setConjunction(',') + ->iterateParts(function ($p, $key) { + if ($key === 1) { + $p = ['value' => $p, 'type' => null]; + } + + return $p; + }); + break; + case 'DAYOFWEEK': + $expression + ->setName('STRFTIME') + ->setConjunction(' ') + ->add(["'%w', " => 'literal'], [], true) + ->add([') + (1' => 'literal']); // Sqlite starts on index 0 but Sunday should be 1 + break; + } + } + + /** + * Returns true if the server supports common table expressions. + * + * @return bool + */ + public function supportsCTEs(): bool + { + if ($this->supportsCTEs === null) { + $this->supportsCTEs = version_compare($this->version(), '3.8.3', '>='); + } + + return $this->supportsCTEs; + } + + /** + * Returns true if the connected server supports window functions. + * + * @return bool + */ + public function supportsWindowFunctions(): bool + { + if ($this->_supportsWindowFunctions === null) { + $this->_supportsWindowFunctions = version_compare($this->version(), '3.25.0', '>='); + } + + return $this->_supportsWindowFunctions; + } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlserver.php b/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlserver.php index 76adab63c..3a6555373 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlserver.php +++ b/app/vendor/cakephp/cakephp/src/Database/Driver/Sqlserver.php @@ -16,9 +16,18 @@ */ namespace Cake\Database\Driver; -use Cake\Database\Dialect\SqlserverDialectTrait; use Cake\Database\Driver; +use Cake\Database\Expression\FunctionExpression; +use Cake\Database\Expression\OrderByExpression; +use Cake\Database\Expression\OrderClauseExpression; +use Cake\Database\Expression\TupleComparison; +use Cake\Database\Expression\UnaryExpression; +use Cake\Database\ExpressionInterface; use Cake\Database\Query; +use Cake\Database\QueryCompiler; +use Cake\Database\Schema\SchemaDialect; +use Cake\Database\Schema\SqlserverSchemaDialect; +use Cake\Database\SqlserverCompiler; use Cake\Database\Statement\SqlserverStatement; use Cake\Database\StatementInterface; use InvalidArgumentException; @@ -29,13 +38,21 @@ */ class Sqlserver extends Driver { - use SqlserverDialectTrait; + use SqlDialectTrait; + use TupleComparisonTranslatorTrait; /** * @inheritDoc */ protected const MAX_ALIAS_LENGTH = 128; + /** + * @inheritDoc + */ + protected const RETRY_ERROR_CODES = [ + 40613, // Azure Sql Database paused + ]; + /** * Base configuration settings for Sqlserver driver * @@ -60,6 +77,32 @@ class Sqlserver extends Driver 'multiSubnetFailover' => null, ]; + /** + * The schema dialect class for this driver + * + * @var \Cake\Database\Schema\SqlserverSchemaDialect|null + */ + protected $_schemaDialect; + + /** + * String used to start a database identifier quoting to make it safe + * + * @var string + */ + protected $_startQuote = '['; + + /** + * String used to end a database identifier quoting to make it safe + * + * @var string + */ + protected $_endQuote = ']'; + + /** + * @inheritDoc + */ + protected $supportsCTEs = true; + /** * Establishes a connection to the database server. * @@ -86,12 +129,10 @@ public function connect(): bool } $config['flags'] += [ - PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, ]; if (!empty($config['encoding'])) { - /** @psalm-suppress UndefinedConstant */ $config['flags'][PDO::SQLSRV_ATTR_ENCODING] = $config['encoding']; } $port = ''; @@ -156,21 +197,74 @@ public function enabled(): bool public function prepare($query): StatementInterface { $this->connect(); - $options = [PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL]; - $isObject = $query instanceof Query; - /** @psalm-suppress PossiblyInvalidMethodCall */ - if ($isObject && $query->isBufferedResultsEnabled() === false) { - $options = []; - } - /** - * @psalm-suppress PossiblyInvalidMethodCall - * @psalm-suppress PossiblyInvalidArgument - */ - $statement = $this->_connection->prepare($isObject ? $query->sql() : $query, $options); + + $sql = $query; + $options = [ + PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL, + PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED, + ]; + if ($query instanceof Query) { + $sql = $query->sql(); + if (count($query->getValueBinder()->bindings()) > 2100) { + throw new InvalidArgumentException( + 'Exceeded maximum number of parameters (2100) for prepared statements in Sql Server. ' . + 'This is probably due to a very large WHERE IN () clause which generates a parameter ' . + 'for each value in the array. ' . + 'If using an Association, try changing the `strategy` from select to subquery.' + ); + } + + if (!$query->isBufferedResultsEnabled()) { + $options = []; + } + } + + /** @psalm-suppress PossiblyInvalidArgument */ + $statement = $this->_connection->prepare($sql, $options); return new SqlserverStatement($statement, $this); } + /** + * @inheritDoc + */ + public function savePointSQL($name): string + { + return 'SAVE TRANSACTION t' . $name; + } + + /** + * @inheritDoc + */ + public function releaseSavePointSQL($name): string + { + return 'COMMIT TRANSACTION t' . $name; + } + + /** + * @inheritDoc + */ + public function rollbackSavePointSQL($name): string + { + return 'ROLLBACK TRANSACTION t' . $name; + } + + /** + * @inheritDoc + */ + public function disableForeignKeySQL(): string + { + return 'EXEC sp_MSforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"'; + } + + /** + * @inheritDoc + */ + public function enableForeignKeySQL(): string + { + return 'EXEC sp_MSforeachtable "ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"'; + } + /** * @inheritDoc */ @@ -178,4 +272,274 @@ public function supportsDynamicConstraints(): bool { return true; } + + /** + * @inheritDoc + */ + public function schemaDialect(): SchemaDialect + { + if ($this->_schemaDialect === null) { + $this->_schemaDialect = new SqlserverSchemaDialect($this); + } + + return $this->_schemaDialect; + } + + /** + * {@inheritDoc} + * + * @return \Cake\Database\SqlserverCompiler + */ + public function newCompiler(): QueryCompiler + { + return new SqlserverCompiler(); + } + + /** + * @inheritDoc + */ + protected function _selectQueryTranslator(Query $query): Query + { + $limit = $query->clause('limit'); + $offset = $query->clause('offset'); + + if ($limit && $offset === null) { + $query->modifier(['_auto_top_' => sprintf('TOP %d', $limit)]); + } + + if ($offset !== null && !$query->clause('order')) { + $query->order($query->newExpr()->add('(SELECT NULL)')); + } + + if ($this->version() < 11 && $offset !== null) { + return $this->_pagingSubquery($query, $limit, $offset); + } + + return $this->_transformDistinct($query); + } + + /** + * Generate a paging subquery for older versions of SQLserver. + * + * Prior to SQLServer 2012 there was no equivalent to LIMIT OFFSET, so a subquery must + * be used. + * + * @param \Cake\Database\Query $original The query to wrap in a subquery. + * @param int|null $limit The number of rows to fetch. + * @param int|null $offset The number of rows to offset. + * @return \Cake\Database\Query Modified query object. + */ + protected function _pagingSubquery(Query $original, ?int $limit, ?int $offset): Query + { + $field = '_cake_paging_._cake_page_rownum_'; + + if ($original->clause('order')) { + // SQL server does not support column aliases in OVER clauses. But + // the only practical way to specify the use of calculated columns + // is with their alias. So substitute the select SQL in place of + // any column aliases for those entries in the order clause. + $select = $original->clause('select'); + $order = new OrderByExpression(); + $original + ->clause('order') + ->iterateParts(function ($direction, $orderBy) use ($select, $order) { + $key = $orderBy; + if ( + isset($select[$orderBy]) && + $select[$orderBy] instanceof ExpressionInterface + ) { + $order->add(new OrderClauseExpression($select[$orderBy], $direction)); + } else { + $order->add([$key => $direction]); + } + + // Leave original order clause unchanged. + return $orderBy; + }); + } else { + $order = new OrderByExpression('(SELECT NULL)'); + } + + $query = clone $original; + $query->select([ + '_cake_page_rownum_' => new UnaryExpression('ROW_NUMBER() OVER', $order), + ])->limit(null) + ->offset(null) + ->order([], true); + + $outer = new Query($query->getConnection()); + $outer->select('*') + ->from(['_cake_paging_' => $query]); + + if ($offset) { + $outer->where(["$field > " . $offset]); + } + if ($limit) { + $value = (int)$offset + $limit; + $outer->where(["$field <= $value"]); + } + + // Decorate the original query as that is what the + // end developer will be calling execute() on originally. + $original->decorateResults(function ($row) { + if (isset($row['_cake_page_rownum_'])) { + unset($row['_cake_page_rownum_']); + } + + return $row; + }); + + return $outer; + } + + /** + * @inheritDoc + */ + protected function _transformDistinct(Query $query): Query + { + if (!is_array($query->clause('distinct'))) { + return $query; + } + + $original = $query; + $query = clone $original; + + $distinct = $query->clause('distinct'); + $query->distinct(false); + + $order = new OrderByExpression($distinct); + $query + ->select(function ($q) use ($distinct, $order) { + $over = $q->newExpr('ROW_NUMBER() OVER') + ->add('(PARTITION BY') + ->add($q->newExpr()->add($distinct)->setConjunction(',')) + ->add($order) + ->add(')') + ->setConjunction(' '); + + return [ + '_cake_distinct_pivot_' => $over, + ]; + }) + ->limit(null) + ->offset(null) + ->order([], true); + + $outer = new Query($query->getConnection()); + $outer->select('*') + ->from(['_cake_distinct_' => $query]) + ->where(['_cake_distinct_pivot_' => 1]); + + // Decorate the original query as that is what the + // end developer will be calling execute() on originally. + $original->decorateResults(function ($row) { + if (isset($row['_cake_distinct_pivot_'])) { + unset($row['_cake_distinct_pivot_']); + } + + return $row; + }); + + return $outer; + } + + /** + * @inheritDoc + */ + protected function _expressionTranslators(): array + { + return [ + FunctionExpression::class => '_transformFunctionExpression', + TupleComparison::class => '_transformTupleComparison', + ]; + } + + /** + * Receives a FunctionExpression and changes it so that it conforms to this + * SQL dialect. + * + * @param \Cake\Database\Expression\FunctionExpression $expression The function expression to convert to TSQL. + * @return void + */ + protected function _transformFunctionExpression(FunctionExpression $expression): void + { + switch ($expression->getName()) { + case 'CONCAT': + // CONCAT function is expressed as exp1 + exp2 + $expression->setName('')->setConjunction(' +'); + break; + case 'DATEDIFF': + /** @var bool $hasDay */ + $hasDay = false; + $visitor = function ($value) use (&$hasDay) { + if ($value === 'day') { + $hasDay = true; + } + + return $value; + }; + $expression->iterateParts($visitor); + + if (!$hasDay) { + $expression->add(['day' => 'literal'], [], true); + } + break; + case 'CURRENT_DATE': + $time = new FunctionExpression('GETUTCDATE'); + $expression->setName('CONVERT')->add(['date' => 'literal', $time]); + break; + case 'CURRENT_TIME': + $time = new FunctionExpression('GETUTCDATE'); + $expression->setName('CONVERT')->add(['time' => 'literal', $time]); + break; + case 'NOW': + $expression->setName('GETUTCDATE'); + break; + case 'EXTRACT': + $expression->setName('DATEPART')->setConjunction(' ,'); + break; + case 'DATE_ADD': + $params = []; + $visitor = function ($p, $key) use (&$params) { + if ($key === 0) { + $params[2] = $p; + } else { + $valueUnit = explode(' ', $p); + $params[0] = rtrim($valueUnit[1], 's'); + $params[1] = $valueUnit[0]; + } + + return $p; + }; + $manipulator = function ($p, $key) use (&$params) { + return $params[$key]; + }; + + $expression + ->setName('DATEADD') + ->setConjunction(',') + ->iterateParts($visitor) + ->iterateParts($manipulator) + ->add([$params[2] => 'literal']); + break; + case 'DAYOFWEEK': + $expression + ->setName('DATEPART') + ->setConjunction(' ') + ->add(['weekday, ' => 'literal'], [], true); + break; + case 'SUBSTR': + $expression->setName('SUBSTRING'); + if (count($expression) < 4) { + $params = []; + $expression + ->iterateParts(function ($p) use (&$params) { + return $params[] = $p; + }) + ->add([new FunctionExpression('LEN', [$params[0]]), ['string']]); + } + + break; + } + } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Dialect/TupleComparisonTranslatorTrait.php b/app/vendor/cakephp/cakephp/src/Database/Driver/TupleComparisonTranslatorTrait.php similarity index 92% rename from app/vendor/cakephp/cakephp/src/Database/Dialect/TupleComparisonTranslatorTrait.php rename to app/vendor/cakephp/cakephp/src/Database/Driver/TupleComparisonTranslatorTrait.php index 728319058..c3cf72fc0 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Dialect/TupleComparisonTranslatorTrait.php +++ b/app/vendor/cakephp/cakephp/src/Database/Driver/TupleComparisonTranslatorTrait.php @@ -14,7 +14,7 @@ * @since 3.0.0 * @license https://opensource.org/licenses/mit-license.php MIT License */ -namespace Cake\Database\Dialect; +namespace Cake\Database\Driver; use Cake\Database\Expression\IdentifierExpression; use Cake\Database\Expression\QueryExpression; @@ -71,6 +71,13 @@ protected function _transformTupleComparison(TupleComparison $expression, Query return; } + $type = $expression->getType(); + if ($type) { + $typeMap = array_combine($fields, $type); + } else { + $typeMap = []; + } + $surrogate = $query->getConnection() ->newQuery() ->select($true); @@ -87,7 +94,7 @@ protected function _transformTupleComparison(TupleComparison $expression, Query } $conditions['OR'][] = $item; } - $surrogate->where($conditions); + $surrogate->where($conditions, $typeMap); $expression->setField($true); $expression->setValue($surrogate); diff --git a/app/vendor/cakephp/cakephp/src/Database/DriverInterface.php b/app/vendor/cakephp/cakephp/src/Database/DriverInterface.php index e04b043f0..5587b795f 100644 --- a/app/vendor/cakephp/cakephp/src/Database/DriverInterface.php +++ b/app/vendor/cakephp/cakephp/src/Database/DriverInterface.php @@ -16,7 +16,7 @@ */ namespace Cake\Database; -use Cake\Database\Schema\BaseSchema; +use Cake\Database\Schema\SchemaDialect; use Cake\Database\Schema\TableSchema; use Closure; @@ -24,6 +24,7 @@ * Interface for database driver. * * @method int|null getMaxAliasLength() Returns the maximum alias length allowed. + * @method int getConnectRetries() Returns the number of connection retry attempts made. */ interface DriverInterface { @@ -96,7 +97,7 @@ public function rollbackTransaction(): bool; /** * Get the SQL for releasing a save point. * - * @param string|int $name The table name. + * @param string|int $name Save point name or id * @return string */ public function releaseSavePointSQL($name): string; @@ -104,7 +105,7 @@ public function releaseSavePointSQL($name): string; /** * Get the SQL for creating a save point. * - * @param string|int $name The table name. + * @param string|int $name Save point name or id * @return string */ public function savePointSQL($name): string; @@ -112,7 +113,7 @@ public function savePointSQL($name): string; /** * Get the SQL for rollingback a save point. * - * @param string|int $name The table name. + * @param string|int $name Save point name or id * @return string */ public function rollbackSavePointSQL($name): string; @@ -150,7 +151,7 @@ public function supportsSavePoints(): bool; * Returns a value in a safe representation to be used in a query string * * @param mixed $value The value to quote. - * @param int $type Type to be used for determining kind of quoting to perform. + * @param int $type Must be one of the \PDO::PARAM_* constants * @return string */ public function quote($value, $type): string; @@ -182,9 +183,9 @@ public function queryTranslator(string $type): Closure; * If all the tables that use this Driver specify their * own schemas, then this may return null. * - * @return \Cake\Database\Schema\BaseSchema + * @return \Cake\Database\Schema\SchemaDialect */ - public function schemaDialect(): BaseSchema; + public function schemaDialect(): SchemaDialect; /** * Quotes a database identifier (a column name, table name, etc..) to @@ -255,11 +256,11 @@ public function isAutoQuotingEnabled(): bool; * of the transformed query and the full compiled SQL string. * * @param \Cake\Database\Query $query The query to compile. - * @param \Cake\Database\ValueBinder $generator The value binder to use. + * @param \Cake\Database\ValueBinder $binder The value binder to use. * @return array containing 2 entries. The first entity is the transformed query * and the second one the compiled SQL. */ - public function compileQuery(Query $query, ValueBinder $generator): array; + public function compileQuery(Query $query, ValueBinder $binder): array; /** * Returns an instance of a QueryCompiler. diff --git a/app/vendor/cakephp/cakephp/src/Database/Exception.php b/app/vendor/cakephp/cakephp/src/Database/Exception.php index 812fb00a8..d47914df0 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Exception.php +++ b/app/vendor/cakephp/cakephp/src/Database/Exception.php @@ -6,21 +6,11 @@ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) * * Licensed under The MIT License - * For full copyright and license information, please see the LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) - * @link https://cakephp.org CakePHP(tm) Project - * @since 3.0.0 + * @since 4.2.0 * @license https://opensource.org/licenses/mit-license.php MIT License */ -namespace Cake\Database; -use Cake\Core\Exception\Exception as CakeException; - -/** - * Exception for the database package. - */ -class Exception extends CakeException -{ -} +class_alias('Cake\Database\Exception\DatabaseException', 'Cake\Database\Exception'); diff --git a/app/vendor/cakephp/cakephp/src/Database/Exception/DatabaseException.php b/app/vendor/cakephp/cakephp/src/Database/Exception/DatabaseException.php new file mode 100644 index 000000000..3852808d9 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Exception/DatabaseException.php @@ -0,0 +1,30 @@ +filter === null) { + $this->filter = new QueryExpression(); + } + + if ($conditions instanceof Closure) { + $conditions = $conditions(new QueryExpression()); + } + + $this->filter->add($conditions, $types); + + return $this; + } + + /** + * Adds an empty `OVER()` window expression or a named window epression. + * + * @param string|null $name Window name + * @return $this + */ + public function over(?string $name = null) + { + if ($this->window === null) { + $this->window = new WindowExpression(); + } + if ($name) { + // Set name manually in case this was chained from FunctionsBuilder wrapper + $this->window->name($name); + } + + return $this; + } + + /** + * @inheritDoc + */ + public function partition($partitions) + { + $this->over(); + $this->window->partition($partitions); + + return $this; + } + + /** + * @inheritDoc + */ + public function order($fields) + { + $this->over(); + $this->window->order($fields); + + return $this; + } + + /** + * @inheritDoc + */ + public function range($start, $end = 0) + { + $this->over(); + $this->window->range($start, $end); + + return $this; + } + + /** + * @inheritDoc + */ + public function rows(?int $start, ?int $end = 0) + { + $this->over(); + $this->window->rows($start, $end); + + return $this; + } + + /** + * @inheritDoc + */ + public function groups(?int $start, ?int $end = 0) + { + $this->over(); + $this->window->groups($start, $end); + + return $this; + } + + /** + * @inheritDoc + */ + public function frame( + string $type, + $startOffset, + string $startDirection, + $endOffset, + string $endDirection + ) { + $this->over(); + $this->window->frame($type, $startOffset, $startDirection, $endOffset, $endDirection); + + return $this; + } + + /** + * @inheritDoc + */ + public function excludeCurrent() + { + $this->over(); + $this->window->excludeCurrent(); + + return $this; + } + + /** + * @inheritDoc + */ + public function excludeGroup() + { + $this->over(); + $this->window->excludeGroup(); + + return $this; + } + + /** + * @inheritDoc + */ + public function excludeTies() + { + $this->over(); + $this->window->excludeTies(); + + return $this; + } + + /** + * @inheritDoc + */ + public function sql(ValueBinder $binder): string + { + $sql = parent::sql($binder); + if ($this->filter !== null) { + $sql .= ' FILTER (WHERE ' . $this->filter->sql($binder) . ')'; + } + if ($this->window !== null) { + if ($this->window->isNamedOnly()) { + $sql .= ' OVER ' . $this->window->sql($binder); + } else { + $sql .= ' OVER (' . $this->window->sql($binder) . ')'; + } + } + + return $sql; + } + + /** + * @inheritDoc + */ + public function traverse(Closure $callback) + { + parent::traverse($callback); + if ($this->filter !== null) { + $callback($this->filter); + $this->filter->traverse($callback); + } + if ($this->window !== null) { + $callback($this->window); + $this->window->traverse($callback); + } + + return $this; + } + + /** + * @inheritDoc + */ + public function count(): int + { + $count = parent::count(); + if ($this->window !== null) { + $count = $count + 1; + } + + return $count; + } + + /** + * Clone this object and its subtree of expressions. + * + * @return void + */ + public function __clone() + { + parent::__clone(); + if ($this->filter !== null) { + $this->filter = clone $this->filter; + } + if ($this->window !== null) { + $this->window = clone $this->window; + } + } +} diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/BetweenExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/BetweenExpression.php index 30c8b06fe..34b7a1ee6 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Expression/BetweenExpression.php +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/BetweenExpression.php @@ -72,12 +72,9 @@ public function __construct($field, $from, $to, $type = null) } /** - * Converts the expression to its string representation - * - * @param \Cake\Database\ValueBinder $generator Placeholder generator object - * @return string + * @inheritDoc */ - public function sql(ValueBinder $generator): string + public function sql(ValueBinder $binder): string { $parts = [ 'from' => $this->_from, @@ -87,15 +84,15 @@ public function sql(ValueBinder $generator): string /** @var string|\Cake\Database\ExpressionInterface $field */ $field = $this->_field; if ($field instanceof ExpressionInterface) { - $field = $field->sql($generator); + $field = $field->sql($binder); } foreach ($parts as $name => $part) { if ($part instanceof ExpressionInterface) { - $parts[$name] = $part->sql($generator); + $parts[$name] = $part->sql($binder); continue; } - $parts[$name] = $this->_bindValue($part, $generator, $this->_type); + $parts[$name] = $this->_bindValue($part, $binder, $this->_type); } return sprintf('%s BETWEEN %s AND %s', $field, $parts['from'], $parts['to']); @@ -104,11 +101,11 @@ public function sql(ValueBinder $generator): string /** * @inheritDoc */ - public function traverse(Closure $visitor) + public function traverse(Closure $callback) { foreach ([$this->_field, $this->_from, $this->_to] as $part) { if ($part instanceof ExpressionInterface) { - $visitor($part); + $callback($part); } } @@ -119,14 +116,14 @@ public function traverse(Closure $visitor) * Registers a value in the placeholder generator and returns the generated placeholder * * @param mixed $value The value to bind - * @param \Cake\Database\ValueBinder $generator The value binder to use + * @param \Cake\Database\ValueBinder $binder The value binder to use * @param string $type The type of $value * @return string generated placeholder */ - protected function _bindValue($value, $generator, $type): string + protected function _bindValue($value, $binder, $type): string { - $placeholder = $generator->placeholder('c'); - $generator->bind($placeholder, $value, $type); + $placeholder = $binder->placeholder('c'); + $binder->bind($placeholder, $value, $type); return $placeholder; } diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/CaseExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/CaseExpression.php index 90a5f1218..d8c6ebe13 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Expression/CaseExpression.php +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/CaseExpression.php @@ -187,16 +187,16 @@ public function elseValue($value = null, ?string $type = null): void * Compiles the relevant parts into sql * * @param array|string|\Cake\Database\ExpressionInterface $part The part to compile - * @param \Cake\Database\ValueBinder $generator Sql generator + * @param \Cake\Database\ValueBinder $binder Sql generator * @return string */ - protected function _compile($part, ValueBinder $generator): string + protected function _compile($part, ValueBinder $binder): string { if ($part instanceof ExpressionInterface) { - $part = $part->sql($generator); + $part = $part->sql($binder); } elseif (is_array($part)) { - $placeholder = $generator->placeholder('param'); - $generator->bind($placeholder, $part['value'], $part['type']); + $placeholder = $binder->placeholder('param'); + $binder->bind($placeholder, $part['value'], $part['type']); $part = $placeholder; } @@ -206,20 +206,20 @@ protected function _compile($part, ValueBinder $generator): string /** * Converts the Node into a SQL string fragment. * - * @param \Cake\Database\ValueBinder $generator Placeholder generator object + * @param \Cake\Database\ValueBinder $binder Placeholder generator object * @return string */ - public function sql(ValueBinder $generator): string + public function sql(ValueBinder $binder): string { $parts = []; $parts[] = 'CASE'; foreach ($this->_conditions as $k => $part) { $value = $this->_values[$k]; - $parts[] = 'WHEN ' . $this->_compile($part, $generator) . ' THEN ' . $this->_compile($value, $generator); + $parts[] = 'WHEN ' . $this->_compile($part, $binder) . ' THEN ' . $this->_compile($value, $binder); } if ($this->_elseValue !== null) { $parts[] = 'ELSE'; - $parts[] = $this->_compile($this->_elseValue, $generator); + $parts[] = $this->_compile($this->_elseValue, $binder); } $parts[] = 'END'; @@ -229,19 +229,19 @@ public function sql(ValueBinder $generator): string /** * @inheritDoc */ - public function traverse(Closure $visitor) + public function traverse(Closure $callback) { foreach (['_conditions', '_values'] as $part) { foreach ($this->{$part} as $c) { if ($c instanceof ExpressionInterface) { - $visitor($c); - $c->traverse($visitor); + $callback($c); + $c->traverse($callback); } } } if ($this->_elseValue instanceof ExpressionInterface) { - $visitor($this->_elseValue); - $this->_elseValue->traverse($visitor); + $callback($this->_elseValue); + $this->_elseValue->traverse($callback); } return $this; diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/CommonTableExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/CommonTableExpression.php new file mode 100644 index 000000000..d2a38cadc --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/CommonTableExpression.php @@ -0,0 +1,239 @@ +name = new IdentifierExpression($name); + if ($query) { + $this->query($query); + } + } + + /** + * Sets the name of this CTE. + * + * This is the named you used to reference the expression + * in select, insert, etc queries. + * + * @param string $name The CTE name. + * @return $this + */ + public function name(string $name) + { + $this->name = new IdentifierExpression($name); + + return $this; + } + + /** + * Sets the query for this CTE. + * + * @param \Closure|\Cake\Database\ExpressionInterface $query CTE query + * @return $this + */ + public function query($query) + { + if ($query instanceof Closure) { + $query = $query(); + if (!($query instanceof ExpressionInterface)) { + throw new RuntimeException( + 'You must return an `ExpressionInterface` from a Closure passed to `query()`.' + ); + } + } + $this->query = $query; + + return $this; + } + + /** + * Adds one or more fields (arguments) to the CTE. + * + * @param string|string[]|\Cake\Database\Expression\IdentifierExpression|\Cake\Database\Expression\IdentifierExpression[] $fields Field names + * @return $this + */ + public function field($fields) + { + $fields = (array)$fields; + foreach ($fields as &$field) { + if (!($field instanceof IdentifierExpression)) { + $field = new IdentifierExpression($field); + } + } + $this->fields = array_merge($this->fields, $fields); + + return $this; + } + + /** + * Sets this CTE as materialized. + * + * @return $this + */ + public function materialized() + { + $this->materialized = 'MATERIALIZED'; + + return $this; + } + + /** + * Sets this CTE as not materialized. + * + * @return $this + */ + public function notMaterialized() + { + $this->materialized = 'NOT MATERIALIZED'; + + return $this; + } + + /** + * Gets whether this CTE is recursive. + * + * @return bool + */ + public function isRecursive(): bool + { + return $this->recursive; + } + + /** + * Sets this CTE as recursive. + * + * @return $this + */ + public function recursive() + { + $this->recursive = true; + + return $this; + } + + /** + * @inheritDoc + */ + public function sql(ValueBinder $binder): string + { + $fields = ''; + if ($this->fields) { + $expressions = array_map(function (IdentifierExpression $e) use ($binder) { + return $e->sql($binder); + }, $this->fields); + $fields = sprintf('(%s)', implode(', ', $expressions)); + } + + $suffix = $this->materialized ? $this->materialized . ' ' : ''; + + return sprintf( + '%s%s AS %s(%s)', + $this->name->sql($binder), + $fields, + $suffix, + $this->query ? $this->query->sql($binder) : '' + ); + } + + /** + * @inheritDoc + */ + public function traverse(Closure $callback) + { + $callback($this->name); + foreach ($this->fields as $field) { + $callback($field); + $field->traverse($callback); + } + + if ($this->query) { + $callback($this->query); + $this->query->traverse($callback); + } + + return $this; + } + + /** + * Clones the inner expression objects. + * + * @return void + */ + public function __clone() + { + $this->name = clone $this->name; + if ($this->query) { + $this->query = clone $this->query; + } + + foreach ($this->fields as $key => $field) { + $this->fields[$key] = clone $field; + } + } +} diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/Comparison.php b/app/vendor/cakephp/cakephp/src/Database/Expression/Comparison.php index 544805e87..11cdb984c 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Expression/Comparison.php +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/Comparison.php @@ -1,317 +1,6 @@ _type = $type; - $this->setField($field); - $this->setValue($value); - $this->_operator = $operator; - } - - /** - * Sets the value - * - * @param mixed $value The value to compare - * @return void - */ - public function setValue($value): void - { - $value = $this->_castToExpression($value, $this->_type); - - $isMultiple = $this->_type && strpos($this->_type, '[]') !== false; - if ($isMultiple) { - [$value, $this->_valueExpressions] = $this->_collectExpressions($value); - } - - $this->_isMultiple = $isMultiple; - $this->_value = $value; - } - - /** - * Returns the value used for comparison - * - * @return mixed - */ - public function getValue() - { - return $this->_value; - } - - /** - * Sets the operator to use for the comparison - * - * @param string $operator The operator to be used for the comparison. - * @return void - */ - public function setOperator(string $operator): void - { - $this->_operator = $operator; - } - - /** - * Returns the operator used for comparison - * - * @return string - */ - public function getOperator(): string - { - return $this->_operator; - } - - /** - * Convert the expression into a SQL fragment. - * - * @param \Cake\Database\ValueBinder $generator Placeholder generator object - * @return string - */ - public function sql(ValueBinder $generator): string - { - /** @var string|\Cake\Database\ExpressionInterface $field */ - $field = $this->_field; - - if ($field instanceof ExpressionInterface) { - $field = $field->sql($generator); - } - - if ($this->_value instanceof ExpressionInterface) { - $template = '%s %s (%s)'; - $value = $this->_value->sql($generator); - } else { - [$template, $value] = $this->_stringExpression($generator); - } - - return sprintf($template, $field, $this->_operator, $value); - } - - /** - * @inheritDoc - */ - public function traverse(Closure $visitor) - { - if ($this->_field instanceof ExpressionInterface) { - $visitor($this->_field); - $this->_field->traverse($visitor); - } - - if ($this->_value instanceof ExpressionInterface) { - $visitor($this->_value); - $this->_value->traverse($visitor); - } - - foreach ($this->_valueExpressions as $v) { - $visitor($v); - $v->traverse($visitor); - } - - return $this; - } - - /** - * Create a deep clone. - * - * Clones the field and value if they are expression objects. - * - * @return void - */ - public function __clone() - { - foreach (['_value', '_field'] as $prop) { - if ($this->{$prop} instanceof ExpressionInterface) { - $this->{$prop} = clone $this->{$prop}; - } - } - } - - /** - * Returns a template and a placeholder for the value after registering it - * with the placeholder $generator - * - * @param \Cake\Database\ValueBinder $generator The value binder to use. - * @return array First position containing the template and the second a placeholder - */ - protected function _stringExpression(ValueBinder $generator): array - { - $template = '%s '; - - if ($this->_field instanceof ExpressionInterface) { - $template = '(%s) '; - } - - if ($this->_isMultiple) { - $template .= '%s (%s)'; - $type = $this->_type; - if ($type !== null) { - $type = str_replace('[]', '', $type); - } - $value = $this->_flattenValue($this->_value, $generator, $type); - - // To avoid SQL errors when comparing a field to a list of empty values, - // better just throw an exception here - if ($value === '') { - $field = $this->_field instanceof ExpressionInterface ? $this->_field->sql($generator) : $this->_field; - /** @psalm-suppress PossiblyInvalidCast */ - throw new DatabaseException( - "Impossible to generate condition with empty list of values for field ($field)" - ); - } - } else { - $template .= '%s %s'; - $value = $this->_bindValue($this->_value, $generator, $this->_type); - } - - return [$template, $value]; - } - - /** - * Registers a value in the placeholder generator and returns the generated placeholder - * - * @param mixed $value The value to bind - * @param \Cake\Database\ValueBinder $generator The value binder to use - * @param string|null $type The type of $value - * @return string generated placeholder - */ - protected function _bindValue($value, ValueBinder $generator, ?string $type = null): string - { - $placeholder = $generator->placeholder('c'); - $generator->bind($placeholder, $value, $type); - - return $placeholder; - } - - /** - * Converts a traversable value into a set of placeholders generated by - * $generator and separated by `,` - * - * @param iterable $value the value to flatten - * @param \Cake\Database\ValueBinder $generator The value binder to use - * @param string|null $type the type to cast values to - * @return string - */ - protected function _flattenValue(iterable $value, ValueBinder $generator, ?string $type = null): string - { - $parts = []; - if (is_array($value)) { - foreach ($this->_valueExpressions as $k => $v) { - $parts[$k] = $v->sql($generator); - unset($value[$k]); - } - } - - if (!empty($value)) { - $parts += $generator->generateManyNamed($value, $type); - } - - return implode(',', $parts); - } - - /** - * Returns an array with the original $values in the first position - * and all ExpressionInterface objects that could be found in the second - * position. - * - * @param iterable|\Cake\Database\ExpressionInterface $values The rows to insert - * @return array - */ - protected function _collectExpressions($values): array - { - if ($values instanceof ExpressionInterface) { - return [$values, []]; - } - - $expressions = $result = []; - $isArray = is_array($values); - - if ($isArray) { - /** @var array $result */ - $result = $values; - } - - foreach ($values as $k => $v) { - if ($v instanceof ExpressionInterface) { - $expressions[$k] = $v; - } - - if ($isArray) { - $result[$k] = $v; - } - } - - return [$result, $expressions]; - } -} +deprecationWarning('`Comparison` deprecated in 4.1.0, use `ComparisonExpression` instead.'); diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/ComparisonExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/ComparisonExpression.php new file mode 100644 index 000000000..2eba1deec --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/ComparisonExpression.php @@ -0,0 +1,320 @@ +_type = $type; + $this->setField($field); + $this->setValue($value); + $this->_operator = $operator; + } + + /** + * Sets the value + * + * @param mixed $value The value to compare + * @return void + */ + public function setValue($value): void + { + $value = $this->_castToExpression($value, $this->_type); + + $isMultiple = $this->_type && strpos($this->_type, '[]') !== false; + if ($isMultiple) { + [$value, $this->_valueExpressions] = $this->_collectExpressions($value); + } + + $this->_isMultiple = $isMultiple; + $this->_value = $value; + } + + /** + * Returns the value used for comparison + * + * @return mixed + */ + public function getValue() + { + return $this->_value; + } + + /** + * Sets the operator to use for the comparison + * + * @param string $operator The operator to be used for the comparison. + * @return void + */ + public function setOperator(string $operator): void + { + $this->_operator = $operator; + } + + /** + * Returns the operator used for comparison + * + * @return string + */ + public function getOperator(): string + { + return $this->_operator; + } + + /** + * @inheritDoc + */ + public function sql(ValueBinder $binder): string + { + /** @var string|\Cake\Database\ExpressionInterface $field */ + $field = $this->_field; + + if ($field instanceof ExpressionInterface) { + $field = $field->sql($binder); + } + + if ($this->_value instanceof ExpressionInterface) { + $template = '%s %s (%s)'; + $value = $this->_value->sql($binder); + } else { + [$template, $value] = $this->_stringExpression($binder); + } + + return sprintf($template, $field, $this->_operator, $value); + } + + /** + * @inheritDoc + */ + public function traverse(Closure $callback) + { + if ($this->_field instanceof ExpressionInterface) { + $callback($this->_field); + $this->_field->traverse($callback); + } + + if ($this->_value instanceof ExpressionInterface) { + $callback($this->_value); + $this->_value->traverse($callback); + } + + foreach ($this->_valueExpressions as $v) { + $callback($v); + $v->traverse($callback); + } + + return $this; + } + + /** + * Create a deep clone. + * + * Clones the field and value if they are expression objects. + * + * @return void + */ + public function __clone() + { + foreach (['_value', '_field'] as $prop) { + if ($this->{$prop} instanceof ExpressionInterface) { + $this->{$prop} = clone $this->{$prop}; + } + } + } + + /** + * Returns a template and a placeholder for the value after registering it + * with the placeholder $binder + * + * @param \Cake\Database\ValueBinder $binder The value binder to use. + * @return array First position containing the template and the second a placeholder + */ + protected function _stringExpression(ValueBinder $binder): array + { + $template = '%s '; + + if ($this->_field instanceof ExpressionInterface) { + $template = '(%s) '; + } + + if ($this->_isMultiple) { + $template .= '%s (%s)'; + $type = $this->_type; + if ($type !== null) { + $type = str_replace('[]', '', $type); + } + $value = $this->_flattenValue($this->_value, $binder, $type); + + // To avoid SQL errors when comparing a field to a list of empty values, + // better just throw an exception here + if ($value === '') { + $field = $this->_field instanceof ExpressionInterface ? $this->_field->sql($binder) : $this->_field; + /** @psalm-suppress PossiblyInvalidCast */ + throw new DatabaseException( + "Impossible to generate condition with empty list of values for field ($field)" + ); + } + } else { + $template .= '%s %s'; + $value = $this->_bindValue($this->_value, $binder, $this->_type); + } + + return [$template, $value]; + } + + /** + * Registers a value in the placeholder generator and returns the generated placeholder + * + * @param mixed $value The value to bind + * @param \Cake\Database\ValueBinder $binder The value binder to use + * @param string|null $type The type of $value + * @return string generated placeholder + */ + protected function _bindValue($value, ValueBinder $binder, ?string $type = null): string + { + $placeholder = $binder->placeholder('c'); + $binder->bind($placeholder, $value, $type); + + return $placeholder; + } + + /** + * Converts a traversable value into a set of placeholders generated by + * $binder and separated by `,` + * + * @param iterable $value the value to flatten + * @param \Cake\Database\ValueBinder $binder The value binder to use + * @param string|null $type the type to cast values to + * @return string + */ + protected function _flattenValue(iterable $value, ValueBinder $binder, ?string $type = null): string + { + $parts = []; + if (is_array($value)) { + foreach ($this->_valueExpressions as $k => $v) { + $parts[$k] = $v->sql($binder); + unset($value[$k]); + } + } + + if (!empty($value)) { + $parts += $binder->generateManyNamed($value, $type); + } + + return implode(',', $parts); + } + + /** + * Returns an array with the original $values in the first position + * and all ExpressionInterface objects that could be found in the second + * position. + * + * @param iterable|\Cake\Database\ExpressionInterface $values The rows to insert + * @return array + */ + protected function _collectExpressions($values): array + { + if ($values instanceof ExpressionInterface) { + return [$values, []]; + } + + $expressions = $result = []; + $isArray = is_array($values); + + if ($isArray) { + /** @var array $result */ + $result = $values; + } + + foreach ($values as $k => $v) { + if ($v instanceof ExpressionInterface) { + $expressions[$k] = $v; + } + + if ($isArray) { + $result[$k] = $v; + } + } + + return [$result, $expressions]; + } +} + +// phpcs:disable +// Comparison will not load during instanceof checks so ensure it's loaded here +// @deprecated 4.1.0 Add backwards compatible alias. +class_alias('Cake\Database\Expression\ComparisonExpression', 'Cake\Database\Expression\Comparison'); +// phpcs:enable diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/FunctionExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/FunctionExpression.php index 81ab886bb..217e1ebd0 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Expression/FunctionExpression.php +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/FunctionExpression.php @@ -99,7 +99,7 @@ public function getName(): string /** * Adds one or more arguments for the function call. * - * @param array $params list of arguments to be passed to the function + * @param array $conditions list of arguments to be passed to the function * If associative the key would be used as argument when value is 'literal' * @param array $types associative array of types to be associated with the * passed arguments @@ -108,11 +108,11 @@ public function getName(): string * @return $this * @psalm-suppress MoreSpecificImplementedParamType */ - public function add($params, array $types = [], bool $prepend = false) + public function add($conditions, array $types = [], bool $prepend = false) { $put = $prepend ? 'array_unshift' : 'array_push'; $typeMap = $this->getTypeMap()->setTypes($types); - foreach ($params as $k => $p) { + foreach ($conditions as $k => $p) { if ($p === 'literal') { $put($this->_conditions, $k); continue; @@ -141,25 +141,19 @@ public function add($params, array $types = [], bool $prepend = false) } /** - * Returns the string representation of this object so that it can be used in a - * SQL query. Note that values condition values are not included in the string, - * in their place placeholders are put and can be replaced by the quoted values - * accordingly. - * - * @param \Cake\Database\ValueBinder $generator Placeholder generator object - * @return string + * @inheritDoc */ - public function sql(ValueBinder $generator): string + public function sql(ValueBinder $binder): string { $parts = []; foreach ($this->_conditions as $condition) { if ($condition instanceof Query) { - $condition = sprintf('(%s)', $condition->sql($generator)); + $condition = sprintf('(%s)', $condition->sql($binder)); } elseif ($condition instanceof ExpressionInterface) { - $condition = $condition->sql($generator); + $condition = $condition->sql($binder); } elseif (is_array($condition)) { - $p = $generator->placeholder('param'); - $generator->bind($p, $condition['value'], $condition['type']); + $p = $binder->placeholder('param'); + $binder->bind($p, $condition['value'], $condition['type']); $condition = $p; } $parts[] = $condition; diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/IdentifierExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/IdentifierExpression.php index 9b97879ca..69486a41f 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Expression/IdentifierExpression.php +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/IdentifierExpression.php @@ -37,14 +37,21 @@ class IdentifierExpression implements ExpressionInterface */ protected $_identifier; + /** + * @var string|null + */ + protected $collation; + /** * Constructor * * @param string $identifier The identifier this expression represents + * @param string|null $collation The identifier collation */ - public function __construct(string $identifier) + public function __construct(string $identifier, ?string $collation = null) { $this->_identifier = $identifier; + $this->collation = $collation; } /** @@ -69,20 +76,43 @@ public function getIdentifier(): string } /** - * Converts the expression to its string representation + * Sets the collation. * - * @param \Cake\Database\ValueBinder $generator Placeholder generator object - * @return string + * @param string $collation Identifier collation + * @return void */ - public function sql(ValueBinder $generator): string + public function setCollation(string $collation): void { - return $this->_identifier; + $this->collation = $collation; + } + + /** + * Returns the collation. + * + * @return string|null + */ + public function getCollation(): ?string + { + return $this->collation; + } + + /** + * @inheritDoc + */ + public function sql(ValueBinder $binder): string + { + $sql = $this->_identifier; + if ($this->collation) { + $sql .= ' COLLATE ' . $this->collation; + } + + return $sql; } /** * @inheritDoc */ - public function traverse(Closure $visitor) + public function traverse(Closure $callback) { return $this; } diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/OrderByExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/OrderByExpression.php index 2aaa6a10f..dd7381144 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Expression/OrderByExpression.php +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/OrderByExpression.php @@ -38,17 +38,14 @@ public function __construct($conditions = [], $types = [], $conjunction = '') } /** - * Convert the expression into a SQL fragment. - * - * @param \Cake\Database\ValueBinder $generator Placeholder generator object - * @return string + * @inheritDoc */ - public function sql(ValueBinder $generator): string + public function sql(ValueBinder $binder): string { $order = []; foreach ($this->_conditions as $k => $direction) { if ($direction instanceof ExpressionInterface) { - $direction = $direction->sql($generator); + $direction = $direction->sql($binder); } $order[] = is_numeric($k) ? $direction : sprintf('%s %s', $k, $direction); } @@ -62,26 +59,30 @@ public function sql(ValueBinder $generator): string * * New order by expressions are merged to existing ones * - * @param array $orders list of order by expressions + * @param array $conditions list of order by expressions * @param array $types list of types associated on fields referenced in $conditions * @return void */ - protected function _addConditions(array $orders, array $types): void + protected function _addConditions(array $conditions, array $types): void { - foreach ($orders as $key => $val) { + foreach ($conditions as $key => $val) { if ( is_string($key) && is_string($val) && !in_array(strtoupper($val), ['ASC', 'DESC'], true) ) { throw new RuntimeException( - 'Passing extra expressions by associative array is not ' . - 'allowed to avoid potential SQL injection. ' . - 'Use QueryExpression or numeric array instead.' + sprintf( + 'Passing extra expressions by associative array (`\'%s\' => \'%s\'`) ' . + 'is not allowed to avoid potential SQL injection. ' . + 'Use QueryExpression or numeric array instead.', + $key, + $val + ) ); } } - $this->_conditions = array_merge($this->_conditions, $orders); + $this->_conditions = array_merge($this->_conditions, $conditions); } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/OrderClauseExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/OrderClauseExpression.php index 5a6aec266..5e7b84058 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Expression/OrderClauseExpression.php +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/OrderClauseExpression.php @@ -17,6 +17,7 @@ namespace Cake\Database\Expression; use Cake\Database\ExpressionInterface; +use Cake\Database\Query; use Cake\Database\ValueBinder; use Closure; @@ -49,12 +50,14 @@ public function __construct($field, $direction) /** * @inheritDoc */ - public function sql(ValueBinder $generator): string + public function sql(ValueBinder $binder): string { /** @var string|\Cake\Database\ExpressionInterface $field */ $field = $this->_field; - if ($field instanceof ExpressionInterface) { - $field = $field->sql($generator); + if ($field instanceof Query) { + $field = sprintf('(%s)', $field->sql($binder)); + } elseif ($field instanceof ExpressionInterface) { + $field = $field->sql($binder); } return sprintf('%s %s', $field, $this->_direction); @@ -63,11 +66,11 @@ public function sql(ValueBinder $generator): string /** * @inheritDoc */ - public function traverse(Closure $visitor) + public function traverse(Closure $callback) { if ($this->_field instanceof ExpressionInterface) { - $visitor($this->_field); - $this->_field->traverse($visitor); + $callback($this->_field); + $this->_field->traverse($callback); } return $this; diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/QueryExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/QueryExpression.php index 0bf3d08ef..3f3ec4ff8 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Expression/QueryExpression.php +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/QueryExpression.php @@ -151,7 +151,7 @@ public function eq($field, $value, ?string $type = null) $type = $this->_calculateType($field); } - return $this->add(new Comparison($field, $value, $type, '=')); + return $this->add(new ComparisonExpression($field, $value, $type, '=')); } /** @@ -170,7 +170,7 @@ public function notEq($field, $value, $type = null) $type = $this->_calculateType($field); } - return $this->add(new Comparison($field, $value, $type, '!=')); + return $this->add(new ComparisonExpression($field, $value, $type, '!=')); } /** @@ -187,7 +187,7 @@ public function gt($field, $value, $type = null) $type = $this->_calculateType($field); } - return $this->add(new Comparison($field, $value, $type, '>')); + return $this->add(new ComparisonExpression($field, $value, $type, '>')); } /** @@ -204,7 +204,7 @@ public function lt($field, $value, $type = null) $type = $this->_calculateType($field); } - return $this->add(new Comparison($field, $value, $type, '<')); + return $this->add(new ComparisonExpression($field, $value, $type, '<')); } /** @@ -221,7 +221,7 @@ public function gte($field, $value, $type = null) $type = $this->_calculateType($field); } - return $this->add(new Comparison($field, $value, $type, '>=')); + return $this->add(new ComparisonExpression($field, $value, $type, '>=')); } /** @@ -238,7 +238,7 @@ public function lte($field, $value, $type = null) $type = $this->_calculateType($field); } - return $this->add(new Comparison($field, $value, $type, '<=')); + return $this->add(new ComparisonExpression($field, $value, $type, '<=')); } /** @@ -287,7 +287,7 @@ public function like($field, $value, $type = null) $type = $this->_calculateType($field); } - return $this->add(new Comparison($field, $value, $type, 'LIKE')); + return $this->add(new ComparisonExpression($field, $value, $type, 'LIKE')); } /** @@ -304,7 +304,7 @@ public function notLike($field, $value, $type = null) $type = $this->_calculateType($field); } - return $this->add(new Comparison($field, $value, $type, 'NOT LIKE')); + return $this->add(new ComparisonExpression($field, $value, $type, 'NOT LIKE')); } /** @@ -325,7 +325,7 @@ public function in($field, $values, $type = null) $type .= '[]'; $values = $values instanceof ExpressionInterface ? $values : (array)$values; - return $this->add(new Comparison($field, $values, $type, 'IN')); + return $this->add(new ComparisonExpression($field, $values, $type, 'IN')); } /** @@ -350,7 +350,7 @@ public function addCase($conditions, $values = [], $types = []) * "field NOT IN (value1, value2)". * * @param string|\Cake\Database\ExpressionInterface $field Database field to be compared against value - * @param array|\Cake\Database\ExpressionInterface $values the value to be bound to $field for comparison + * @param string|array|\Cake\Database\ExpressionInterface $values the value to be bound to $field for comparison * @param string|null $type the type name for $value as configured using the Type map. * @return $this */ @@ -363,29 +363,29 @@ public function notIn($field, $values, $type = null) $type .= '[]'; $values = $values instanceof ExpressionInterface ? $values : (array)$values; - return $this->add(new Comparison($field, $values, $type, 'NOT IN')); + return $this->add(new ComparisonExpression($field, $values, $type, 'NOT IN')); } /** * Adds a new condition to the expression object in the form "EXISTS (...)". * - * @param \Cake\Database\ExpressionInterface $query the inner query + * @param \Cake\Database\ExpressionInterface $expression the inner query * @return $this */ - public function exists(ExpressionInterface $query) + public function exists(ExpressionInterface $expression) { - return $this->add(new UnaryExpression('EXISTS', $query, UnaryExpression::PREFIX)); + return $this->add(new UnaryExpression('EXISTS', $expression, UnaryExpression::PREFIX)); } /** * Adds a new condition to the expression object in the form "NOT EXISTS (...)". * - * @param \Cake\Database\ExpressionInterface $query the inner query + * @param \Cake\Database\ExpressionInterface $expression the inner query * @return $this */ - public function notExists(ExpressionInterface $query) + public function notExists(ExpressionInterface $expression) { - return $this->add(new UnaryExpression('NOT EXISTS', $query, UnaryExpression::PREFIX)); + return $this->add(new UnaryExpression('NOT EXISTS', $expression, UnaryExpression::PREFIX)); } /** @@ -443,7 +443,8 @@ public function or($conditions, $types = []) return new static($conditions, $this->getTypeMap()->setTypes($types), 'OR'); } -// phpcs:disable + // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps + /** * Returns a new QueryExpression object containing all the conditions passed * and set up the conjunction to be "AND" @@ -456,6 +457,8 @@ public function or($conditions, $types = []) */ public function and_($conditions, $types = []) { + deprecationWarning('QueryExpression::and_() is deprecated use and() instead.'); + return $this->and($conditions, $types); } @@ -471,9 +474,12 @@ public function and_($conditions, $types = []) */ public function or_($conditions, $types = []) { + deprecationWarning('QueryExpression::or_() is deprecated use or() instead.'); + return $this->or($conditions, $types); } -// phpcs:enable + + // phpcs:enable /** * Adds a new set of conditions to this level of the tree and negates @@ -481,7 +487,7 @@ public function or_($conditions, $types = []) * "NOT ( (condition1) AND (conditions2) )" conjunction depends on the one * currently configured for this object. * - * @param string|array|\Cake\Database\ExpressionInterface $conditions to be added and negated + * @param string|array|\Closure|\Cake\Database\ExpressionInterface $conditions to be added and negated * @param array $types associative array of fields pointing to the type of the * values that are being passed. Used for correctly binding values to statements. * @return $this @@ -506,11 +512,11 @@ public function count(): int /** * Builds equal condition or assignment with identifier wrapping. * - * @param string $left Left join condition field name. - * @param string $right Right join condition field name. + * @param string $leftField Left join condition field name. + * @param string $rightField Right join condition field name. * @return $this */ - public function equalFields(string $left, string $right) + public function equalFields(string $leftField, string $rightField) { $wrapIdentifier = function ($field) { if ($field instanceof ExpressionInterface) { @@ -520,19 +526,13 @@ public function equalFields(string $left, string $right) return new IdentifierExpression($field); }; - return $this->eq($wrapIdentifier($left), $wrapIdentifier($right)); + return $this->eq($wrapIdentifier($leftField), $wrapIdentifier($rightField)); } /** - * Returns the string representation of this object so that it can be used in a - * SQL query. Note that values condition values are not included in the string, - * in their place placeholders are put and can be replaced by the quoted values - * accordingly. - * - * @param \Cake\Database\ValueBinder $generator Placeholder generator object - * @return string + * @inheritDoc */ - public function sql(ValueBinder $generator): string + public function sql(ValueBinder $binder): string { $len = $this->count(); if ($len === 0) { @@ -543,9 +543,9 @@ public function sql(ValueBinder $generator): string $parts = []; foreach ($this->_conditions as $part) { if ($part instanceof Query) { - $part = '(' . $part->sql($generator) . ')'; + $part = '(' . $part->sql($binder) . ')'; } elseif ($part instanceof ExpressionInterface) { - $part = $part->sql($generator); + $part = $part->sql($binder); } if (strlen($part)) { $parts[] = $part; @@ -556,22 +556,14 @@ public function sql(ValueBinder $generator): string } /** - * Traverses the tree structure of this query expression by executing a callback - * function for each of the conditions that are included in this object. - * Useful for compiling the final expression, or doing - * introspection in the structure. - * - * Callback function receives as only argument an instance of ExpressionInterface - * - * @param \Closure $visitor The callable to apply to all sub-expressions. - * @return $this + * @inheritDoc */ - public function traverse(Closure $visitor) + public function traverse(Closure $callback) { foreach ($this->_conditions as $c) { if ($c instanceof ExpressionInterface) { - $visitor($c); - $c->traverse($visitor); + $callback($c); + $c->traverse($callback); } } @@ -590,15 +582,15 @@ public function traverse(Closure $visitor) * passed by reference, this will enable you to change the key under which the * modified part is stored. * - * @param callable $visitor The callable to apply to each part. + * @param callable $callback The callable to apply to each part. * @return $this */ - public function iterateParts(callable $visitor) + public function iterateParts(callable $callback) { $parts = []; foreach ($this->_conditions as $k => $c) { $key = &$k; - $part = $visitor($c, $key); + $part = $callback($c, $key); if ($part !== null) { $parts[$key] = $part; } @@ -615,19 +607,21 @@ public function iterateParts(callable $visitor) * as they often contain user input and arrays of strings * are easy to sneak in. * - * @param callable|string|array|\Cake\Database\ExpressionInterface $c The callable to check. + * @param callable|string|array|\Cake\Database\ExpressionInterface $callable The callable to check. * @return bool Valid callable. + * @deprecated 4.2.0 This method is unused. + * @codeCoverageIgnore */ - public function isCallable($c): bool + public function isCallable($callable): bool { - if (is_string($c)) { + if (is_string($callable)) { return false; } - if (is_object($c) && is_callable($c)) { + if (is_object($callable) && is_callable($callable)) { return true; } - return is_array($c) && isset($c[0]) && is_object($c[0]) && is_callable($c); + return is_array($callable) && isset($callable[0]) && is_object($callable[0]) && is_callable($callable); } /** @@ -720,7 +714,7 @@ protected function _addConditions(array $conditions, array $types): void * generating the placeholders and replacing the values by them, while storing * the value elsewhere for future binding. * - * @param string $field The value from with the actual field and operator will + * @param string $field The value from which the actual field and operator will * be extracted. * @param mixed $value The value to be bound to a placeholder for the field * @return string|\Cake\Database\ExpressionInterface @@ -728,16 +722,27 @@ protected function _addConditions(array $conditions, array $types): void */ protected function _parseCondition(string $field, $value) { + $field = trim($field); $operator = '='; $expression = $field; - $parts = explode(' ', trim($field), 2); - if (count($parts) > 1) { + $spaces = substr_count($field, ' '); + // Handle operators with a space in them like `is not` and `not like` + if ($spaces > 1) { + $parts = explode(' ', $field); + if (preg_match('/(is not|not \w+)$/i', $field)) { + $last = array_pop($parts); + $second = array_pop($parts); + array_push($parts, strtolower("{$second} {$last}")); + } + $operator = array_pop($parts); + $expression = implode(' ', $parts); + } elseif ($spaces == 1) { + $parts = explode(' ', $field, 2); [$expression, $operator] = $parts; + $operator = strtolower(trim($operator)); } - $type = $this->getTypeMap()->type($expression); - $operator = strtolower(trim($operator)); $typeMultiple = (is_string($type) && strpos($type, '[]') !== false); if (in_array($operator, ['in', 'not in']) || $typeMultiple) { @@ -779,10 +784,12 @@ protected function _parseCondition(string $field, $value) } if ($value === null && $this->_conjunction !== ',') { - throw new InvalidArgumentException('Expression is missing operator (IS, IS NOT) with `null` value.'); + throw new InvalidArgumentException( + sprintf('Expression `%s` is missing operator (IS, IS NOT) with `null` value.', $expression) + ); } - return new Comparison($expression, $value, $type, $operator); + return new ComparisonExpression($expression, $value, $type, $operator); } /** diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/StringExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/StringExpression.php new file mode 100644 index 000000000..e4380744b --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/StringExpression.php @@ -0,0 +1,87 @@ +string = $string; + $this->collation = $collation; + } + + /** + * Sets the string collation. + * + * @param string $collation String collation + * @return void + */ + public function setCollation(string $collation): void + { + $this->collation = $collation; + } + + /** + * Returns the string collation. + * + * @return string + */ + public function getCollation(): string + { + return $this->collation; + } + + /** + * @inheritDoc + */ + public function sql(ValueBinder $binder): string + { + $placeholder = $binder->placeholder('c'); + $binder->bind($placeholder, $this->string, 'string'); + + return $placeholder . ' COLLATE ' . $this->collation; + } + + /** + * @inheritDoc + */ + public function traverse(Closure $callback) + { + return $this; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/TupleComparison.php b/app/vendor/cakephp/cakephp/src/Database/Expression/TupleComparison.php index 322e01fd7..8896e337e 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Expression/TupleComparison.php +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/TupleComparison.php @@ -24,12 +24,13 @@ * This expression represents SQL fragments that are used for comparing one tuple * to another, one tuple to a set of other tuples or one tuple to an expression */ -class TupleComparison extends Comparison +class TupleComparison extends ComparisonExpression { /** * The type to be used for casting the value to a database representation * - * @var array + * @var array + * @psalm-suppress NonInvariantDocblockPropertyType */ protected $_type; @@ -38,7 +39,7 @@ class TupleComparison extends Comparison * * @param string|array|\Cake\Database\ExpressionInterface $fields the fields to use to form a tuple * @param array|\Cake\Database\ExpressionInterface $values the values to use to form a tuple - * @param array $types the types names to use for casting each of the values, only + * @param array $types the types names to use for casting each of the values, only * one type per position in the value array in needed * @param string $conjunction the operator used for comparing field and value */ @@ -50,6 +51,16 @@ public function __construct($fields, $values, array $types = [], string $conjunc $this->_operator = $conjunction; } + /** + * Returns the type to be used for casting the value to a database representation + * + * @return array + */ + public function getType(): array + { + return $this->_type; + } + /** * Sets the value * @@ -62,12 +73,9 @@ public function setValue($value): void } /** - * Convert the expression into a SQL fragment. - * - * @param \Cake\Database\ValueBinder $generator Placeholder generator object - * @return string + * @inheritDoc */ - public function sql(ValueBinder $generator): string + public function sql(ValueBinder $binder): string { $template = '(%s) %s (%s)'; $fields = []; @@ -78,10 +86,10 @@ public function sql(ValueBinder $generator): string } foreach ($originalFields as $field) { - $fields[] = $field instanceof ExpressionInterface ? $field->sql($generator) : $field; + $fields[] = $field instanceof ExpressionInterface ? $field->sql($binder) : $field; } - $values = $this->_stringifyValues($generator); + $values = $this->_stringifyValues($binder); $field = implode(', ', $fields); @@ -92,21 +100,21 @@ public function sql(ValueBinder $generator): string * Returns a string with the values as placeholders in a string to be used * for the SQL version of this expression * - * @param \Cake\Database\ValueBinder $generator The value binder to convert expressions with. + * @param \Cake\Database\ValueBinder $binder The value binder to convert expressions with. * @return string */ - protected function _stringifyValues(ValueBinder $generator): string + protected function _stringifyValues(ValueBinder $binder): string { $values = []; $parts = $this->getValue(); if ($parts instanceof ExpressionInterface) { - return $parts->sql($generator); + return $parts->sql($binder); } foreach ($parts as $i => $value) { if ($value instanceof ExpressionInterface) { - $values[] = $value->sql($generator); + $values[] = $value->sql($binder); continue; } @@ -121,7 +129,7 @@ protected function _stringifyValues(ValueBinder $generator): string foreach ($value as $k => $val) { /** @var string $valType */ $valType = $type && isset($type[$k]) ? $type[$k] : $type; - $bound[] = $this->_bindValue($val, $generator, $valType); + $bound[] = $this->_bindValue($val, $binder, $valType); } $values[] = sprintf('(%s)', implode(',', $bound)); @@ -130,7 +138,7 @@ protected function _stringifyValues(ValueBinder $generator): string /** @var string $valType */ $valType = $type && isset($type[$i]) ? $type[$i] : $type; - $values[] = $this->_bindValue($value, $generator, $valType); + $values[] = $this->_bindValue($value, $binder, $valType); } return implode(', ', $values); @@ -139,35 +147,29 @@ protected function _stringifyValues(ValueBinder $generator): string /** * @inheritDoc */ - protected function _bindValue($value, ValueBinder $generator, ?string $type = null): string + protected function _bindValue($value, ValueBinder $binder, ?string $type = null): string { - $placeholder = $generator->placeholder('tuple'); - $generator->bind($placeholder, $value, $type); + $placeholder = $binder->placeholder('tuple'); + $binder->bind($placeholder, $value, $type); return $placeholder; } /** - * Traverses the tree of expressions stored in this object, visiting first - * expressions in the left hand side and then the rest. - * - * Callback function receives as its only argument an instance of an ExpressionInterface - * - * @param \Closure $visitor The callable to apply to sub-expressions - * @return $this + * @inheritDoc */ - public function traverse(Closure $visitor) + public function traverse(Closure $callback) { /** @var string[] $fields */ $fields = $this->getField(); foreach ($fields as $field) { - $this->_traverseValue($field, $visitor); + $this->_traverseValue($field, $callback); } $value = $this->getValue(); if ($value instanceof ExpressionInterface) { - $visitor($value); - $value->traverse($visitor); + $callback($value); + $value->traverse($callback); return $this; } @@ -175,10 +177,10 @@ public function traverse(Closure $visitor) foreach ($value as $val) { if ($this->isMulti()) { foreach ($val as $v) { - $this->_traverseValue($v, $visitor); + $this->_traverseValue($v, $callback); } } else { - $this->_traverseValue($val, $visitor); + $this->_traverseValue($val, $callback); } } @@ -190,14 +192,14 @@ public function traverse(Closure $visitor) * it is an ExpressionInterface * * @param mixed $value The value to traverse - * @param \Closure $callable The callable to use when traversing + * @param \Closure $callback The callable to use when traversing * @return void */ - protected function _traverseValue($value, Closure $callable): void + protected function _traverseValue($value, Closure $callback): void { if ($value instanceof ExpressionInterface) { - $callable($value); - $value->traverse($callable); + $callback($value); + $value->traverse($callback); } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/UnaryExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/UnaryExpression.php index dc42cff5b..1c40f913f 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Expression/UnaryExpression.php +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/UnaryExpression.php @@ -58,36 +58,33 @@ class UnaryExpression implements ExpressionInterface * * @var int */ - protected $_mode; + protected $position; /** * Constructor * * @param string $operator The operator to used for the expression * @param mixed $value the value to use as the operand for the expression - * @param int $mode either UnaryExpression::PREFIX or UnaryExpression::POSTFIX + * @param int $position either UnaryExpression::PREFIX or UnaryExpression::POSTFIX */ - public function __construct(string $operator, $value, $mode = self::PREFIX) + public function __construct(string $operator, $value, $position = self::PREFIX) { $this->_operator = $operator; $this->_value = $value; - $this->_mode = $mode; + $this->position = $position; } /** - * Converts the expression to its string representation - * - * @param \Cake\Database\ValueBinder $generator Placeholder generator object - * @return string + * @inheritDoc */ - public function sql(ValueBinder $generator): string + public function sql(ValueBinder $binder): string { $operand = $this->_value; if ($operand instanceof ExpressionInterface) { - $operand = $operand->sql($generator); + $operand = $operand->sql($binder); } - if ($this->_mode === self::POSTFIX) { + if ($this->position === self::POSTFIX) { return '(' . $operand . ') ' . $this->_operator; } @@ -97,11 +94,11 @@ public function sql(ValueBinder $generator): string /** * @inheritDoc */ - public function traverse(Closure $visitor) + public function traverse(Closure $callback) { if ($this->_value instanceof ExpressionInterface) { - $visitor($this->_value); - $this->_value->traverse($visitor); + $callback($this->_value); + $this->_value->traverse($callback); } return $this; diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/ValuesExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/ValuesExpression.php index 57113ed83..8219de365 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Expression/ValuesExpression.php +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/ValuesExpression.php @@ -16,7 +16,7 @@ */ namespace Cake\Database\Expression; -use Cake\Database\Exception; +use Cake\Database\Exception\DatabaseException; use Cake\Database\ExpressionInterface; use Cake\Database\Query; use Cake\Database\Type\ExpressionTypeCasterTrait; @@ -80,45 +80,45 @@ public function __construct(array $columns, TypeMap $typeMap) /** * Add a row of data to be inserted. * - * @param array|\Cake\Database\Query $data Array of data to append into the insert, or + * @param array|\Cake\Database\Query $values Array of data to append into the insert, or * a query for doing INSERT INTO .. SELECT style commands * @return void - * @throws \Cake\Database\Exception When mixing array + Query data types. + * @throws \Cake\Database\Exception\DatabaseException When mixing array + Query data types. */ - public function add($data): void + public function add($values): void { if ( ( count($this->_values) && - $data instanceof Query + $values instanceof Query ) || ( $this->_query && - is_array($data) + is_array($values) ) ) { - throw new Exception( - 'You cannot mix subqueries and array data in inserts.' + throw new DatabaseException( + 'You cannot mix subqueries and array values in inserts.' ); } - if ($data instanceof Query) { - $this->setQuery($data); + if ($values instanceof Query) { + $this->setQuery($values); return; } - $this->_values[] = $data; + $this->_values[] = $values; $this->_castedExpressions = false; } /** * Sets the columns to be inserted. * - * @param array $cols Array with columns to be inserted. + * @param array $columns Array with columns to be inserted. * @return $this */ - public function setColumns(array $cols) + public function setColumns(array $columns) { - $this->_columns = $cols; + $this->_columns = $columns; $this->_castedExpressions = false; return $this; @@ -209,12 +209,9 @@ public function getQuery(): ?Query } /** - * Convert the values into a SQL string with placeholders. - * - * @param \Cake\Database\ValueBinder $generator Placeholder generator object - * @return string + * @inheritDoc */ - public function sql(ValueBinder $generator): string + public function sql(ValueBinder $binder): string { if (empty($this->_values) && empty($this->_query)) { return ''; @@ -242,13 +239,13 @@ public function sql(ValueBinder $generator): string $value = $row[$column]; if ($value instanceof ExpressionInterface) { - $rowPlaceholders[] = '(' . $value->sql($generator) . ')'; + $rowPlaceholders[] = '(' . $value->sql($binder) . ')'; continue; } - $placeholder = $generator->placeholder('c'); + $placeholder = $binder->placeholder('c'); $rowPlaceholders[] = $placeholder; - $generator->bind($placeholder, $value, $types[$column]); + $binder->bind($placeholder, $value, $types[$column]); } $placeholders[] = implode(', ', $rowPlaceholders); @@ -256,22 +253,16 @@ public function sql(ValueBinder $generator): string $query = $this->getQuery(); if ($query) { - return ' ' . $query->sql($generator); + return ' ' . $query->sql($binder); } return sprintf(' VALUES (%s)', implode('), (', $placeholders)); } /** - * Traverse the values expression. - * - * This method will also traverse any queries that are to be used in the INSERT - * values. - * - * @param \Closure $visitor The visitor to traverse the expression with. - * @return $this + * @inheritDoc */ - public function traverse(Closure $visitor) + public function traverse(Closure $callback) { if ($this->_query) { return $this; @@ -283,15 +274,15 @@ public function traverse(Closure $visitor) foreach ($this->_values as $v) { if ($v instanceof ExpressionInterface) { - $v->traverse($visitor); + $v->traverse($callback); } if (!is_array($v)) { continue; } foreach ($v as $field) { if ($field instanceof ExpressionInterface) { - $visitor($field); - $field->traverse($visitor); + $callback($field); + $field->traverse($callback); } } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/WindowExpression.php b/app/vendor/cakephp/cakephp/src/Database/Expression/WindowExpression.php new file mode 100644 index 000000000..cfbf53c14 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/WindowExpression.php @@ -0,0 +1,337 @@ +name = new IdentifierExpression($name); + } + + /** + * Return whether is only a named window expression. + * + * These window expressions only specify a named window and do not + * specify their own partitions, frame or order. + * + * @return bool + */ + public function isNamedOnly(): bool + { + return $this->name->getIdentifier() && (!$this->partitions && !$this->frame && !$this->order); + } + + /** + * Sets the window name. + * + * @param string $name Window name + * @return $this + */ + public function name(string $name) + { + $this->name = new IdentifierExpression($name); + + return $this; + } + + /** + * @inheritDoc + */ + public function partition($partitions) + { + if (!$partitions) { + return $this; + } + + if ($partitions instanceof Closure) { + $partitions = $partitions(new QueryExpression([], [], '')); + } + + if (!is_array($partitions)) { + $partitions = [$partitions]; + } + + foreach ($partitions as &$partition) { + if (is_string($partition)) { + $partition = new IdentifierExpression($partition); + } + } + + $this->partitions = array_merge($this->partitions, $partitions); + + return $this; + } + + /** + * @inheritDoc + */ + public function order($fields) + { + if (!$fields) { + return $this; + } + + if ($this->order === null) { + $this->order = new OrderByExpression(); + } + + if ($fields instanceof Closure) { + $fields = $fields(new QueryExpression([], [], '')); + } + + $this->order->add($fields); + + return $this; + } + + /** + * @inheritDoc + */ + public function range($start, $end = 0) + { + return $this->frame(self::RANGE, $start, self::PRECEDING, $end, self::FOLLOWING); + } + + /** + * @inheritDoc + */ + public function rows(?int $start, ?int $end = 0) + { + return $this->frame(self::ROWS, $start, self::PRECEDING, $end, self::FOLLOWING); + } + + /** + * @inheritDoc + */ + public function groups(?int $start, ?int $end = 0) + { + return $this->frame(self::GROUPS, $start, self::PRECEDING, $end, self::FOLLOWING); + } + + /** + * @inheritDoc + */ + public function frame( + string $type, + $startOffset, + string $startDirection, + $endOffset, + string $endDirection + ) { + $this->frame = [ + 'type' => $type, + 'start' => [ + 'offset' => $startOffset, + 'direction' => $startDirection, + ], + 'end' => [ + 'offset' => $endOffset, + 'direction' => $endDirection, + ], + ]; + + return $this; + } + + /** + * @inheritDoc + */ + public function excludeCurrent() + { + $this->exclusion = 'CURRENT ROW'; + + return $this; + } + + /** + * @inheritDoc + */ + public function excludeGroup() + { + $this->exclusion = 'GROUP'; + + return $this; + } + + /** + * @inheritDoc + */ + public function excludeTies() + { + $this->exclusion = 'TIES'; + + return $this; + } + + /** + * @inheritDoc + */ + public function sql(ValueBinder $binder): string + { + $clauses = []; + if ($this->name->getIdentifier()) { + $clauses[] = $this->name->sql($binder); + } + + if ($this->partitions) { + $expressions = []; + foreach ($this->partitions as $partition) { + $expressions[] = $partition->sql($binder); + } + + $clauses[] = 'PARTITION BY ' . implode(', ', $expressions); + } + + if ($this->order) { + $clauses[] = $this->order->sql($binder); + } + + if ($this->frame) { + $start = $this->buildOffsetSql( + $binder, + $this->frame['start']['offset'], + $this->frame['start']['direction'] + ); + $end = $this->buildOffsetSql( + $binder, + $this->frame['end']['offset'], + $this->frame['end']['direction'] + ); + + $frameSql = sprintf('%s BETWEEN %s AND %s', $this->frame['type'], $start, $end); + + if ($this->exclusion !== null) { + $frameSql .= ' EXCLUDE ' . $this->exclusion; + } + + $clauses[] = $frameSql; + } + + return implode(' ', $clauses); + } + + /** + * @inheritDoc + */ + public function traverse(Closure $callback) + { + $callback($this->name); + foreach ($this->partitions as $partition) { + $callback($partition); + $partition->traverse($callback); + } + + if ($this->order) { + $callback($this->order); + $this->order->traverse($callback); + } + + if ($this->frame !== null) { + $offset = $this->frame['start']['offset']; + if ($offset instanceof ExpressionInterface) { + $callback($offset); + $offset->traverse($callback); + } + $offset = $this->frame['end']['offset'] ?? null; + if ($offset instanceof ExpressionInterface) { + $callback($offset); + $offset->traverse($callback); + } + } + + return $this; + } + + /** + * Builds frame offset sql. + * + * @param \Cake\Database\ValueBinder $binder Value binder + * @param int|string|\Cake\Database\ExpressionInterface|null $offset Frame offset + * @param string $direction Frame offset direction + * @return string + */ + protected function buildOffsetSql(ValueBinder $binder, $offset, string $direction): string + { + if ($offset === 0) { + return 'CURRENT ROW'; + } + + if ($offset instanceof ExpressionInterface) { + $offset = $offset->sql($binder); + } + + $sql = sprintf( + '%s %s', + $offset ?? 'UNBOUNDED', + $direction + ); + + return $sql; + } + + /** + * Clone this object and its subtree of expressions. + * + * @return void + */ + public function __clone() + { + $this->name = clone $this->name; + foreach ($this->partitions as $i => $partition) { + $this->partitions[$i] = clone $partition; + } + if ($this->order !== null) { + $this->order = clone $this->order; + } + } +} diff --git a/app/vendor/cakephp/cakephp/src/Database/Expression/WindowInterface.php b/app/vendor/cakephp/cakephp/src/Database/Expression/WindowInterface.php new file mode 100644 index 000000000..03e117ac8 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Expression/WindowInterface.php @@ -0,0 +1,163 @@ + 'literal']; - } - - return $this->_build($name, $expression, $types, $return); - } - /** * Returns a FunctionExpression representing a call to SQL RAND function. * @@ -77,72 +34,72 @@ protected function _literalArgumentFunction( */ public function rand(): FunctionExpression { - return $this->_build('RAND', [], [], 'float'); + return new FunctionExpression('RAND', [], [], 'float'); } /** - * Returns a FunctionExpression representing a call to SQL SUM function. + * Returns a AggregateExpression representing a call to SQL SUM function. * * @param string|\Cake\Database\ExpressionInterface $expression the function argument * @param array $types list of types to bind to the arguments - * @return \Cake\Database\Expression\FunctionExpression + * @return \Cake\Database\Expression\AggregateExpression */ - public function sum($expression, $types = []): FunctionExpression + public function sum($expression, $types = []): AggregateExpression { $returnType = 'float'; if (current($types) === 'integer') { $returnType = 'integer'; } - return $this->_literalArgumentFunction('SUM', $expression, $types, $returnType); + return $this->aggregate('SUM', $this->toLiteralParam($expression), $types, $returnType); } /** - * Returns a FunctionExpression representing a call to SQL AVG function. + * Returns a AggregateExpression representing a call to SQL AVG function. * * @param string|\Cake\Database\ExpressionInterface $expression the function argument * @param array $types list of types to bind to the arguments - * @return \Cake\Database\Expression\FunctionExpression + * @return \Cake\Database\Expression\AggregateExpression */ - public function avg($expression, $types = []): FunctionExpression + public function avg($expression, $types = []): AggregateExpression { - return $this->_literalArgumentFunction('AVG', $expression, $types, 'float'); + return $this->aggregate('AVG', $this->toLiteralParam($expression), $types, 'float'); } /** - * Returns a FunctionExpression representing a call to SQL MAX function. + * Returns a AggregateExpression representing a call to SQL MAX function. * * @param string|\Cake\Database\ExpressionInterface $expression the function argument * @param array $types list of types to bind to the arguments - * @return \Cake\Database\Expression\FunctionExpression + * @return \Cake\Database\Expression\AggregateExpression */ - public function max($expression, $types = []): FunctionExpression + public function max($expression, $types = []): AggregateExpression { - return $this->_literalArgumentFunction('MAX', $expression, $types, current($types) ?: 'string'); + return $this->aggregate('MAX', $this->toLiteralParam($expression), $types, current($types) ?: 'float'); } /** - * Returns a FunctionExpression representing a call to SQL MIN function. + * Returns a AggregateExpression representing a call to SQL MIN function. * * @param string|\Cake\Database\ExpressionInterface $expression the function argument * @param array $types list of types to bind to the arguments - * @return \Cake\Database\Expression\FunctionExpression + * @return \Cake\Database\Expression\AggregateExpression */ - public function min($expression, $types = []): FunctionExpression + public function min($expression, $types = []): AggregateExpression { - return $this->_literalArgumentFunction('MIN', $expression, $types, current($types) ?: 'string'); + return $this->aggregate('MIN', $this->toLiteralParam($expression), $types, current($types) ?: 'float'); } /** - * Returns a FunctionExpression representing a call to SQL COUNT function. + * Returns a AggregateExpression representing a call to SQL COUNT function. * * @param string|\Cake\Database\ExpressionInterface $expression the function argument * @param array $types list of types to bind to the arguments - * @return \Cake\Database\Expression\FunctionExpression + * @return \Cake\Database\Expression\AggregateExpression */ - public function count($expression, $types = []): FunctionExpression + public function count($expression, $types = []): AggregateExpression { - return $this->_literalArgumentFunction('COUNT', $expression, $types, 'integer'); + return $this->aggregate('COUNT', $this->toLiteralParam($expression), $types, 'integer'); } /** @@ -154,7 +111,7 @@ public function count($expression, $types = []): FunctionExpression */ public function concat(array $args, array $types = []): FunctionExpression { - return $this->_build('CONCAT', $args, $types, 'string'); + return new FunctionExpression('CONCAT', $args, $types, 'string'); } /** @@ -166,7 +123,38 @@ public function concat(array $args, array $types = []): FunctionExpression */ public function coalesce(array $args, array $types = []): FunctionExpression { - return $this->_build('COALESCE', $args, $types, current($types) ?: 'string'); + return new FunctionExpression('COALESCE', $args, $types, current($types) ?: 'string'); + } + + /** + * Returns a FunctionExpression representing a SQL CAST. + * + * The `$type` parameter is a SQL type. The return type for the returned expression + * is the default type name. Use `setReturnType()` to update it. + * + * @param string|\Cake\Database\ExpressionInterface $field Field or expression to cast. + * @param string $type The SQL data type + * @return \Cake\Database\Expression\FunctionExpression + */ + public function cast($field, string $type = ''): FunctionExpression + { + if (is_array($field)) { + deprecationWarning( + 'Build cast function by FunctionsBuilder::cast(array $args) is deprecated. ' . + 'Use FunctionsBuilder::cast($field, string $type) instead.' + ); + + return new FunctionExpression('CAST', $field); + } + + if (empty($type)) { + throw new InvalidArgumentException('The `$type` in a cast cannot be empty.'); + } + + $expression = new FunctionExpression('CAST', $this->toLiteralParam($field)); + $expression->setConjunction(' AS')->add([$type => 'literal']); + + return $expression; } /** @@ -179,7 +167,7 @@ public function coalesce(array $args, array $types = []): FunctionExpression */ public function dateDiff(array $args, array $types = []): FunctionExpression { - return $this->_build('DATEDIFF', $args, $types, 'integer'); + return new FunctionExpression('DATEDIFF', $args, $types, 'integer'); } /** @@ -205,7 +193,7 @@ public function datePart(string $part, $expression, array $types = []): Function */ public function extract(string $part, $expression, array $types = []): FunctionExpression { - $expression = $this->_literalArgumentFunction('EXTRACT', $expression, $types, 'integer'); + $expression = new FunctionExpression('EXTRACT', $this->toLiteralParam($expression), $types, 'integer'); $expression->setConjunction(' FROM')->add([$part => 'literal'], [], true); return $expression; @@ -226,7 +214,7 @@ public function dateAdd($expression, $value, string $unit, array $types = []): F $value = 0; } $interval = $value . ' ' . $unit; - $expression = $this->_literalArgumentFunction('DATE_ADD', $expression, $types, 'datetime'); + $expression = new FunctionExpression('DATE_ADD', $this->toLiteralParam($expression), $types, 'datetime'); $expression->setConjunction(', INTERVAL')->add([$interval => 'literal']); return $expression; @@ -242,7 +230,7 @@ public function dateAdd($expression, $value, string $unit, array $types = []): F */ public function dayOfWeek($expression, $types = []): FunctionExpression { - return $this->_literalArgumentFunction('DAYOFWEEK', $expression, $types, 'integer'); + return new FunctionExpression('DAYOFWEEK', $this->toLiteralParam($expression), $types, 'integer'); } /** @@ -269,18 +257,93 @@ public function weekday($expression, $types = []): FunctionExpression public function now(string $type = 'datetime'): FunctionExpression { if ($type === 'datetime') { - return $this->_build('NOW')->setReturnType('datetime'); + return new FunctionExpression('NOW', [], [], 'datetime'); } if ($type === 'date') { - return $this->_build('CURRENT_DATE')->setReturnType('date'); + return new FunctionExpression('CURRENT_DATE', [], [], 'date'); } if ($type === 'time') { - return $this->_build('CURRENT_TIME')->setReturnType('time'); + return new FunctionExpression('CURRENT_TIME', [], [], 'time'); } throw new InvalidArgumentException('Invalid argument for FunctionsBuilder::now(): ' . $type); } + /** + * Returns an AggregateExpression representing call to SQL ROW_NUMBER(). + * + * @return \Cake\Database\Expression\AggregateExpression + */ + public function rowNumber(): AggregateExpression + { + return (new AggregateExpression('ROW_NUMBER', [], [], 'integer'))->over(); + } + + /** + * Returns an AggregateExpression representing call to SQL LAG(). + * + * @param \Cake\Database\ExpressionInterface|string $expression The value evaluated at offset + * @param int $offset The row offset + * @param mixed $default The default value if offset doesn't exist + * @param string $type The output type of the lag expression. Defaults to float. + * @return \Cake\Database\Expression\AggregateExpression + */ + public function lag($expression, int $offset, $default = null, $type = null): AggregateExpression + { + $params = $this->toLiteralParam($expression) + [$offset => 'literal']; + if ($default !== null) { + $params[] = $default; + } + + $types = []; + if ($type !== null) { + $types = [$type, 'integer', $type]; + } + + return (new AggregateExpression('LAG', $params, $types, $type ?? 'float'))->over(); + } + + /** + * Returns an AggregateExpression representing call to SQL LEAD(). + * + * @param \Cake\Database\ExpressionInterface|string $expression The value evaluated at offset + * @param int $offset The row offset + * @param mixed $default The default value if offset doesn't exist + * @param string $type The output type of the lead expression. Defaults to float. + * @return \Cake\Database\Expression\AggregateExpression + */ + public function lead($expression, int $offset, $default = null, $type = null): AggregateExpression + { + $params = $this->toLiteralParam($expression) + [$offset => 'literal']; + if ($default !== null) { + $params[] = $default; + } + + $types = []; + if ($type !== null) { + $types = [$type, 'integer', $type]; + } + + return (new AggregateExpression('LEAD', $params, $types, $type ?? 'float'))->over(); + } + + /** + * Helper method to create arbitrary SQL aggregate function calls. + * + * @param string $name The SQL aggregate function name + * @param array $params Array of arguments to be passed to the function. + * Can be an associative array with the literal value or identifier: + * `['value' => 'literal']` or `['value' => 'identifier'] + * @param array $types Array of types that match the names used in `$params`: + * `['name' => 'type']` + * @param string $return Return type of the entire expression. Defaults to float. + * @return \Cake\Database\Expression\AggregateExpression + */ + public function aggregate(string $name, array $params = [], array $types = [], string $return = 'float') + { + return new AggregateExpression($name, $params, $types, $return); + } + /** * Magic method dispatcher to create custom SQL function calls * @@ -292,15 +355,21 @@ public function now(string $type = 'datetime'): FunctionExpression */ public function __call(string $name, array $args): FunctionExpression { - switch (count($args)) { - case 0: - return $this->_build($name); - case 1: - return $this->_build($name, $args[0]); - case 2: - return $this->_build($name, $args[0], $args[1]); - default: - return $this->_build($name, $args[0], $args[1], $args[2]); + return new FunctionExpression($name, ...$args); + } + + /** + * Creates function parameter array from expression or string literal. + * + * @param \Cake\Database\ExpressionInterface|string $expression function argument + * @return (\Cake\Database\ExpressionInterface|string)[] + */ + protected function toLiteralParam($expression) + { + if (is_string($expression)) { + return [$expression => 'literal']; } + + return [$expression]; } } diff --git a/app/vendor/cakephp/cakephp/src/Database/IdentifierQuoter.php b/app/vendor/cakephp/cakephp/src/Database/IdentifierQuoter.php index c05718aa2..6b21a5ad6 100644 --- a/app/vendor/cakephp/cakephp/src/Database/IdentifierQuoter.php +++ b/app/vendor/cakephp/cakephp/src/Database/IdentifierQuoter.php @@ -134,7 +134,7 @@ protected function _quoteParts(Query $query): void protected function _basicQuoter(array $part): array { $result = []; - foreach ((array)$part as $alias => $value) { + foreach ($part as $alias => $value) { $value = !is_string($value) ? $value : $this->_driver->quoteIdentifier($value); $alias = is_numeric($alias) ? $alias : $this->_driver->quoteIdentifier($alias); $result[$alias] = $value; @@ -178,7 +178,11 @@ protected function _quoteJoins(array $joins): array */ protected function _quoteInsert(Query $query): void { - [$table, $columns] = $query->clause('insert'); + $insert = $query->clause('insert'); + if (!isset($insert[0]) || !isset($insert[1])) { + return; + } + [$table, $columns] = $insert; $table = $this->_driver->quoteIdentifier($table); foreach ($columns as &$column) { if (is_scalar($column)) { diff --git a/app/vendor/cakephp/cakephp/src/Database/LICENSE.txt b/app/vendor/cakephp/cakephp/src/Database/LICENSE.txt index 0b3b94303..b938c9e8e 100644 --- a/app/vendor/cakephp/cakephp/src/Database/LICENSE.txt +++ b/app/vendor/cakephp/cakephp/src/Database/LICENSE.txt @@ -1,7 +1,7 @@ The MIT License (MIT) CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) -Copyright (c) 2005-2019, Cake Software Foundation, Inc. (https://cakefoundation.org) +Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/vendor/cakephp/cakephp/src/Database/Log/LoggedQuery.php b/app/vendor/cakephp/cakephp/src/Database/Log/LoggedQuery.php index 96a3ac199..bc014ee85 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Log/LoggedQuery.php +++ b/app/vendor/cakephp/cakephp/src/Database/Log/LoggedQuery.php @@ -107,6 +107,19 @@ protected function interpolate(): string return preg_replace($keys, $params, $this->query, $limit); } + /** + * Get the logging context data for a query. + * + * @return array + */ + public function getContext(): array + { + return [ + 'numRows' => $this->numRows, + 'took' => $this->took, + ]; + } + /** * Returns data that will be serialized as JSON * @@ -144,6 +157,6 @@ public function __toString(): string $sql = $this->interpolate(); } - return "duration={$this->took} rows={$this->numRows} {$sql}"; + return $sql; } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Log/LoggingStatement.php b/app/vendor/cakephp/cakephp/src/Database/Log/LoggingStatement.php index 674cf6bb3..581a2cb0d 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Log/LoggingStatement.php +++ b/app/vendor/cakephp/cakephp/src/Database/Log/LoggingStatement.php @@ -41,6 +41,20 @@ class LoggingStatement extends StatementDecorator */ protected $_compiledParams = []; + /** + * Query execution start time. + * + * @var float + */ + protected $startTime = 0.0; + + /** + * Logged query + * + * @var \Cake\Database\Log\LoggedQuery|null + */ + protected $loggedQuery; + /** * Wrapper for the execute function to calculate time spent * and log the query afterwards. @@ -51,21 +65,68 @@ class LoggingStatement extends StatementDecorator */ public function execute(?array $params = null): bool { - $t = microtime(true); - $query = new LoggedQuery(); + $this->startTime = microtime(true); + + $this->loggedQuery = new LoggedQuery(); + $this->loggedQuery->params = $params ?: $this->_compiledParams; try { $result = parent::execute($params); + $this->loggedQuery->took = (int)round((microtime(true) - $this->startTime) * 1000, 0); } catch (Exception $e) { /** @psalm-suppress UndefinedPropertyAssignment */ $e->queryString = $this->queryString; - $query->error = $e; - $this->_log($query, $params, $t); + $this->loggedQuery->error = $e; + $this->_log(); throw $e; } - $query->numRows = $this->rowCount(); - $this->_log($query, $params, $t); + if (preg_match('/^(?!SELECT)/i', $this->queryString)) { + $this->rowCount(); + } + + return $result; + } + + /** + * @inheritDoc + */ + public function fetch($type = self::FETCH_TYPE_NUM) + { + $record = parent::fetch($type); + + if ($this->loggedQuery) { + $this->rowCount(); + } + + return $record; + } + + /** + * @inheritDoc + */ + public function fetchAll($type = self::FETCH_TYPE_NUM) + { + $results = parent::fetchAll($type); + + if ($this->loggedQuery) { + $this->rowCount(); + } + + return $results; + } + + /** + * @inheritDoc + */ + public function rowCount(): int + { + $result = parent::rowCount(); + + if ($this->loggedQuery) { + $this->loggedQuery->numRows = $result; + $this->_log(); + } return $result; } @@ -74,17 +135,18 @@ public function execute(?array $params = null): bool * Copies the logging data to the passed LoggedQuery and sends it * to the logging system. * - * @param \Cake\Database\Log\LoggedQuery $query The query to log. - * @param array|null $params List of values to be bound to query. - * @param float $startTime The microtime when the query was executed. * @return void */ - protected function _log(LoggedQuery $query, ?array $params, float $startTime): void + protected function _log(): void { - $query->took = (int)round((microtime(true) - $startTime) * 1000, 0); - $query->params = $params ?: $this->_compiledParams; - $query->query = $this->queryString; - $this->getLogger()->debug((string)$query, ['query' => $query]); + if ($this->loggedQuery === null) { + return; + } + + $this->loggedQuery->query = $this->queryString; + $this->getLogger()->debug((string)$this->loggedQuery, ['query' => $this->loggedQuery]); + + $this->loggedQuery = null; } /** diff --git a/app/vendor/cakephp/cakephp/src/Database/Log/QueryLogger.php b/app/vendor/cakephp/cakephp/src/Database/Log/QueryLogger.php index a3a229655..04040889c 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Log/QueryLogger.php +++ b/app/vendor/cakephp/cakephp/src/Database/Log/QueryLogger.php @@ -35,6 +35,7 @@ class QueryLogger extends BaseLog public function __construct(array $config = []) { $this->_defaultConfig['scopes'] = ['queriesLog']; + $this->_defaultConfig['connection'] = ''; parent::__construct($config); } @@ -44,10 +45,13 @@ public function __construct(array $config = []) */ public function log($level, $message, array $context = []) { - Log::write( - 'debug', - (string)$context['query'], - ['scope' => $this->scopes() ?: ['queriesLog']] - ); + $context['scope'] = $this->scopes() ?: ['queriesLog']; + $context['connection'] = $this->getConfig('connection'); + + if ($context['query'] instanceof LoggedQuery) { + $context = $context['query']->getContext() + $context; + $message = 'connection={connection} duration={took} rows={numRows} ' . $message; + } + Log::write('debug', $message, $context); } } diff --git a/app/vendor/cakephp/cakephp/src/Database/PostgresCompiler.php b/app/vendor/cakephp/cakephp/src/Database/PostgresCompiler.php index e4bc8951c..7beae8b58 100644 --- a/app/vendor/cakephp/cakephp/src/Database/PostgresCompiler.php +++ b/app/vendor/cakephp/cakephp/src/Database/PostgresCompiler.php @@ -16,6 +16,8 @@ */ namespace Cake\Database; +use Cake\Database\Expression\FunctionExpression; + /** * Responsible for compiling a Query object into its SQL representation * for Postgres @@ -32,4 +34,60 @@ class PostgresCompiler extends QueryCompiler * @var bool */ protected $_quotedSelectAliases = true; + + /** + * @inheritDoc + */ + protected $_templates = [ + 'delete' => 'DELETE', + 'where' => ' WHERE %s', + 'group' => ' GROUP BY %s', + 'order' => ' %s', + 'limit' => ' LIMIT %s', + 'offset' => ' OFFSET %s', + 'epilog' => ' %s', + ]; + + /** + * Helper function used to build the string representation of a HAVING clause, + * it constructs the field list taking care of aliasing and + * converting expression objects to string. + * + * @param array $parts list of fields to be transformed to string + * @param \Cake\Database\Query $query The query that is being compiled + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder + * @return string + */ + protected function _buildHavingPart($parts, $query, $binder) + { + $selectParts = $query->clause('select'); + + foreach ($selectParts as $selectKey => $selectPart) { + if (!$selectPart instanceof FunctionExpression) { + continue; + } + foreach ($parts as $k => $p) { + if (!is_string($p)) { + continue; + } + preg_match_all( + '/\b' . trim($selectKey, '"') . '\b/i', + $p, + $matches + ); + + if (empty($matches[0])) { + continue; + } + + $parts[$k] = preg_replace( + ['/"/', '/\b' . trim($selectKey, '"') . '\b/i'], + ['', $selectPart->sql($binder)], + $p + ); + } + } + + return sprintf(' HAVING %s', implode(', ', $parts)); + } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Query.php b/app/vendor/cakephp/cakephp/src/Database/Query.php index a69d52cf9..030decaeb 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Query.php +++ b/app/vendor/cakephp/cakephp/src/Database/Query.php @@ -16,11 +16,14 @@ */ namespace Cake\Database; +use Cake\Database\Exception\DatabaseException; +use Cake\Database\Expression\CommonTableExpression; use Cake\Database\Expression\IdentifierExpression; use Cake\Database\Expression\OrderByExpression; use Cake\Database\Expression\OrderClauseExpression; use Cake\Database\Expression\QueryExpression; use Cake\Database\Expression\ValuesExpression; +use Cake\Database\Expression\WindowExpression; use Cake\Database\Statement\CallbackStatement; use Closure; use InvalidArgumentException; @@ -77,6 +80,7 @@ class Query implements ExpressionInterface, IteratorAggregate 'set' => [], 'insert' => [], 'values' => [], + 'with' => [], 'select' => [], 'distinct' => false, 'modifier' => [], @@ -85,6 +89,7 @@ class Query implements ExpressionInterface, IteratorAggregate 'where' => null, 'group' => [], 'having' => null, + 'window' => [], 'order' => null, 'limit' => null, 'offset' => null, @@ -98,7 +103,7 @@ class Query implements ExpressionInterface, IteratorAggregate * @var string[] */ protected $_selectParts = [ - 'select', 'from', 'join', 'where', 'group', 'having', 'order', 'limit', + 'with', 'select', 'from', 'join', 'where', 'group', 'having', 'order', 'limit', 'offset', 'union', 'epilog', ]; @@ -107,21 +112,21 @@ class Query implements ExpressionInterface, IteratorAggregate * * @var string[] */ - protected $_updateParts = ['update', 'set', 'where', 'epilog']; + protected $_updateParts = ['with', 'update', 'set', 'where', 'epilog']; /** * The list of query clauses to traverse for generating a DELETE statement * * @var string[] */ - protected $_deleteParts = ['delete', 'modifier', 'from', 'where', 'epilog']; + protected $_deleteParts = ['with', 'delete', 'modifier', 'from', 'where', 'epilog']; /** * The list of query clauses to traverse for generating an INSERT statement * * @var string[] */ - protected $_insertParts = ['insert', 'values', 'epilog']; + protected $_insertParts = ['with', 'insert', 'values', 'epilog']; /** * Indicates whether internal state of this query was changed, this is used to @@ -174,7 +179,7 @@ class Query implements ExpressionInterface, IteratorAggregate /** * The Type map for fields in the select clause * - * @var \Cake\Database\TypeMap + * @var \Cake\Database\TypeMap|null */ protected $_selectTypeMap; @@ -292,18 +297,17 @@ public function rowCountAndClose(): int * values when the query is executed, hence it is most suitable to use with * prepared statements. * - * @param \Cake\Database\ValueBinder|null $generator A placeholder object that will hold - * associated values for expressions + * @param \Cake\Database\ValueBinder|null $binder Value binder that generates parameter placeholders * @return string */ - public function sql(?ValueBinder $generator = null): string + public function sql(?ValueBinder $binder = null): string { - if (!$generator) { - $generator = $this->getValueBinder(); - $generator->resetCount(); + if (!$binder) { + $binder = $this->getValueBinder(); + $binder->resetCount(); } - return $this->getConnection()->compileQuery($this, $generator); + return $this->getConnection()->compileQuery($this, $binder); } /** @@ -324,13 +328,13 @@ public function sql(?ValueBinder $generator = null): string * }); * ``` * - * @param callable $visitor A function or callable to be executed for each part + * @param callable $callback A function or callable to be executed for each part * @return $this */ - public function traverse($visitor) + public function traverse($callback) { foreach ($this->_parts as $name => $part) { - $visitor($part, $name); + $callback($part, $name); } return $this; @@ -369,6 +373,71 @@ public function traverseParts(callable $visitor, array $parts) return $this; } + /** + * Adds a new common table expression (CTE) to the query. + * + * ### Examples: + * + * Common table expressions can either be passed as preconstructed expression + * objects: + * + * ``` + * $cte = new \Cake\Database\Expression\CommonTableExpression( + * 'cte', + * $connection + * ->newQuery() + * ->select('*') + * ->from('articles') + * ); + * + * $query->with($cte); + * ``` + * + * or returned from a closure, which will receive a new common table expression + * object as the first argument, and a new blank query object as + * the second argument: + * + * ``` + * $query->with(function ( + * \Cake\Database\Expression\CommonTableExpression $cte, + * \Cake\Database\Query $query + * ) { + * $cteQuery = $query + * ->select('*') + * ->from('articles'); + * + * return $cte + * ->name('cte') + * ->query($cteQuery); + * }); + * ``` + * + * @param \Closure|\Cake\Database\Expression\CommonTableExpression $cte The CTE to add. + * @param bool $overwrite Whether to reset the list of CTEs. + * @return $this + */ + public function with($cte, bool $overwrite = false) + { + if ($overwrite) { + $this->_parts['with'] = []; + } + + if ($cte instanceof Closure) { + $query = $this->getConnection()->newQuery(); + $cte = $cte(new CommonTableExpression(), $query); + if (!($cte instanceof CommonTableExpression)) { + throw new RuntimeException( + 'You must return a `CommonTableExpression` from a Closure passed to `with()`.' + ); + } + } + + $this->_parts['with'][] = $cte; + $this->_dirty(); + + return $this; + } + /** * Adds new fields to be returned by a `SELECT` statement when this query is * executed. Fields can be passed as an array of strings, array of expression @@ -798,7 +867,10 @@ protected function _makeJoin($table, $conditions, $type): array $table = current($table); } - /** @psalm-suppress InvalidReturnStatement */ + /** + * @psalm-suppress InvalidArrayOffset + * @psalm-suppress InvalidReturnStatement + */ return [ $alias => [ 'table' => $table, @@ -916,7 +988,7 @@ protected function _makeJoin($table, $conditions, $type): array * Please note that when using the array notation or the expression objects, all * *values* will be correctly quoted and transformed to the correspondent database * data type automatically for you, thus securing your application from SQL injections. - * The keys however, are not treated as unsafe input, and should be sanitized/whitelisted. + * The keys however, are not treated as unsafe input, and should be validated/sanitized. * * If you use string conditions make sure that your values are correctly quoted. * The safest thing you can do is to never use string conditions. @@ -1128,7 +1200,9 @@ public function andWhere($conditions, array $types = []) * `ORDER BY title DESC, author_id ASC` * * ``` - * $query->order(['title' => 'DESC NULLS FIRST'])->order('author_id'); + * $query + * ->order(['title' => $query->newExpr('DESC NULLS FIRST')]) + * ->order('author_id'); * ``` * * Will generate: @@ -1153,7 +1227,7 @@ public function andWhere($conditions, array $types = []) * `ORDER BY (id %2 = 0), title ASC` * * Order fields/directions are not sanitized by the query builder. - * You should use a whitelist of fields/directions when passing + * You should use an allowed list of fields/directions when passing * in user-supplied data to `order()`. * * If you need to set complex expressions as order conditions, you @@ -1190,7 +1264,7 @@ public function order($fields, $overwrite = false) * Order fields are not suitable for use with user supplied data as they are * not sanitized by the query builder. * - * @param string|\Cake\Database\Expression\QueryExpression $field The field to order on. + * @param string|\Cake\Database\Expression\QueryExpression|\Closure $field The field to order on. * @param bool $overwrite Whether or not to reset the order clauses. * @return $this */ @@ -1203,6 +1277,10 @@ public function orderAsc($field, $overwrite = false) return $this; } + if ($field instanceof Closure) { + $field = $field($this->newExpr(), $this); + } + if (!$this->_parts['order']) { $this->_parts['order'] = new OrderByExpression(); } @@ -1220,7 +1298,7 @@ public function orderAsc($field, $overwrite = false) * Order fields are not suitable for use with user supplied data as they are * not sanitized by the query builder. * - * @param string|\Cake\Database\Expression\QueryExpression $field The field to order on. + * @param string|\Cake\Database\Expression\QueryExpression|\Closure $field The field to order on. * @param bool $overwrite Whether or not to reset the order clauses. * @return $this */ @@ -1233,6 +1311,10 @@ public function orderDesc($field, $overwrite = false) return $this; } + if ($field instanceof Closure) { + $field = $field($this->newExpr(), $this); + } + if (!$this->_parts['order']) { $this->_parts['order'] = new OrderByExpression(); } @@ -1328,6 +1410,35 @@ public function andHaving($conditions, $types = []) return $this; } + /** + * Adds a named window expression. + * + * You are responsible for adding windows in the order your database requires. + * + * @param string $name Window name + * @param \Cake\Database\Expression\WindowExpression|\Closure $window Window expression + * @param bool $overwrite Clear all previous query window expressions + * @return $this + */ + public function window(string $name, $window, bool $overwrite = false) + { + if ($overwrite) { + $this->_parts['window'] = []; + } + + if ($window instanceof Closure) { + $window = $window(new WindowExpression(), $this); + if (!($window instanceof WindowExpression)) { + throw new RuntimeException('You must return a `WindowExpression` from a Closure passed to `window()`.'); + } + } + + $this->_parts['window'][] = ['name' => new IdentifierExpression($name), 'window' => $window]; + $this->_dirty(); + + return $this; + } + /** * Set the page of results you want. * @@ -1384,9 +1495,6 @@ public function page(int $num, ?int $limit = null) public function limit($num) { $this->_dirty(); - if ($num !== null && !is_object($num)) { - $num = (int)$num; - } $this->_parts['limit'] = $num; return $this; @@ -1413,9 +1521,6 @@ public function limit($num) public function offset($num) { $this->_dirty(); - if ($num !== null && !is_object($num)) { - $num = (int)$num; - } $this->_parts['offset'] = $num; return $this; @@ -1567,18 +1672,18 @@ public function identifier(string $identifier): ExpressionInterface * * @param array|\Cake\Database\Query|\Cake\Database\Expression\ValuesExpression $data The data to insert. * @return $this - * @throws \Cake\Database\Exception if you try to set values before declaring columns. + * @throws \Cake\Database\Exception\DatabaseException if you try to set values before declaring columns. * Or if you try to set values on non-insert queries. */ public function values($data) { if ($this->_type !== 'insert') { - throw new Exception( + throw new DatabaseException( 'You cannot add values before defining columns to use.' ); } if (empty($this->_parts['insert'])) { - throw new Exception( + throw new DatabaseException( 'You cannot add values before defining columns to use.' ); } @@ -1714,7 +1819,7 @@ public function delete(?string $table = null) * * Epliog content is raw SQL and not suitable for use with user supplied data. * - * @param string|\Cake\Database\Expression\QueryExpression|null $expression The expression to be appended + * @param string|\Cake\Database\ExpressionInterface|null $expression The expression to be appended * @return $this */ public function epilog($expression = null) @@ -2013,7 +2118,7 @@ public function setValueBinder(?ValueBinder $binder) public function enableBufferedResults(bool $enable = true) { $this->_dirty(); - $this->_useBufferedResults = (bool)$enable; + $this->_useBufferedResults = $enable; return $this; } @@ -2082,7 +2187,11 @@ public function getSelectTypeMap(): TypeMap } /** - * Disables the automatic casting of fields to their corresponding PHP data type + * Disables result casting. + * + * When disabled, the fields will be returned as received from the database + * driver (which in most environments means they are being returned as + * strings), which can improve performance with larger datasets. * * @return $this */ @@ -2094,7 +2203,10 @@ public function disableResultsCasting() } /** - * Enables the automatic casting of fields to their corresponding type + * Enables result casting. + * + * When enabled, the fields in the results returned by this Query will be + * cast to their corresponding PHP data type. * * @return $this */ @@ -2105,6 +2217,23 @@ public function enableResultsCasting() return $this; } + /** + * Returns whether result casting is enabled/disabled. + * + * When enabled, the fields in the results returned by this Query will be + * casted to their corresponding PHP data type. + * + * When disabled, the fields will be returned as received from the database + * driver (which in most environments means they are being returned as + * strings), which can improve performance with larger datasets. + * + * @return bool + */ + public function isResultsCastingEnabled(): bool + { + return $this->typeCastEnabled; + } + /** * Auxiliary function used to wrap the original statement from the driver with * any registered callbacks. @@ -2179,10 +2308,7 @@ protected function _dirty(): void } /** - * Do a deep clone on this object. - * - * Will clone all of the expression objects used in - * each of the clauses, as well as the valueBinder. + * Handles clearing iterator and cloning all expressions and value binders. * * @return void */ @@ -2232,9 +2358,13 @@ public function __toString(): string public function __debugInfo(): array { try { - set_error_handler(function ($errno, $errstr) { - throw new RuntimeException($errstr, $errno); - }, E_ALL); + set_error_handler( + /** @return no-return */ + function ($errno, $errstr) { + throw new RuntimeException($errstr, $errno); + }, + E_ALL + ); $sql = $this->sql(); $params = $this->getValueBinder()->bindings(); } catch (RuntimeException $e) { diff --git a/app/vendor/cakephp/cakephp/src/Database/QueryCompiler.php b/app/vendor/cakephp/cakephp/src/Database/QueryCompiler.php index b0d08d134..3f8bf33a5 100644 --- a/app/vendor/cakephp/cakephp/src/Database/QueryCompiler.php +++ b/app/vendor/cakephp/cakephp/src/Database/QueryCompiler.php @@ -16,7 +16,7 @@ */ namespace Cake\Database; -use Cake\Database\Expression\QueryExpression; +use Cake\Database\Exception\DatabaseException; use Closure; use Countable; @@ -51,8 +51,8 @@ class QueryCompiler * @var array */ protected $_selectParts = [ - 'select', 'from', 'join', 'where', 'group', 'having', 'order', 'limit', - 'offset', 'union', 'epilog', + 'with', 'select', 'from', 'join', 'where', 'group', 'having', 'window', 'order', + 'limit', 'offset', 'union', 'epilog', ]; /** @@ -60,21 +60,21 @@ class QueryCompiler * * @var array */ - protected $_updateParts = ['update', 'set', 'where', 'epilog']; + protected $_updateParts = ['with', 'update', 'set', 'where', 'epilog']; /** * The list of query clauses to traverse for generating a DELETE statement * * @var array */ - protected $_deleteParts = ['delete', 'modifier', 'from', 'where', 'epilog']; + protected $_deleteParts = ['with', 'delete', 'modifier', 'from', 'where', 'epilog']; /** * The list of query clauses to traverse for generating an INSERT statement * * @var array */ - protected $_insertParts = ['insert', 'values', 'epilog']; + protected $_insertParts = ['with', 'insert', 'values', 'epilog']; /** * Indicate whether or not this query dialect supports ordered unions. @@ -97,25 +97,25 @@ class QueryCompiler * the placeholders for the bound values using the provided generator * * @param \Cake\Database\Query $query The query that is being compiled - * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholders * @return string */ - public function compile(Query $query, ValueBinder $generator): string + public function compile(Query $query, ValueBinder $binder): string { $sql = ''; $type = $query->type(); $query->traverseParts( - $this->_sqlCompiler($sql, $query, $generator), + $this->_sqlCompiler($sql, $query, $binder), $this->{"_{$type}Parts"} ); // Propagate bound parameters from sub-queries if the // placeholders can be found in the SQL statement. - if ($query->getValueBinder() !== $generator) { + if ($query->getValueBinder() !== $binder) { foreach ($query->getValueBinder()->bindings() as $binding) { $placeholder = ':' . $binding['placeholder']; if (preg_match('/' . $placeholder . '(?:\W|$)/', $sql) > 0) { - $generator->bind($placeholder, $binding['value'], $binding['type']); + $binder->bind($placeholder, $binding['value'], $binding['type']); } } } @@ -129,12 +129,12 @@ public function compile(Query $query, ValueBinder $generator): string * * @param string $sql initial sql string to append to * @param \Cake\Database\Query $query The query that is being compiled - * @param \Cake\Database\ValueBinder $generator The placeholder and value binder object + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder * @return \Closure */ - protected function _sqlCompiler(string &$sql, Query $query, ValueBinder $generator): Closure + protected function _sqlCompiler(string &$sql, Query $query, ValueBinder $binder): Closure { - return function ($part, $partName) use (&$sql, $query, $generator) { + return function ($part, $partName) use (&$sql, $query, $binder) { if ( $part === null || (is_array($part) && empty($part)) || @@ -144,19 +144,43 @@ protected function _sqlCompiler(string &$sql, Query $query, ValueBinder $generat } if ($part instanceof ExpressionInterface) { - $part = [$part->sql($generator)]; + $part = [$part->sql($binder)]; } if (isset($this->_templates[$partName])) { - $part = $this->_stringifyExpressions((array)$part, $generator); + $part = $this->_stringifyExpressions((array)$part, $binder); $sql .= sprintf($this->_templates[$partName], implode(', ', $part)); return; } - $sql .= $this->{'_build' . $partName . 'Part'}($part, $query, $generator); + $sql .= $this->{'_build' . $partName . 'Part'}($part, $query, $binder); }; } + /** + * Helper function used to build the string representation of a `WITH` clause, + * it constructs the CTE definitions list and generates the `RECURSIVE` + * keyword when required. + * + * @param array $parts List of CTEs to be transformed to string + * @param \Cake\Database\Query $query The query that is being compiled + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder + * @return string + */ + protected function _buildWithPart(array $parts, Query $query, ValueBinder $binder): string + { + $recursive = false; + $expressions = []; + foreach ($parts as $cte) { + $recursive = $recursive || $cte->isRecursive(); + $expressions[] = $cte->sql($binder); + } + + $recursive = $recursive ? 'RECURSIVE ' : ''; + + return sprintf('WITH %s%s ', $recursive, implode(', ', $expressions)); + } + /** * Helper function used to build the string representation of a SELECT clause, * it constructs the field list taking care of aliasing and @@ -165,22 +189,22 @@ protected function _sqlCompiler(string &$sql, Query $query, ValueBinder $generat * * @param array $parts list of fields to be transformed to string * @param \Cake\Database\Query $query The query that is being compiled - * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder * @return string */ - protected function _buildSelectPart(array $parts, Query $query, ValueBinder $generator): string + protected function _buildSelectPart(array $parts, Query $query, ValueBinder $binder): string { $select = 'SELECT%s %s%s'; if ($this->_orderedUnion && $query->clause('union')) { $select = '(SELECT%s %s%s'; } $distinct = $query->clause('distinct'); - $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $generator); + $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $binder); $driver = $query->getConnection()->getDriver(); $quoteIdentifiers = $driver->isAutoQuotingEnabled() || $this->_quotedSelectAliases; $normalized = []; - $parts = $this->_stringifyExpressions($parts, $generator); + $parts = $this->_stringifyExpressions($parts, $binder); foreach ($parts as $k => $p) { if (!is_numeric($k)) { $p = $p . ' AS '; @@ -198,7 +222,7 @@ protected function _buildSelectPart(array $parts, Query $query, ValueBinder $gen } if (is_array($distinct)) { - $distinct = $this->_stringifyExpressions($distinct, $generator); + $distinct = $this->_stringifyExpressions($distinct, $binder); $distinct = sprintf('DISTINCT ON (%s) ', implode(', ', $distinct)); } @@ -212,14 +236,14 @@ protected function _buildSelectPart(array $parts, Query $query, ValueBinder $gen * * @param array $parts list of tables to be transformed to string * @param \Cake\Database\Query $query The query that is being compiled - * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder * @return string */ - protected function _buildFromPart(array $parts, Query $query, ValueBinder $generator): string + protected function _buildFromPart(array $parts, Query $query, ValueBinder $binder): string { $select = ' FROM %s'; $normalized = []; - $parts = $this->_stringifyExpressions($parts, $generator); + $parts = $this->_stringifyExpressions($parts, $binder); foreach ($parts as $k => $p) { if (!is_numeric($k)) { $p = $p . ' ' . $k; @@ -238,27 +262,22 @@ protected function _buildFromPart(array $parts, Query $query, ValueBinder $gener * * @param array $parts list of joins to be transformed to string * @param \Cake\Database\Query $query The query that is being compiled - * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder * @return string */ - protected function _buildJoinPart(array $parts, Query $query, ValueBinder $generator): string + protected function _buildJoinPart(array $parts, Query $query, ValueBinder $binder): string { $joins = ''; foreach ($parts as $join) { - $subquery = $join['table'] instanceof Query || $join['table'] instanceof QueryExpression; if ($join['table'] instanceof ExpressionInterface) { - $join['table'] = $join['table']->sql($generator); - } - - if ($subquery) { - $join['table'] = '(' . $join['table'] . ')'; + $join['table'] = '(' . $join['table']->sql($binder) . ')'; } $joins .= sprintf(' %s JOIN %s %s', $join['type'], $join['table'], $join['alias']); $condition = ''; if (isset($join['conditions']) && $join['conditions'] instanceof ExpressionInterface) { - $condition = $join['conditions']->sql($generator); + $condition = $join['conditions']->sql($binder); } if (strlen($condition)) { $joins .= " ON {$condition}"; @@ -270,20 +289,38 @@ protected function _buildJoinPart(array $parts, Query $query, ValueBinder $gener return $joins; } + /** + * Helper function to build the string representation of a window clause. + * + * @param array $parts List of windows to be transformed to string + * @param \Cake\Database\Query $query The query that is being compiled + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder + * @return string + */ + protected function _buildWindowPart(array $parts, Query $query, ValueBinder $binder): string + { + $windows = []; + foreach ($parts as $window) { + $windows[] = $window['name']->sql($binder) . ' AS (' . $window['window']->sql($binder) . ')'; + } + + return ' WINDOW ' . implode(', ', $windows); + } + /** * Helper function to generate SQL for SET expressions. * * @param array $parts List of keys & values to set. * @param \Cake\Database\Query $query The query that is being compiled - * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder * @return string */ - protected function _buildSetPart(array $parts, Query $query, ValueBinder $generator): string + protected function _buildSetPart(array $parts, Query $query, ValueBinder $binder): string { $set = []; foreach ($parts as $part) { if ($part instanceof ExpressionInterface) { - $part = $part->sql($generator); + $part = $part->sql($binder); } if ($part[0] === '(') { $part = substr($part, 1, -1); @@ -301,13 +338,13 @@ protected function _buildSetPart(array $parts, Query $query, ValueBinder $genera * * @param array $parts list of queries to be operated with UNION * @param \Cake\Database\Query $query The query that is being compiled - * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder * @return string */ - protected function _buildUnionPart(array $parts, Query $query, ValueBinder $generator): string + protected function _buildUnionPart(array $parts, Query $query, ValueBinder $binder): string { - $parts = array_map(function ($p) use ($generator) { - $p['query'] = $p['query']->sql($generator); + $parts = array_map(function ($p) use ($binder) { + $p['query'] = $p['query']->sql($binder); $p['query'] = $p['query'][0] === '(' ? trim($p['query'], '()') : $p['query']; $prefix = $p['all'] ? 'ALL ' : ''; if ($this->_orderedUnion) { @@ -329,14 +366,20 @@ protected function _buildUnionPart(array $parts, Query $query, ValueBinder $gene * * @param array $parts The insert parts. * @param \Cake\Database\Query $query The query that is being compiled - * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder * @return string SQL fragment. */ - protected function _buildInsertPart(array $parts, Query $query, ValueBinder $generator): string + protected function _buildInsertPart(array $parts, Query $query, ValueBinder $binder): string { + if (!isset($parts[0])) { + throw new DatabaseException( + 'Could not compile insert query. No table was specified. ' . + 'Use `into()` to define a table.' + ); + } $table = $parts[0]; - $columns = $this->_stringifyExpressions($parts[1], $generator); - $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $generator); + $columns = $this->_stringifyExpressions($parts[1], $binder); + $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $binder); return sprintf('INSERT%s INTO %s (%s)', $modifiers, $table, implode(', ', $columns)); } @@ -346,12 +389,12 @@ protected function _buildInsertPart(array $parts, Query $query, ValueBinder $gen * * @param array $parts The values parts. * @param \Cake\Database\Query $query The query that is being compiled - * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder * @return string SQL fragment. */ - protected function _buildValuesPart(array $parts, Query $query, ValueBinder $generator): string + protected function _buildValuesPart(array $parts, Query $query, ValueBinder $binder): string { - return implode('', $this->_stringifyExpressions($parts, $generator)); + return implode('', $this->_stringifyExpressions($parts, $binder)); } /** @@ -359,13 +402,13 @@ protected function _buildValuesPart(array $parts, Query $query, ValueBinder $gen * * @param array $parts The update parts. * @param \Cake\Database\Query $query The query that is being compiled - * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder * @return string SQL fragment. */ - protected function _buildUpdatePart(array $parts, Query $query, ValueBinder $generator): string + protected function _buildUpdatePart(array $parts, Query $query, ValueBinder $binder): string { - $table = $this->_stringifyExpressions($parts, $generator); - $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $generator); + $table = $this->_stringifyExpressions($parts, $binder); + $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $binder); return sprintf('UPDATE%s %s', $modifiers, implode(',', $table)); } @@ -375,16 +418,16 @@ protected function _buildUpdatePart(array $parts, Query $query, ValueBinder $gen * * @param array $parts The query modifier parts * @param \Cake\Database\Query $query The query that is being compiled - * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder * @return string SQL fragment. */ - protected function _buildModifierPart(array $parts, Query $query, ValueBinder $generator): string + protected function _buildModifierPart(array $parts, Query $query, ValueBinder $binder): string { if ($parts === []) { return ''; } - return ' ' . implode(' ', $this->_stringifyExpressions($parts, $generator, false)); + return ' ' . implode(' ', $this->_stringifyExpressions($parts, $binder, false)); } /** @@ -392,16 +435,16 @@ protected function _buildModifierPart(array $parts, Query $query, ValueBinder $g * into their string representation. * * @param array $expressions list of strings and ExpressionInterface objects - * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder * @param bool $wrap Whether to wrap each expression object with parenthesis * @return array */ - protected function _stringifyExpressions(array $expressions, ValueBinder $generator, bool $wrap = true): array + protected function _stringifyExpressions(array $expressions, ValueBinder $binder, bool $wrap = true): array { $result = []; foreach ($expressions as $k => $expression) { if ($expression instanceof ExpressionInterface) { - $value = $expression->sql($generator); + $value = $expression->sql($binder); $expression = $wrap ? '(' . $value . ')' : $value; } $result[$k] = $expression; diff --git a/app/vendor/cakephp/cakephp/src/Database/README.md b/app/vendor/cakephp/cakephp/src/Database/README.md index e4c513ba8..877c7b68d 100644 --- a/app/vendor/cakephp/cakephp/src/Database/README.md +++ b/app/vendor/cakephp/cakephp/src/Database/README.md @@ -342,7 +342,7 @@ SELECT CONCAT(title, :c0) ...; ### Other SQL Clauses -Read of all other SQL clauses that the builder is capable of generating in the [official API docs](https://api.cakephp.org/3.x/class-Cake.Database.Query.html) +Read of all other SQL clauses that the builder is capable of generating in the [official API docs](https://api.cakephp.org/4.x/class-Cake.Database.Query.html) ### Getting Results out of a Query @@ -360,5 +360,5 @@ $results = $query->execute()->fetchAll('assoc'); ## Official API -You can read the official [official API docs](https://api.cakephp.org/3.x/namespace-Cake.Database.html) to learn more of what this library +You can read the official [official API docs](https://api.cakephp.org/4.x/namespace-Cake.Database.html) to learn more of what this library has to offer. diff --git a/app/vendor/cakephp/cakephp/src/Database/Retry/ErrorCodeWaitStrategy.php b/app/vendor/cakephp/cakephp/src/Database/Retry/ErrorCodeWaitStrategy.php new file mode 100644 index 000000000..564dcc414 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Retry/ErrorCodeWaitStrategy.php @@ -0,0 +1,69 @@ +errorCodes = $errorCodes; + $this->retryInterval = $retryInterval; + } + + /** + * @inheritDoc + */ + public function shouldRetry(Exception $exception, int $retryCount): bool + { + if ( + $exception instanceof PDOException && + $exception->errorInfo && + in_array($exception->errorInfo[1], $this->errorCodes) + ) { + if ($this->retryInterval > 0) { + sleep($this->retryInterval); + } + + return true; + } + + return false; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Database/Retry/ReconnectStrategy.php b/app/vendor/cakephp/cakephp/src/Database/Retry/ReconnectStrategy.php index 4bc549f44..fc85a1ef6 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Retry/ReconnectStrategy.php +++ b/app/vendor/cakephp/cakephp/src/Database/Retry/ReconnectStrategy.php @@ -72,12 +72,10 @@ public function __construct(Connection $connection) } /** + * {@inheritDoc} + * * Checks whether or not the exception was caused by a lost connection, * and returns true if it was able to successfully reconnect. - * - * @param \Exception $exception The exception to check for its message - * @param int $retryCount The number of times the action has been already called - * @return bool Whether or not it is OK to retry the action */ public function shouldRetry(Exception $exception, int $retryCount): bool { diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/BaseSchema.php b/app/vendor/cakephp/cakephp/src/Database/Schema/BaseSchema.php index bb1939118..46513170d 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/BaseSchema.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/BaseSchema.php @@ -1,285 +1,5 @@ connect(); - $this->_driver = $driver; - } - - /** - * Generate an ON clause for a foreign key. - * - * @param string $on The on clause - * @return string - */ - protected function _foreignOnClause(string $on): string - { - if ($on === TableSchema::ACTION_SET_NULL) { - return 'SET NULL'; - } - if ($on === TableSchema::ACTION_SET_DEFAULT) { - return 'SET DEFAULT'; - } - if ($on === TableSchema::ACTION_CASCADE) { - return 'CASCADE'; - } - if ($on === TableSchema::ACTION_RESTRICT) { - return 'RESTRICT'; - } - if ($on === TableSchema::ACTION_NO_ACTION) { - return 'NO ACTION'; - } - - throw new InvalidArgumentException('Invalid value for "on": ' . $on); - } - - /** - * Convert string on clauses to the abstract ones. - * - * @param string $clause The on clause to convert. - * @return string - */ - protected function _convertOnClause(string $clause): string - { - if ($clause === 'CASCADE' || $clause === 'RESTRICT') { - return strtolower($clause); - } - if ($clause === 'NO ACTION') { - return TableSchema::ACTION_NO_ACTION; - } - - return TableSchema::ACTION_SET_NULL; - } - - /** - * Convert foreign key constraints references to a valid - * stringified list - * - * @param string|array $references The referenced columns of a foreign key constraint statement - * @return string - */ - protected function _convertConstraintColumns($references): string - { - if (is_string($references)) { - return $this->_driver->quoteIdentifier($references); - } - - return implode(', ', array_map( - [$this->_driver, 'quoteIdentifier'], - $references - )); - } - - /** - * Generate the SQL to drop a table. - * - * @param \Cake\Database\Schema\TableSchema $schema Schema instance - * @return array SQL statements to drop a table. - */ - public function dropTableSql(TableSchema $schema): array - { - $sql = sprintf( - 'DROP TABLE %s', - $this->_driver->quoteIdentifier($schema->name()) - ); - - return [$sql]; - } - - /** - * Generate the SQL to list the tables. - * - * @param array $config The connection configuration to use for - * getting tables from. - * @return array An array of (sql, params) to execute. - */ - abstract public function listTablesSql(array $config): array; - - /** - * Generate the SQL to describe a table. - * - * @param string $tableName The table name to get information on. - * @param array $config The connection configuration. - * @return array An array of (sql, params) to execute. - */ - abstract public function describeColumnSql(string $tableName, array $config): array; - - /** - * Generate the SQL to describe the indexes in a table. - * - * @param string $tableName The table name to get information on. - * @param array $config The connection configuration. - * @return array An array of (sql, params) to execute. - */ - abstract public function describeIndexSql(string $tableName, array $config): array; - - /** - * Generate the SQL to describe the foreign keys in a table. - * - * @param string $tableName The table name to get information on. - * @param array $config The connection configuration. - * @return array An array of (sql, params) to execute. - */ - abstract public function describeForeignKeySql(string $tableName, array $config): array; - - /** - * Generate the SQL to describe table options - * - * @param string $tableName Table name. - * @param array $config The connection configuration. - * @return array SQL statements to get options for a table. - */ - public function describeOptionsSql(string $tableName, array $config): array - { - return ['', '']; - } - - /** - * Convert field description results into abstract schema fields. - * - * @param \Cake\Database\Schema\TableSchema $schema The table object to append fields to. - * @param array $row The row data from `describeColumnSql`. - * @return void - */ - abstract public function convertColumnDescription(TableSchema $schema, array $row): void; - - /** - * Convert an index description results into abstract schema indexes or constraints. - * - * @param \Cake\Database\Schema\TableSchema $schema The table object to append - * an index or constraint to. - * @param array $row The row data from `describeIndexSql`. - * @return void - */ - abstract public function convertIndexDescription(TableSchema $schema, array $row): void; - - /** - * Convert a foreign key description into constraints on the Table object. - * - * @param \Cake\Database\Schema\TableSchema $schema The table object to append - * a constraint to. - * @param array $row The row data from `describeForeignKeySql`. - * @return void - */ - abstract public function convertForeignKeyDescription(TableSchema $schema, array $row): void; - - /** - * Convert options data into table options. - * - * @param \Cake\Database\Schema\TableSchema $schema Table instance. - * @param array $row The row of data. - * @return void - */ - public function convertOptionsDescription(TableSchema $schema, array $row): void - { - } - - /** - * Generate the SQL to create a table. - * - * @param \Cake\Database\Schema\TableSchema $schema Table instance. - * @param string[] $columns The columns to go inside the table. - * @param string[] $constraints The constraints for the table. - * @param string[] $indexes The indexes for the table. - * @return string[] SQL statements to create a table. - */ - abstract public function createTableSql( - TableSchema $schema, - array $columns, - array $constraints, - array $indexes - ): array; - - /** - * Generate the SQL fragment for a single column in a table. - * - * @param \Cake\Database\Schema\TableSchema $schema The table instance the column is in. - * @param string $name The name of the column. - * @return string SQL fragment. - */ - abstract public function columnSql(TableSchema $schema, string $name): string; - - /** - * Generate the SQL queries needed to add foreign key constraints to the table - * - * @param \Cake\Database\Schema\TableSchema $schema The table instance the foreign key constraints are. - * @return array SQL fragment. - */ - abstract public function addConstraintSql(TableSchema $schema): array; - - /** - * Generate the SQL queries needed to drop foreign key constraints from the table - * - * @param \Cake\Database\Schema\TableSchema $schema The table instance the foreign key constraints are. - * @return array SQL fragment. - */ - abstract public function dropConstraintSql(TableSchema $schema): array; - - /** - * Generate the SQL fragments for defining table constraints. - * - * @param \Cake\Database\Schema\TableSchema $schema The table instance the column is in. - * @param string $name The name of the column. - * @return string SQL fragment. - */ - abstract public function constraintSql(TableSchema $schema, string $name): string; - - /** - * Generate the SQL fragment for a single index in a table. - * - * @param \Cake\Database\Schema\TableSchema $schema The table object the column is in. - * @param string $name The name of the column. - * @return string SQL fragment. - */ - abstract public function indexSql(TableSchema $schema, string $name): string; - - /** - * Generate the SQL to truncate a table. - * - * @param \Cake\Database\Schema\TableSchema $schema Table instance. - * @return array SQL statements to truncate a table. - */ - abstract public function truncateTableSql(TableSchema $schema): array; -} +// @deprecated 4.1.0 Load new class location and alias for old location +class_exists('Cake\Database\Schema\SchemaDialect'); diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/Collection.php b/app/vendor/cakephp/cakephp/src/Database/Schema/Collection.php index b214bdc08..0cf43cf66 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/Collection.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/Collection.php @@ -17,7 +17,7 @@ namespace Cake\Database\Schema; use Cake\Database\Connection; -use Cake\Database\Exception; +use Cake\Database\Exception\DatabaseException; use PDOException; /** @@ -38,7 +38,7 @@ class Collection implements CollectionInterface /** * Schema dialect instance. * - * @var \Cake\Database\Schema\BaseSchema + * @var \Cake\Database\Schema\SchemaDialect */ protected $_dialect; @@ -87,7 +87,7 @@ public function listTables(): array * @param string $name The name of the table to describe. * @param array $options The options to use, see above. * @return \Cake\Database\Schema\TableSchema Object with column metadata. - * @throws \Cake\Database\Exception when table cannot be described. + * @throws \Cake\Database\Exception\DatabaseException when table cannot be described. */ public function describe(string $name, array $options = []): TableSchemaInterface { @@ -99,7 +99,7 @@ public function describe(string $name, array $options = []): TableSchemaInterfac $this->_reflect('Column', $name, $config, $table); if (count($table->columns()) === 0) { - throw new Exception(sprintf('Cannot describe %s. It has 0 columns.', $name)); + throw new DatabaseException(sprintf('Cannot describe %s. It has 0 columns.', $name)); } $this->_reflect('Index', $name, $config, $table); @@ -117,15 +117,15 @@ public function describe(string $name, array $options = []): TableSchemaInterfac * @param array $config The config data. * @param \Cake\Database\Schema\TableSchema $schema The table schema instance. * @return void - * @throws \Cake\Database\Exception on query failure. - * @uses \Cake\Database\Schema\BaseSchema::describeColumnSql - * @uses \Cake\Database\Schema\BaseSchema::describeIndexSql - * @uses \Cake\Database\Schema\BaseSchema::describeForeignKeySql - * @uses \Cake\Database\Schema\BaseSchema::describeOptionsSql - * @uses \Cake\Database\Schema\BaseSchema::convertColumnDescription - * @uses \Cake\Database\Schema\BaseSchema::convertIndexDescription - * @uses \Cake\Database\Schema\BaseSchema::convertForeignKeyDescription - * @uses \Cake\Database\Schema\BaseSchema::convertOptionsDescription + * @throws \Cake\Database\Exception\DatabaseException on query failure. + * @uses \Cake\Database\Schema\SchemaDialect::describeColumnSql + * @uses \Cake\Database\Schema\SchemaDialect::describeIndexSql + * @uses \Cake\Database\Schema\SchemaDialect::describeForeignKeySql + * @uses \Cake\Database\Schema\SchemaDialect::describeOptionsSql + * @uses \Cake\Database\Schema\SchemaDialect::convertColumnDescription + * @uses \Cake\Database\Schema\SchemaDialect::convertIndexDescription + * @uses \Cake\Database\Schema\SchemaDialect::convertForeignKeyDescription + * @uses \Cake\Database\Schema\SchemaDialect::convertOptionsDescription */ protected function _reflect(string $stage, string $name, array $config, TableSchema $schema): void { @@ -139,7 +139,7 @@ protected function _reflect(string $stage, string $name, array $config, TableSch try { $statement = $this->_connection->execute($sql, $params); } catch (PDOException $e) { - throw new Exception($e->getMessage(), 500, $e); + throw new DatabaseException($e->getMessage(), 500, $e); } /** @psalm-suppress PossiblyFalseIterator */ foreach ($statement->fetchAll('assoc') as $row) { diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/CollectionInterface.php b/app/vendor/cakephp/cakephp/src/Database/Schema/CollectionInterface.php index 9bd58cac3..2e3189765 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/CollectionInterface.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/CollectionInterface.php @@ -45,7 +45,7 @@ public function listTables(): array; * @param string $name The name of the table to describe. * @param array $options The options to use, see above. * @return \Cake\Database\Schema\TableSchemaInterface Object with column metadata. - * @throws \Cake\Database\Exception when table cannot be described. + * @throws \Cake\Database\Exception\DatabaseException when table cannot be described. */ public function describe(string $name, array $options = []): TableSchemaInterface; } diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/MysqlSchema.php b/app/vendor/cakephp/cakephp/src/Database/Schema/MysqlSchema.php index 06ab67513..237d6362b 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/MysqlSchema.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/MysqlSchema.php @@ -1,623 +1,5 @@ _driver->quoteIdentifier($config['database']), []]; - } - - /** - * @inheritDoc - */ - public function describeColumnSql(string $tableName, array $config): array - { - return ['SHOW FULL COLUMNS FROM ' . $this->_driver->quoteIdentifier($tableName), []]; - } - - /** - * @inheritDoc - */ - public function describeIndexSql(string $tableName, array $config): array - { - return ['SHOW INDEXES FROM ' . $this->_driver->quoteIdentifier($tableName), []]; - } - - /** - * @inheritDoc - */ - public function describeOptionsSql(string $tableName, array $config): array - { - return ['SHOW TABLE STATUS WHERE Name = ?', [$tableName]]; - } - - /** - * @inheritDoc - */ - public function convertOptionsDescription(TableSchema $schema, array $row): void - { - $schema->setOptions([ - 'engine' => $row['Engine'], - 'collation' => $row['Collation'], - ]); - } - - /** - * Convert a MySQL column type into an abstract type. - * - * The returned type will be a type that Cake\Database\TypeFactory can handle. - * - * @param string $column The column type + length - * @return array Array of column information. - * @throws \Cake\Database\Exception When column type cannot be parsed. - */ - protected function _convertColumn(string $column): array - { - preg_match('/([a-z]+)(?:\(([0-9,]+)\))?\s*([a-z]+)?/i', $column, $matches); - if (empty($matches)) { - throw new Exception(sprintf('Unable to parse column type from "%s"', $column)); - } - - $col = strtolower($matches[1]); - $length = $precision = null; - if (isset($matches[2]) && strlen($matches[2])) { - $length = $matches[2]; - if (strpos($matches[2], ',') !== false) { - [$length, $precision] = explode(',', $length); - } - $length = (int)$length; - $precision = (int)$precision; - } - - if (in_array($col, ['date', 'time'])) { - return ['type' => $col, 'length' => null]; - } - if (in_array($col, ['datetime', 'timestamp'])) { - $typeName = $col; - if ($length > 0) { - $typeName = $col . 'fractional'; - } - - return ['type' => $typeName, 'length' => null, 'precision' => $length]; - } - - if (($col === 'tinyint' && $length === 1) || $col === 'boolean') { - return ['type' => TableSchema::TYPE_BOOLEAN, 'length' => null]; - } - - $unsigned = (isset($matches[3]) && strtolower($matches[3]) === 'unsigned'); - if (strpos($col, 'bigint') !== false || $col === 'bigint') { - return ['type' => TableSchema::TYPE_BIGINTEGER, 'length' => $length, 'unsigned' => $unsigned]; - } - if ($col === 'tinyint') { - return ['type' => TableSchema::TYPE_TINYINTEGER, 'length' => $length, 'unsigned' => $unsigned]; - } - if ($col === 'smallint') { - return ['type' => TableSchema::TYPE_SMALLINTEGER, 'length' => $length, 'unsigned' => $unsigned]; - } - if (in_array($col, ['int', 'integer', 'mediumint'])) { - return ['type' => TableSchema::TYPE_INTEGER, 'length' => $length, 'unsigned' => $unsigned]; - } - if ($col === 'char' && $length === 36) { - return ['type' => TableSchema::TYPE_UUID, 'length' => null]; - } - if ($col === 'char') { - return ['type' => TableSchema::TYPE_CHAR, 'length' => $length]; - } - if (strpos($col, 'char') !== false) { - return ['type' => TableSchema::TYPE_STRING, 'length' => $length]; - } - if (strpos($col, 'text') !== false) { - $lengthName = substr($col, 0, -4); - $length = TableSchema::$columnLengths[$lengthName] ?? null; - - return ['type' => TableSchema::TYPE_TEXT, 'length' => $length]; - } - if ($col === 'binary' && $length === 16) { - return ['type' => TableSchema::TYPE_BINARY_UUID, 'length' => null]; - } - if (strpos($col, 'blob') !== false || in_array($col, ['binary', 'varbinary'])) { - $lengthName = substr($col, 0, -4); - $length = TableSchema::$columnLengths[$lengthName] ?? $length; - - return ['type' => TableSchema::TYPE_BINARY, 'length' => $length]; - } - if (strpos($col, 'float') !== false || strpos($col, 'double') !== false) { - return [ - 'type' => TableSchema::TYPE_FLOAT, - 'length' => $length, - 'precision' => $precision, - 'unsigned' => $unsigned, - ]; - } - if (strpos($col, 'decimal') !== false) { - return [ - 'type' => TableSchema::TYPE_DECIMAL, - 'length' => $length, - 'precision' => $precision, - 'unsigned' => $unsigned, - ]; - } - - if (strpos($col, 'json') !== false) { - return ['type' => TableSchema::TYPE_JSON, 'length' => null]; - } - - return ['type' => TableSchema::TYPE_STRING, 'length' => null]; - } - - /** - * @inheritDoc - */ - public function convertColumnDescription(TableSchema $schema, array $row): void - { - $field = $this->_convertColumn($row['Type']); - $field += [ - 'null' => $row['Null'] === 'YES', - 'default' => $row['Default'], - 'collate' => $row['Collation'], - 'comment' => $row['Comment'], - ]; - if (isset($row['Extra']) && $row['Extra'] === 'auto_increment') { - $field['autoIncrement'] = true; - } - $schema->addColumn($row['Field'], $field); - } - - /** - * @inheritDoc - */ - public function convertIndexDescription(TableSchema $schema, array $row): void - { - $type = null; - $columns = $length = []; - - $name = $row['Key_name']; - if ($name === 'PRIMARY') { - $name = $type = TableSchema::CONSTRAINT_PRIMARY; - } - - $columns[] = $row['Column_name']; - - if ($row['Index_type'] === 'FULLTEXT') { - $type = TableSchema::INDEX_FULLTEXT; - } elseif ((int)$row['Non_unique'] === 0 && $type !== 'primary') { - $type = TableSchema::CONSTRAINT_UNIQUE; - } elseif ($type !== 'primary') { - $type = TableSchema::INDEX_INDEX; - } - - if (!empty($row['Sub_part'])) { - $length[$row['Column_name']] = $row['Sub_part']; - } - $isIndex = ( - $type === TableSchema::INDEX_INDEX || - $type === TableSchema::INDEX_FULLTEXT - ); - if ($isIndex) { - $existing = $schema->getIndex($name); - } else { - $existing = $schema->getConstraint($name); - } - - // MySQL multi column indexes come back as multiple rows. - if (!empty($existing)) { - $columns = array_merge($existing['columns'], $columns); - $length = array_merge($existing['length'], $length); - } - if ($isIndex) { - $schema->addIndex($name, [ - 'type' => $type, - 'columns' => $columns, - 'length' => $length, - ]); - } else { - $schema->addConstraint($name, [ - 'type' => $type, - 'columns' => $columns, - 'length' => $length, - ]); - } - } - - /** - * @inheritDoc - */ - public function describeForeignKeySql(string $tableName, array $config): array - { - $sql = 'SELECT * FROM information_schema.key_column_usage AS kcu - INNER JOIN information_schema.referential_constraints AS rc - ON ( - kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME - AND kcu.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA - ) - WHERE kcu.TABLE_SCHEMA = ? AND kcu.TABLE_NAME = ? AND rc.TABLE_NAME = ?'; - - return [$sql, [$config['database'], $tableName, $tableName]]; - } - - /** - * @inheritDoc - */ - public function convertForeignKeyDescription(TableSchema $schema, array $row): void - { - $data = [ - 'type' => TableSchema::CONSTRAINT_FOREIGN, - 'columns' => [$row['COLUMN_NAME']], - 'references' => [$row['REFERENCED_TABLE_NAME'], $row['REFERENCED_COLUMN_NAME']], - 'update' => $this->_convertOnClause($row['UPDATE_RULE']), - 'delete' => $this->_convertOnClause($row['DELETE_RULE']), - ]; - $name = $row['CONSTRAINT_NAME']; - $schema->addConstraint($name, $data); - } - - /** - * @inheritDoc - */ - public function truncateTableSql(TableSchema $schema): array - { - return [sprintf('TRUNCATE TABLE `%s`', $schema->name())]; - } - - /** - * @inheritDoc - */ - public function createTableSql(TableSchema $schema, array $columns, array $constraints, array $indexes): array - { - $content = implode(",\n", array_merge($columns, $constraints, $indexes)); - $temporary = $schema->isTemporary() ? ' TEMPORARY ' : ' '; - $content = sprintf("CREATE%sTABLE `%s` (\n%s\n)", $temporary, $schema->name(), $content); - $options = $schema->getOptions(); - if (isset($options['engine'])) { - $content .= sprintf(' ENGINE=%s', $options['engine']); - } - if (isset($options['charset'])) { - $content .= sprintf(' DEFAULT CHARSET=%s', $options['charset']); - } - if (isset($options['collate'])) { - $content .= sprintf(' COLLATE=%s', $options['collate']); - } - - return [$content]; - } - - /** - * @inheritDoc - */ - public function columnSql(TableSchema $schema, string $name): string - { - /** @var array $data */ - $data = $schema->getColumn($name); - $out = $this->_driver->quoteIdentifier($name); - $nativeJson = $this->_driver->supportsNativeJson(); - - $typeMap = [ - TableSchema::TYPE_TINYINTEGER => ' TINYINT', - TableSchema::TYPE_SMALLINTEGER => ' SMALLINT', - TableSchema::TYPE_INTEGER => ' INTEGER', - TableSchema::TYPE_BIGINTEGER => ' BIGINT', - TableSchema::TYPE_BINARY_UUID => ' BINARY(16)', - TableSchema::TYPE_BOOLEAN => ' BOOLEAN', - TableSchema::TYPE_FLOAT => ' FLOAT', - TableSchema::TYPE_DECIMAL => ' DECIMAL', - TableSchema::TYPE_DATE => ' DATE', - TableSchema::TYPE_TIME => ' TIME', - TableSchema::TYPE_DATETIME => ' DATETIME', - TableSchema::TYPE_DATETIME_FRACTIONAL => ' DATETIME', - TableSchema::TYPE_TIMESTAMP => ' TIMESTAMP', - TableSchema::TYPE_TIMESTAMP_FRACTIONAL => ' TIMESTAMP', - TableSchema::TYPE_TIMESTAMP_TIMEZONE => ' TIMESTAMP', - TableSchema::TYPE_CHAR => ' CHAR', - TableSchema::TYPE_UUID => ' CHAR(36)', - TableSchema::TYPE_JSON => $nativeJson ? ' JSON' : ' LONGTEXT', - ]; - $specialMap = [ - 'string' => true, - 'text' => true, - 'char' => true, - 'binary' => true, - ]; - if (isset($typeMap[$data['type']])) { - $out .= $typeMap[$data['type']]; - } - if (isset($specialMap[$data['type']])) { - switch ($data['type']) { - case TableSchema::TYPE_STRING: - $out .= ' VARCHAR'; - if (!isset($data['length'])) { - $data['length'] = 255; - } - break; - case TableSchema::TYPE_TEXT: - $isKnownLength = in_array($data['length'], TableSchema::$columnLengths); - if (empty($data['length']) || !$isKnownLength) { - $out .= ' TEXT'; - break; - } - - /** @var string $length */ - $length = array_search($data['length'], TableSchema::$columnLengths); - $out .= ' ' . strtoupper($length) . 'TEXT'; - - break; - case TableSchema::TYPE_BINARY: - $isKnownLength = in_array($data['length'], TableSchema::$columnLengths); - if ($isKnownLength) { - /** @var string $length */ - $length = array_search($data['length'], TableSchema::$columnLengths); - $out .= ' ' . strtoupper($length) . 'BLOB'; - break; - } - - if (empty($data['length'])) { - $out .= ' BLOB'; - break; - } - - if ($data['length'] > 2) { - $out .= ' VARBINARY(' . $data['length'] . ')'; - } else { - $out .= ' BINARY(' . $data['length'] . ')'; - } - break; - } - } - $hasLength = [ - TableSchema::TYPE_INTEGER, - TableSchema::TYPE_CHAR, - TableSchema::TYPE_SMALLINTEGER, - TableSchema::TYPE_TINYINTEGER, - TableSchema::TYPE_STRING, - ]; - if (in_array($data['type'], $hasLength, true) && isset($data['length'])) { - $out .= '(' . $data['length'] . ')'; - } - - $lengthAndPrecisionTypes = [TableSchema::TYPE_FLOAT, TableSchema::TYPE_DECIMAL]; - if (in_array($data['type'], $lengthAndPrecisionTypes, true) && isset($data['length'])) { - if (isset($data['precision'])) { - $out .= '(' . (int)$data['length'] . ',' . (int)$data['precision'] . ')'; - } else { - $out .= '(' . (int)$data['length'] . ')'; - } - } - - $precisionTypes = [TableSchema::TYPE_DATETIME_FRACTIONAL, TableSchema::TYPE_TIMESTAMP_FRACTIONAL]; - if (in_array($data['type'], $precisionTypes, true) && isset($data['precision'])) { - $out .= '(' . (int)$data['precision'] . ')'; - } - - $hasUnsigned = [ - TableSchema::TYPE_TINYINTEGER, - TableSchema::TYPE_SMALLINTEGER, - TableSchema::TYPE_INTEGER, - TableSchema::TYPE_BIGINTEGER, - TableSchema::TYPE_FLOAT, - TableSchema::TYPE_DECIMAL, - ]; - if ( - in_array($data['type'], $hasUnsigned, true) && - isset($data['unsigned']) && - $data['unsigned'] === true - ) { - $out .= ' UNSIGNED'; - } - - $hasCollate = [ - TableSchema::TYPE_TEXT, - TableSchema::TYPE_CHAR, - TableSchema::TYPE_STRING, - ]; - if (in_array($data['type'], $hasCollate, true) && isset($data['collate']) && $data['collate'] !== '') { - $out .= ' COLLATE ' . $data['collate']; - } - - if (isset($data['null']) && $data['null'] === false) { - $out .= ' NOT NULL'; - } - $addAutoIncrement = ( - (array)$schema->getPrimaryKey() === [$name] && - !$schema->hasAutoincrement() && - !isset($data['autoIncrement']) - ); - if ( - in_array($data['type'], [TableSchema::TYPE_INTEGER, TableSchema::TYPE_BIGINTEGER]) && - ( - $data['autoIncrement'] === true || - $addAutoIncrement - ) - ) { - $out .= ' AUTO_INCREMENT'; - } - - $timestampTypes = [ - TableSchema::TYPE_TIMESTAMP, - TableSchema::TYPE_TIMESTAMP_FRACTIONAL, - TableSchema::TYPE_TIMESTAMP_TIMEZONE, - ]; - if (isset($data['null']) && $data['null'] === true && in_array($data['type'], $timestampTypes, true)) { - $out .= ' NULL'; - unset($data['default']); - } - - $dateTimeTypes = [ - TableSchema::TYPE_DATETIME, - TableSchema::TYPE_DATETIME_FRACTIONAL, - TableSchema::TYPE_TIMESTAMP, - TableSchema::TYPE_TIMESTAMP_FRACTIONAL, - TableSchema::TYPE_TIMESTAMP_TIMEZONE, - ]; - if ( - isset($data['default']) && - in_array($data['type'], $dateTimeTypes) && - strpos(strtolower($data['default']), 'current_timestamp') !== false - ) { - $out .= ' DEFAULT CURRENT_TIMESTAMP'; - if (isset($data['precision'])) { - $out .= '(' . $data['precision'] . ')'; - } - unset($data['default']); - } - if (isset($data['default'])) { - $out .= ' DEFAULT ' . $this->_driver->schemaValue($data['default']); - unset($data['default']); - } - if (isset($data['comment']) && $data['comment'] !== '') { - $out .= ' COMMENT ' . $this->_driver->schemaValue($data['comment']); - } - - return $out; - } - - /** - * @inheritDoc - */ - public function constraintSql(TableSchema $schema, string $name): string - { - /** @var array $data */ - $data = $schema->getConstraint($name); - if ($data['type'] === TableSchema::CONSTRAINT_PRIMARY) { - $columns = array_map( - [$this->_driver, 'quoteIdentifier'], - $data['columns'] - ); - - return sprintf('PRIMARY KEY (%s)', implode(', ', $columns)); - } - - $out = ''; - if ($data['type'] === TableSchema::CONSTRAINT_UNIQUE) { - $out = 'UNIQUE KEY '; - } - if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) { - $out = 'CONSTRAINT '; - } - $out .= $this->_driver->quoteIdentifier($name); - - return $this->_keySql($out, $data); - } - - /** - * @inheritDoc - */ - public function addConstraintSql(TableSchema $schema): array - { - $sqlPattern = 'ALTER TABLE %s ADD %s;'; - $sql = []; - - foreach ($schema->constraints() as $name) { - /** @var array $constraint */ - $constraint = $schema->getConstraint($name); - if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) { - $tableName = $this->_driver->quoteIdentifier($schema->name()); - $sql[] = sprintf($sqlPattern, $tableName, $this->constraintSql($schema, $name)); - } - } - - return $sql; - } - - /** - * @inheritDoc - */ - public function dropConstraintSql(TableSchema $schema): array - { - $sqlPattern = 'ALTER TABLE %s DROP FOREIGN KEY %s;'; - $sql = []; - - foreach ($schema->constraints() as $name) { - /** @var array $constraint */ - $constraint = $schema->getConstraint($name); - if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) { - $tableName = $this->_driver->quoteIdentifier($schema->name()); - $constraintName = $this->_driver->quoteIdentifier($name); - $sql[] = sprintf($sqlPattern, $tableName, $constraintName); - } - } - - return $sql; - } - - /** - * @inheritDoc - */ - public function indexSql(TableSchema $schema, string $name): string - { - /** @var array $data */ - $data = $schema->getIndex($name); - $out = ''; - if ($data['type'] === TableSchema::INDEX_INDEX) { - $out = 'KEY '; - } - if ($data['type'] === TableSchema::INDEX_FULLTEXT) { - $out = 'FULLTEXT KEY '; - } - $out .= $this->_driver->quoteIdentifier($name); - - return $this->_keySql($out, $data); - } - - /** - * Helper method for generating key SQL snippets. - * - * @param string $prefix The key prefix - * @param array $data Key data. - * @return string - */ - protected function _keySql(string $prefix, array $data): string - { - $columns = array_map( - [$this->_driver, 'quoteIdentifier'], - $data['columns'] - ); - foreach ($data['columns'] as $i => $column) { - if (isset($data['length'][$column])) { - $columns[$i] .= sprintf('(%d)', $data['length'][$column]); - } - } - if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) { - return $prefix . sprintf( - ' FOREIGN KEY (%s) REFERENCES %s (%s) ON UPDATE %s ON DELETE %s', - implode(', ', $columns), - $this->_driver->quoteIdentifier($data['references'][0]), - $this->_convertConstraintColumns($data['references'][1]), - $this->_foreignOnClause($data['update']), - $this->_foreignOnClause($data['delete']) - ); - } - - return $prefix . ' (' . implode(', ', $columns) . ')'; - } -} +// @deprecated 4.1.0 Load new class location and alias for old location +class_exists('Cake\Database\Schema\MysqlSchemaDialect'); diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/MysqlSchemaDialect.php b/app/vendor/cakephp/cakephp/src/Database/Schema/MysqlSchemaDialect.php new file mode 100644 index 000000000..277555648 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/MysqlSchemaDialect.php @@ -0,0 +1,631 @@ +_driver->quoteIdentifier($config['database']), []]; + } + + /** + * @inheritDoc + */ + public function describeColumnSql(string $tableName, array $config): array + { + return ['SHOW FULL COLUMNS FROM ' . $this->_driver->quoteIdentifier($tableName), []]; + } + + /** + * @inheritDoc + */ + public function describeIndexSql(string $tableName, array $config): array + { + return ['SHOW INDEXES FROM ' . $this->_driver->quoteIdentifier($tableName), []]; + } + + /** + * @inheritDoc + */ + public function describeOptionsSql(string $tableName, array $config): array + { + return ['SHOW TABLE STATUS WHERE Name = ?', [$tableName]]; + } + + /** + * @inheritDoc + */ + public function convertOptionsDescription(TableSchema $schema, array $row): void + { + $schema->setOptions([ + 'engine' => $row['Engine'], + 'collation' => $row['Collation'], + ]); + } + + /** + * Convert a MySQL column type into an abstract type. + * + * The returned type will be a type that Cake\Database\TypeFactory can handle. + * + * @param string $column The column type + length + * @return array Array of column information. + * @throws \Cake\Database\Exception\DatabaseException When column type cannot be parsed. + */ + protected function _convertColumn(string $column): array + { + preg_match('/([a-z]+)(?:\(([0-9,]+)\))?\s*([a-z]+)?/i', $column, $matches); + if (empty($matches)) { + throw new DatabaseException(sprintf('Unable to parse column type from "%s"', $column)); + } + + $col = strtolower($matches[1]); + $length = $precision = null; + if (isset($matches[2]) && strlen($matches[2])) { + $length = $matches[2]; + if (strpos($matches[2], ',') !== false) { + [$length, $precision] = explode(',', $length); + } + $length = (int)$length; + $precision = (int)$precision; + } + + if (in_array($col, ['date', 'time'])) { + return ['type' => $col, 'length' => null]; + } + if (in_array($col, ['datetime', 'timestamp'])) { + $typeName = $col; + if ($length > 0) { + $typeName = $col . 'fractional'; + } + + return ['type' => $typeName, 'length' => null, 'precision' => $length]; + } + + if (($col === 'tinyint' && $length === 1) || $col === 'boolean') { + return ['type' => TableSchema::TYPE_BOOLEAN, 'length' => null]; + } + + $unsigned = (isset($matches[3]) && strtolower($matches[3]) === 'unsigned'); + if (strpos($col, 'bigint') !== false || $col === 'bigint') { + return ['type' => TableSchema::TYPE_BIGINTEGER, 'length' => null, 'unsigned' => $unsigned]; + } + if ($col === 'tinyint') { + return ['type' => TableSchema::TYPE_TINYINTEGER, 'length' => null, 'unsigned' => $unsigned]; + } + if ($col === 'smallint') { + return ['type' => TableSchema::TYPE_SMALLINTEGER, 'length' => null, 'unsigned' => $unsigned]; + } + if (in_array($col, ['int', 'integer', 'mediumint'])) { + return ['type' => TableSchema::TYPE_INTEGER, 'length' => null, 'unsigned' => $unsigned]; + } + if ($col === 'char' && $length === 36) { + return ['type' => TableSchema::TYPE_UUID, 'length' => null]; + } + if ($col === 'char') { + return ['type' => TableSchema::TYPE_CHAR, 'length' => $length]; + } + if (strpos($col, 'char') !== false) { + return ['type' => TableSchema::TYPE_STRING, 'length' => $length]; + } + if (strpos($col, 'text') !== false) { + $lengthName = substr($col, 0, -4); + $length = TableSchema::$columnLengths[$lengthName] ?? null; + + return ['type' => TableSchema::TYPE_TEXT, 'length' => $length]; + } + if ($col === 'binary' && $length === 16) { + return ['type' => TableSchema::TYPE_BINARY_UUID, 'length' => null]; + } + if (strpos($col, 'blob') !== false || in_array($col, ['binary', 'varbinary'])) { + $lengthName = substr($col, 0, -4); + $length = TableSchema::$columnLengths[$lengthName] ?? $length; + + return ['type' => TableSchema::TYPE_BINARY, 'length' => $length]; + } + if (strpos($col, 'float') !== false || strpos($col, 'double') !== false) { + return [ + 'type' => TableSchema::TYPE_FLOAT, + 'length' => $length, + 'precision' => $precision, + 'unsigned' => $unsigned, + ]; + } + if (strpos($col, 'decimal') !== false) { + return [ + 'type' => TableSchema::TYPE_DECIMAL, + 'length' => $length, + 'precision' => $precision, + 'unsigned' => $unsigned, + ]; + } + + if (strpos($col, 'json') !== false) { + return ['type' => TableSchema::TYPE_JSON, 'length' => null]; + } + + return ['type' => TableSchema::TYPE_STRING, 'length' => null]; + } + + /** + * @inheritDoc + */ + public function convertColumnDescription(TableSchema $schema, array $row): void + { + $field = $this->_convertColumn($row['Type']); + $field += [ + 'null' => $row['Null'] === 'YES', + 'default' => $row['Default'], + 'collate' => $row['Collation'], + 'comment' => $row['Comment'], + ]; + if (isset($row['Extra']) && $row['Extra'] === 'auto_increment') { + $field['autoIncrement'] = true; + } + $schema->addColumn($row['Field'], $field); + } + + /** + * @inheritDoc + */ + public function convertIndexDescription(TableSchema $schema, array $row): void + { + $type = null; + $columns = $length = []; + + $name = $row['Key_name']; + if ($name === 'PRIMARY') { + $name = $type = TableSchema::CONSTRAINT_PRIMARY; + } + + $columns[] = $row['Column_name']; + + if ($row['Index_type'] === 'FULLTEXT') { + $type = TableSchema::INDEX_FULLTEXT; + } elseif ((int)$row['Non_unique'] === 0 && $type !== 'primary') { + $type = TableSchema::CONSTRAINT_UNIQUE; + } elseif ($type !== 'primary') { + $type = TableSchema::INDEX_INDEX; + } + + if (!empty($row['Sub_part'])) { + $length[$row['Column_name']] = $row['Sub_part']; + } + $isIndex = ( + $type === TableSchema::INDEX_INDEX || + $type === TableSchema::INDEX_FULLTEXT + ); + if ($isIndex) { + $existing = $schema->getIndex($name); + } else { + $existing = $schema->getConstraint($name); + } + + // MySQL multi column indexes come back as multiple rows. + if (!empty($existing)) { + $columns = array_merge($existing['columns'], $columns); + $length = array_merge($existing['length'], $length); + } + if ($isIndex) { + $schema->addIndex($name, [ + 'type' => $type, + 'columns' => $columns, + 'length' => $length, + ]); + } else { + $schema->addConstraint($name, [ + 'type' => $type, + 'columns' => $columns, + 'length' => $length, + ]); + } + } + + /** + * @inheritDoc + */ + public function describeForeignKeySql(string $tableName, array $config): array + { + $sql = 'SELECT * FROM information_schema.key_column_usage AS kcu + INNER JOIN information_schema.referential_constraints AS rc + ON ( + kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME + AND kcu.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA + ) + WHERE kcu.TABLE_SCHEMA = ? AND kcu.TABLE_NAME = ? AND rc.TABLE_NAME = ? + ORDER BY kcu.ORDINAL_POSITION ASC'; + + return [$sql, [$config['database'], $tableName, $tableName]]; + } + + /** + * @inheritDoc + */ + public function convertForeignKeyDescription(TableSchema $schema, array $row): void + { + $data = [ + 'type' => TableSchema::CONSTRAINT_FOREIGN, + 'columns' => [$row['COLUMN_NAME']], + 'references' => [$row['REFERENCED_TABLE_NAME'], $row['REFERENCED_COLUMN_NAME']], + 'update' => $this->_convertOnClause($row['UPDATE_RULE']), + 'delete' => $this->_convertOnClause($row['DELETE_RULE']), + ]; + $name = $row['CONSTRAINT_NAME']; + $schema->addConstraint($name, $data); + } + + /** + * @inheritDoc + */ + public function truncateTableSql(TableSchema $schema): array + { + return [sprintf('TRUNCATE TABLE `%s`', $schema->name())]; + } + + /** + * @inheritDoc + */ + public function createTableSql(TableSchema $schema, array $columns, array $constraints, array $indexes): array + { + $content = implode(",\n", array_merge($columns, $constraints, $indexes)); + $temporary = $schema->isTemporary() ? ' TEMPORARY ' : ' '; + $content = sprintf("CREATE%sTABLE `%s` (\n%s\n)", $temporary, $schema->name(), $content); + $options = $schema->getOptions(); + if (isset($options['engine'])) { + $content .= sprintf(' ENGINE=%s', $options['engine']); + } + if (isset($options['charset'])) { + $content .= sprintf(' DEFAULT CHARSET=%s', $options['charset']); + } + if (isset($options['collate'])) { + $content .= sprintf(' COLLATE=%s', $options['collate']); + } + + return [$content]; + } + + /** + * @inheritDoc + */ + public function columnSql(TableSchema $schema, string $name): string + { + /** @var array $data */ + $data = $schema->getColumn($name); + $out = $this->_driver->quoteIdentifier($name); + $nativeJson = $this->_driver->supportsNativeJson(); + + $typeMap = [ + TableSchema::TYPE_TINYINTEGER => ' TINYINT', + TableSchema::TYPE_SMALLINTEGER => ' SMALLINT', + TableSchema::TYPE_INTEGER => ' INTEGER', + TableSchema::TYPE_BIGINTEGER => ' BIGINT', + TableSchema::TYPE_BINARY_UUID => ' BINARY(16)', + TableSchema::TYPE_BOOLEAN => ' BOOLEAN', + TableSchema::TYPE_FLOAT => ' FLOAT', + TableSchema::TYPE_DECIMAL => ' DECIMAL', + TableSchema::TYPE_DATE => ' DATE', + TableSchema::TYPE_TIME => ' TIME', + TableSchema::TYPE_DATETIME => ' DATETIME', + TableSchema::TYPE_DATETIME_FRACTIONAL => ' DATETIME', + TableSchema::TYPE_TIMESTAMP => ' TIMESTAMP', + TableSchema::TYPE_TIMESTAMP_FRACTIONAL => ' TIMESTAMP', + TableSchema::TYPE_TIMESTAMP_TIMEZONE => ' TIMESTAMP', + TableSchema::TYPE_CHAR => ' CHAR', + TableSchema::TYPE_UUID => ' CHAR(36)', + TableSchema::TYPE_JSON => $nativeJson ? ' JSON' : ' LONGTEXT', + ]; + $specialMap = [ + 'string' => true, + 'text' => true, + 'char' => true, + 'binary' => true, + ]; + if (isset($typeMap[$data['type']])) { + $out .= $typeMap[$data['type']]; + } + if (isset($specialMap[$data['type']])) { + switch ($data['type']) { + case TableSchema::TYPE_STRING: + $out .= ' VARCHAR'; + if (!isset($data['length'])) { + $data['length'] = 255; + } + break; + case TableSchema::TYPE_TEXT: + $isKnownLength = in_array($data['length'], TableSchema::$columnLengths); + if (empty($data['length']) || !$isKnownLength) { + $out .= ' TEXT'; + break; + } + + /** @var string $length */ + $length = array_search($data['length'], TableSchema::$columnLengths); + $out .= ' ' . strtoupper($length) . 'TEXT'; + + break; + case TableSchema::TYPE_BINARY: + $isKnownLength = in_array($data['length'], TableSchema::$columnLengths); + if ($isKnownLength) { + /** @var string $length */ + $length = array_search($data['length'], TableSchema::$columnLengths); + $out .= ' ' . strtoupper($length) . 'BLOB'; + break; + } + + if (empty($data['length'])) { + $out .= ' BLOB'; + break; + } + + if ($data['length'] > 2) { + $out .= ' VARBINARY(' . $data['length'] . ')'; + } else { + $out .= ' BINARY(' . $data['length'] . ')'; + } + break; + } + } + $hasLength = [ + TableSchema::TYPE_INTEGER, + TableSchema::TYPE_CHAR, + TableSchema::TYPE_SMALLINTEGER, + TableSchema::TYPE_TINYINTEGER, + TableSchema::TYPE_STRING, + ]; + if (in_array($data['type'], $hasLength, true) && isset($data['length'])) { + $out .= '(' . $data['length'] . ')'; + } + + $lengthAndPrecisionTypes = [TableSchema::TYPE_FLOAT, TableSchema::TYPE_DECIMAL]; + if (in_array($data['type'], $lengthAndPrecisionTypes, true) && isset($data['length'])) { + if (isset($data['precision'])) { + $out .= '(' . (int)$data['length'] . ',' . (int)$data['precision'] . ')'; + } else { + $out .= '(' . (int)$data['length'] . ')'; + } + } + + $precisionTypes = [TableSchema::TYPE_DATETIME_FRACTIONAL, TableSchema::TYPE_TIMESTAMP_FRACTIONAL]; + if (in_array($data['type'], $precisionTypes, true) && isset($data['precision'])) { + $out .= '(' . (int)$data['precision'] . ')'; + } + + $hasUnsigned = [ + TableSchema::TYPE_TINYINTEGER, + TableSchema::TYPE_SMALLINTEGER, + TableSchema::TYPE_INTEGER, + TableSchema::TYPE_BIGINTEGER, + TableSchema::TYPE_FLOAT, + TableSchema::TYPE_DECIMAL, + ]; + if ( + in_array($data['type'], $hasUnsigned, true) && + isset($data['unsigned']) && + $data['unsigned'] === true + ) { + $out .= ' UNSIGNED'; + } + + $hasCollate = [ + TableSchema::TYPE_TEXT, + TableSchema::TYPE_CHAR, + TableSchema::TYPE_STRING, + ]; + if (in_array($data['type'], $hasCollate, true) && isset($data['collate']) && $data['collate'] !== '') { + $out .= ' COLLATE ' . $data['collate']; + } + + if (isset($data['null']) && $data['null'] === false) { + $out .= ' NOT NULL'; + } + $addAutoIncrement = ( + $schema->getPrimaryKey() === [$name] && + !$schema->hasAutoincrement() && + !isset($data['autoIncrement']) + ); + if ( + in_array($data['type'], [TableSchema::TYPE_INTEGER, TableSchema::TYPE_BIGINTEGER]) && + ( + $data['autoIncrement'] === true || + $addAutoIncrement + ) + ) { + $out .= ' AUTO_INCREMENT'; + } + + $timestampTypes = [ + TableSchema::TYPE_TIMESTAMP, + TableSchema::TYPE_TIMESTAMP_FRACTIONAL, + TableSchema::TYPE_TIMESTAMP_TIMEZONE, + ]; + if (isset($data['null']) && $data['null'] === true && in_array($data['type'], $timestampTypes, true)) { + $out .= ' NULL'; + unset($data['default']); + } + + $dateTimeTypes = [ + TableSchema::TYPE_DATETIME, + TableSchema::TYPE_DATETIME_FRACTIONAL, + TableSchema::TYPE_TIMESTAMP, + TableSchema::TYPE_TIMESTAMP_FRACTIONAL, + TableSchema::TYPE_TIMESTAMP_TIMEZONE, + ]; + if ( + isset($data['default']) && + in_array($data['type'], $dateTimeTypes) && + strpos(strtolower($data['default']), 'current_timestamp') !== false + ) { + $out .= ' DEFAULT CURRENT_TIMESTAMP'; + if (isset($data['precision'])) { + $out .= '(' . $data['precision'] . ')'; + } + unset($data['default']); + } + if (isset($data['default'])) { + $out .= ' DEFAULT ' . $this->_driver->schemaValue($data['default']); + unset($data['default']); + } + if (isset($data['comment']) && $data['comment'] !== '') { + $out .= ' COMMENT ' . $this->_driver->schemaValue($data['comment']); + } + + return $out; + } + + /** + * @inheritDoc + */ + public function constraintSql(TableSchema $schema, string $name): string + { + /** @var array $data */ + $data = $schema->getConstraint($name); + if ($data['type'] === TableSchema::CONSTRAINT_PRIMARY) { + $columns = array_map( + [$this->_driver, 'quoteIdentifier'], + $data['columns'] + ); + + return sprintf('PRIMARY KEY (%s)', implode(', ', $columns)); + } + + $out = ''; + if ($data['type'] === TableSchema::CONSTRAINT_UNIQUE) { + $out = 'UNIQUE KEY '; + } + if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) { + $out = 'CONSTRAINT '; + } + $out .= $this->_driver->quoteIdentifier($name); + + return $this->_keySql($out, $data); + } + + /** + * @inheritDoc + */ + public function addConstraintSql(TableSchema $schema): array + { + $sqlPattern = 'ALTER TABLE %s ADD %s;'; + $sql = []; + + foreach ($schema->constraints() as $name) { + /** @var array $constraint */ + $constraint = $schema->getConstraint($name); + if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) { + $tableName = $this->_driver->quoteIdentifier($schema->name()); + $sql[] = sprintf($sqlPattern, $tableName, $this->constraintSql($schema, $name)); + } + } + + return $sql; + } + + /** + * @inheritDoc + */ + public function dropConstraintSql(TableSchema $schema): array + { + $sqlPattern = 'ALTER TABLE %s DROP FOREIGN KEY %s;'; + $sql = []; + + foreach ($schema->constraints() as $name) { + /** @var array $constraint */ + $constraint = $schema->getConstraint($name); + if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) { + $tableName = $this->_driver->quoteIdentifier($schema->name()); + $constraintName = $this->_driver->quoteIdentifier($name); + $sql[] = sprintf($sqlPattern, $tableName, $constraintName); + } + } + + return $sql; + } + + /** + * @inheritDoc + */ + public function indexSql(TableSchema $schema, string $name): string + { + /** @var array $data */ + $data = $schema->getIndex($name); + $out = ''; + if ($data['type'] === TableSchema::INDEX_INDEX) { + $out = 'KEY '; + } + if ($data['type'] === TableSchema::INDEX_FULLTEXT) { + $out = 'FULLTEXT KEY '; + } + $out .= $this->_driver->quoteIdentifier($name); + + return $this->_keySql($out, $data); + } + + /** + * Helper method for generating key SQL snippets. + * + * @param string $prefix The key prefix + * @param array $data Key data. + * @return string + */ + protected function _keySql(string $prefix, array $data): string + { + $columns = array_map( + [$this->_driver, 'quoteIdentifier'], + $data['columns'] + ); + foreach ($data['columns'] as $i => $column) { + if (isset($data['length'][$column])) { + $columns[$i] .= sprintf('(%d)', $data['length'][$column]); + } + } + if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) { + return $prefix . sprintf( + ' FOREIGN KEY (%s) REFERENCES %s (%s) ON UPDATE %s ON DELETE %s', + implode(', ', $columns), + $this->_driver->quoteIdentifier($data['references'][0]), + $this->_convertConstraintColumns($data['references'][1]), + $this->_foreignOnClause($data['update']), + $this->_foreignOnClause($data['delete']) + ); + } + + return $prefix . ' (' . implode(', ', $columns) . ')'; + } +} + +// phpcs:disable +// Add backwards compatible alias. +class_alias('Cake\Database\Schema\MysqlSchemaDialect', 'Cake\Database\Schema\MysqlSchema'); +// phpcs:enable diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/PostgresSchema.php b/app/vendor/cakephp/cakephp/src/Database/Schema/PostgresSchema.php index 1e4ab2a88..c74646505 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/PostgresSchema.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/PostgresSchema.php @@ -1,656 +1,5 @@ $col, 'length' => null]; - } - if (in_array($col, ['timestamptz', 'timestamp with time zone'], true)) { - return ['type' => TableSchema::TYPE_TIMESTAMP_TIMEZONE, 'length' => null]; - } - if (strpos($col, 'timestamp') !== false) { - return ['type' => TableSchema::TYPE_TIMESTAMP_FRACTIONAL, 'length' => null]; - } - if (strpos($col, 'time') !== false) { - return ['type' => TableSchema::TYPE_TIME, 'length' => null]; - } - if ($col === 'serial' || $col === 'integer') { - return ['type' => TableSchema::TYPE_INTEGER, 'length' => 10]; - } - if ($col === 'bigserial' || $col === 'bigint') { - return ['type' => TableSchema::TYPE_BIGINTEGER, 'length' => 20]; - } - if ($col === 'smallint') { - return ['type' => TableSchema::TYPE_SMALLINTEGER, 'length' => 5]; - } - if ($col === 'inet') { - return ['type' => TableSchema::TYPE_STRING, 'length' => 39]; - } - if ($col === 'uuid') { - return ['type' => TableSchema::TYPE_UUID, 'length' => null]; - } - if ($col === 'char') { - return ['type' => TableSchema::TYPE_CHAR, 'length' => $length]; - } - if (strpos($col, 'character') !== false) { - return ['type' => TableSchema::TYPE_STRING, 'length' => $length]; - } - // money is 'string' as it includes arbitrary text content - // before the number value. - if (strpos($col, 'money') !== false || $col === 'string') { - return ['type' => TableSchema::TYPE_STRING, 'length' => $length]; - } - if (strpos($col, 'text') !== false) { - return ['type' => TableSchema::TYPE_TEXT, 'length' => null]; - } - if ($col === 'bytea') { - return ['type' => TableSchema::TYPE_BINARY, 'length' => null]; - } - if ($col === 'real' || strpos($col, 'double') !== false) { - return ['type' => TableSchema::TYPE_FLOAT, 'length' => null]; - } - if ( - strpos($col, 'numeric') !== false || - strpos($col, 'decimal') !== false - ) { - return ['type' => TableSchema::TYPE_DECIMAL, 'length' => null]; - } - - if (strpos($col, 'json') !== false) { - return ['type' => TableSchema::TYPE_JSON, 'length' => null]; - } - - $length = is_numeric($length) ? $length : null; - - return ['type' => TableSchema::TYPE_STRING, 'length' => $length]; - } - - /** - * @inheritDoc - */ - public function convertColumnDescription(TableSchema $schema, array $row): void - { - $field = $this->_convertColumn($row['type']); - - if ($field['type'] === TableSchema::TYPE_BOOLEAN) { - if ($row['default'] === 'true') { - $row['default'] = 1; - } - if ($row['default'] === 'false') { - $row['default'] = 0; - } - } - if (!empty($row['has_serial'])) { - $field['autoIncrement'] = true; - } - - $field += [ - 'default' => $this->_defaultValue($row['default']), - 'null' => $row['null'] === 'YES', - 'collate' => $row['collation_name'], - 'comment' => $row['comment'], - ]; - $field['length'] = $row['char_length'] ?: $field['length']; - - if ($field['type'] === 'numeric' || $field['type'] === 'decimal') { - $field['length'] = $row['column_precision']; - $field['precision'] = $row['column_scale'] ?: null; - } - - if ($field['type'] === TableSchema::TYPE_TIMESTAMP_FRACTIONAL) { - $field['precision'] = $row['datetime_precision']; - if ($field['precision'] === 0) { - $field['type'] = TableSchema::TYPE_TIMESTAMP; - } - } - - if ($field['type'] === TableSchema::TYPE_TIMESTAMP_TIMEZONE) { - $field['precision'] = $row['datetime_precision']; - } - - $schema->addColumn($row['name'], $field); - } - - /** - * Manipulate the default value. - * - * Postgres includes sequence data and casting information in default values. - * We need to remove those. - * - * @param string|int|null $default The default value. - * @return string|int|null - */ - protected function _defaultValue($default) - { - if (is_numeric($default) || $default === null) { - return $default; - } - // Sequences - if (strpos($default, 'nextval') === 0) { - return null; - } - - if (strpos($default, 'NULL::') === 0) { - return null; - } - - // Remove quotes and postgres casts - return preg_replace( - "/^'(.*)'(?:::.*)$/", - '$1', - $default - ); - } - - /** - * @inheritDoc - */ - public function describeIndexSql(string $tableName, array $config): array - { - $sql = 'SELECT - c2.relname, - a.attname, - i.indisprimary, - i.indisunique - FROM pg_catalog.pg_namespace n - INNER JOIN pg_catalog.pg_class c ON (n.oid = c.relnamespace) - INNER JOIN pg_catalog.pg_index i ON (c.oid = i.indrelid) - INNER JOIN pg_catalog.pg_class c2 ON (c2.oid = i.indexrelid) - INNER JOIN pg_catalog.pg_attribute a ON (a.attrelid = c.oid AND i.indrelid::regclass = a.attrelid::regclass) - WHERE n.nspname = ? - AND a.attnum = ANY(i.indkey) - AND c.relname = ? - ORDER BY i.indisprimary DESC, i.indisunique DESC, c.relname, a.attnum'; - - $schema = 'public'; - if (!empty($config['schema'])) { - $schema = $config['schema']; - } - - return [$sql, [$schema, $tableName]]; - } - - /** - * @inheritDoc - */ - public function convertIndexDescription(TableSchema $schema, array $row): void - { - $type = TableSchema::INDEX_INDEX; - $name = $row['relname']; - if ($row['indisprimary']) { - $name = $type = TableSchema::CONSTRAINT_PRIMARY; - } - if ($row['indisunique'] && $type === TableSchema::INDEX_INDEX) { - $type = TableSchema::CONSTRAINT_UNIQUE; - } - if ($type === TableSchema::CONSTRAINT_PRIMARY || $type === TableSchema::CONSTRAINT_UNIQUE) { - $this->_convertConstraint($schema, $name, $type, $row); - - return; - } - $index = $schema->getIndex($name); - if (!$index) { - $index = [ - 'type' => $type, - 'columns' => [], - ]; - } - $index['columns'][] = $row['attname']; - $schema->addIndex($name, $index); - } - - /** - * Add/update a constraint into the schema object. - * - * @param \Cake\Database\Schema\TableSchema $schema The table to update. - * @param string $name The index name. - * @param string $type The index type. - * @param array $row The metadata record to update with. - * @return void - */ - protected function _convertConstraint(TableSchema $schema, string $name, string $type, array $row): void - { - $constraint = $schema->getConstraint($name); - if (!$constraint) { - $constraint = [ - 'type' => $type, - 'columns' => [], - ]; - } - $constraint['columns'][] = $row['attname']; - $schema->addConstraint($name, $constraint); - } - - /** - * @inheritDoc - */ - public function describeForeignKeySql(string $tableName, array $config): array - { - // phpcs:disable Generic.Files.LineLength - $sql = 'SELECT - c.conname AS name, - c.contype AS type, - a.attname AS column_name, - c.confmatchtype AS match_type, - c.confupdtype AS on_update, - c.confdeltype AS on_delete, - c.confrelid::regclass AS references_table, - ab.attname AS references_field - FROM pg_catalog.pg_namespace n - INNER JOIN pg_catalog.pg_class cl ON (n.oid = cl.relnamespace) - INNER JOIN pg_catalog.pg_constraint c ON (n.oid = c.connamespace) - INNER JOIN pg_catalog.pg_attribute a ON (a.attrelid = cl.oid AND c.conrelid = a.attrelid AND a.attnum = ANY(c.conkey)) - INNER JOIN pg_catalog.pg_attribute ab ON (a.attrelid = cl.oid AND c.confrelid = ab.attrelid AND ab.attnum = ANY(c.confkey)) - WHERE n.nspname = ? - AND cl.relname = ? - ORDER BY name, a.attnum, ab.attnum DESC'; - // phpcs:enable Generic.Files.LineLength - - $schema = empty($config['schema']) ? 'public' : $config['schema']; - - return [$sql, [$schema, $tableName]]; - } - - /** - * @inheritDoc - */ - public function convertForeignKeyDescription(TableSchema $schema, array $row): void - { - $data = [ - 'type' => TableSchema::CONSTRAINT_FOREIGN, - 'columns' => $row['column_name'], - 'references' => [$row['references_table'], $row['references_field']], - 'update' => $this->_convertOnClause($row['on_update']), - 'delete' => $this->_convertOnClause($row['on_delete']), - ]; - $schema->addConstraint($row['name'], $data); - } - - /** - * @inheritDoc - */ - protected function _convertOnClause(string $clause): string - { - if ($clause === 'r') { - return TableSchema::ACTION_RESTRICT; - } - if ($clause === 'a') { - return TableSchema::ACTION_NO_ACTION; - } - if ($clause === 'c') { - return TableSchema::ACTION_CASCADE; - } - - return TableSchema::ACTION_SET_NULL; - } - - /** - * @inheritDoc - */ - public function columnSql(TableSchema $schema, string $name): string - { - /** @var array $data */ - $data = $schema->getColumn($name); - $out = $this->_driver->quoteIdentifier($name); - $typeMap = [ - TableSchema::TYPE_TINYINTEGER => ' SMALLINT', - TableSchema::TYPE_SMALLINTEGER => ' SMALLINT', - TableSchema::TYPE_BINARY_UUID => ' UUID', - TableSchema::TYPE_BOOLEAN => ' BOOLEAN', - TableSchema::TYPE_FLOAT => ' FLOAT', - TableSchema::TYPE_DECIMAL => ' DECIMAL', - TableSchema::TYPE_DATE => ' DATE', - TableSchema::TYPE_TIME => ' TIME', - TableSchema::TYPE_DATETIME => ' TIMESTAMP', - TableSchema::TYPE_DATETIME_FRACTIONAL => ' TIMESTAMP', - TableSchema::TYPE_TIMESTAMP => ' TIMESTAMP', - TableSchema::TYPE_TIMESTAMP_FRACTIONAL => ' TIMESTAMP', - TableSchema::TYPE_TIMESTAMP_TIMEZONE => ' TIMESTAMPTZ', - TableSchema::TYPE_UUID => ' UUID', - TableSchema::TYPE_CHAR => ' CHAR', - TableSchema::TYPE_JSON => ' JSONB', - ]; - - if (isset($typeMap[$data['type']])) { - $out .= $typeMap[$data['type']]; - } - - if ($data['type'] === TableSchema::TYPE_INTEGER || $data['type'] === TableSchema::TYPE_BIGINTEGER) { - $type = $data['type'] === TableSchema::TYPE_INTEGER ? ' INTEGER' : ' BIGINT'; - if ($schema->getPrimaryKey() === [$name] || $data['autoIncrement'] === true) { - $type = $data['type'] === TableSchema::TYPE_INTEGER ? ' SERIAL' : ' BIGSERIAL'; - unset($data['null'], $data['default']); - } - $out .= $type; - } - - if ($data['type'] === TableSchema::TYPE_TEXT && $data['length'] !== TableSchema::LENGTH_TINY) { - $out .= ' TEXT'; - } - if ($data['type'] === TableSchema::TYPE_BINARY) { - $out .= ' BYTEA'; - } - - if ($data['type'] === TableSchema::TYPE_CHAR) { - $out .= '(' . $data['length'] . ')'; - } - - if ( - $data['type'] === TableSchema::TYPE_STRING || - ( - $data['type'] === TableSchema::TYPE_TEXT && - $data['length'] === TableSchema::LENGTH_TINY - ) - ) { - $out .= ' VARCHAR'; - if (isset($data['length']) && $data['length'] !== '') { - $out .= '(' . $data['length'] . ')'; - } - } - - $hasCollate = [TableSchema::TYPE_TEXT, TableSchema::TYPE_STRING, TableSchema::TYPE_CHAR]; - if (in_array($data['type'], $hasCollate, true) && isset($data['collate']) && $data['collate'] !== '') { - $out .= ' COLLATE "' . $data['collate'] . '"'; - } - - $hasPrecision = [ - TableSchema::TYPE_FLOAT, - TableSchema::TYPE_DATETIME, - TableSchema::TYPE_DATETIME_FRACTIONAL, - TableSchema::TYPE_TIMESTAMP, - TableSchema::TYPE_TIMESTAMP_FRACTIONAL, - TableSchema::TYPE_TIMESTAMP_TIMEZONE, - ]; - if (in_array($data['type'], $hasPrecision) && isset($data['precision'])) { - $out .= '(' . $data['precision'] . ')'; - } - - if ( - $data['type'] === TableSchema::TYPE_DECIMAL && - ( - isset($data['length']) || - isset($data['precision']) - ) - ) { - $out .= '(' . $data['length'] . ',' . (int)$data['precision'] . ')'; - } - - if (isset($data['null']) && $data['null'] === false) { - $out .= ' NOT NULL'; - } - - $datetimeTypes = [ - TableSchema::TYPE_DATETIME, - TableSchema::TYPE_DATETIME_FRACTIONAL, - TableSchema::TYPE_TIMESTAMP, - TableSchema::TYPE_TIMESTAMP_FRACTIONAL, - TableSchema::TYPE_TIMESTAMP_TIMEZONE, - ]; - if ( - isset($data['default']) && - in_array($data['type'], $datetimeTypes) && - strtolower($data['default']) === 'current_timestamp' - ) { - $out .= ' DEFAULT CURRENT_TIMESTAMP'; - } elseif (isset($data['default'])) { - $defaultValue = $data['default']; - if ($data['type'] === 'boolean') { - $defaultValue = (bool)$defaultValue; - } - $out .= ' DEFAULT ' . $this->_driver->schemaValue($defaultValue); - } elseif (isset($data['null']) && $data['null'] !== false) { - $out .= ' DEFAULT NULL'; - } - - return $out; - } - - /** - * @inheritDoc - */ - public function addConstraintSql(TableSchema $schema): array - { - $sqlPattern = 'ALTER TABLE %s ADD %s;'; - $sql = []; - - foreach ($schema->constraints() as $name) { - /** @var array $constraint */ - $constraint = $schema->getConstraint($name); - if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) { - $tableName = $this->_driver->quoteIdentifier($schema->name()); - $sql[] = sprintf($sqlPattern, $tableName, $this->constraintSql($schema, $name)); - } - } - - return $sql; - } - - /** - * @inheritDoc - */ - public function dropConstraintSql(TableSchema $schema): array - { - $sqlPattern = 'ALTER TABLE %s DROP CONSTRAINT %s;'; - $sql = []; - - foreach ($schema->constraints() as $name) { - /** @var array $constraint */ - $constraint = $schema->getConstraint($name); - if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) { - $tableName = $this->_driver->quoteIdentifier($schema->name()); - $constraintName = $this->_driver->quoteIdentifier($name); - $sql[] = sprintf($sqlPattern, $tableName, $constraintName); - } - } - - return $sql; - } - - /** - * @inheritDoc - */ - public function indexSql(TableSchema $schema, string $name): string - { - /** @var array $data */ - $data = $schema->getIndex($name); - $columns = array_map( - [$this->_driver, 'quoteIdentifier'], - $data['columns'] - ); - - return sprintf( - 'CREATE INDEX %s ON %s (%s)', - $this->_driver->quoteIdentifier($name), - $this->_driver->quoteIdentifier($schema->name()), - implode(', ', $columns) - ); - } - - /** - * @inheritDoc - */ - public function constraintSql(TableSchema $schema, string $name): string - { - /** @var array $data */ - $data = $schema->getConstraint($name); - $out = 'CONSTRAINT ' . $this->_driver->quoteIdentifier($name); - if ($data['type'] === TableSchema::CONSTRAINT_PRIMARY) { - $out = 'PRIMARY KEY'; - } - if ($data['type'] === TableSchema::CONSTRAINT_UNIQUE) { - $out .= ' UNIQUE'; - } - - return $this->_keySql($out, $data); - } - - /** - * Helper method for generating key SQL snippets. - * - * @param string $prefix The key prefix - * @param array $data Key data. - * @return string - */ - protected function _keySql(string $prefix, array $data): string - { - $columns = array_map( - [$this->_driver, 'quoteIdentifier'], - $data['columns'] - ); - if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) { - return $prefix . sprintf( - ' FOREIGN KEY (%s) REFERENCES %s (%s) ON UPDATE %s ON DELETE %s DEFERRABLE INITIALLY IMMEDIATE', - implode(', ', $columns), - $this->_driver->quoteIdentifier($data['references'][0]), - $this->_convertConstraintColumns($data['references'][1]), - $this->_foreignOnClause($data['update']), - $this->_foreignOnClause($data['delete']) - ); - } - - return $prefix . ' (' . implode(', ', $columns) . ')'; - } - - /** - * @inheritDoc - */ - public function createTableSql(TableSchema $schema, array $columns, array $constraints, array $indexes): array - { - $content = array_merge($columns, $constraints); - $content = implode(",\n", array_filter($content)); - $tableName = $this->_driver->quoteIdentifier($schema->name()); - $temporary = $schema->isTemporary() ? ' TEMPORARY ' : ' '; - $out = []; - $out[] = sprintf("CREATE%sTABLE %s (\n%s\n)", $temporary, $tableName, $content); - foreach ($indexes as $index) { - $out[] = $index; - } - foreach ($schema->columns() as $column) { - $columnData = $schema->getColumn($column); - if (isset($columnData['comment'])) { - $out[] = sprintf( - 'COMMENT ON COLUMN %s.%s IS %s', - $tableName, - $this->_driver->quoteIdentifier($column), - $this->_driver->schemaValue($columnData['comment']) - ); - } - } - - return $out; - } - - /** - * @inheritDoc - */ - public function truncateTableSql(TableSchema $schema): array - { - $name = $this->_driver->quoteIdentifier($schema->name()); - - return [ - sprintf('TRUNCATE %s RESTART IDENTITY CASCADE', $name), - ]; - } - - /** - * Generate the SQL to drop a table. - * - * @param \Cake\Database\Schema\TableSchema $schema Table instance - * @return array SQL statements to drop a table. - */ - public function dropTableSql(TableSchema $schema): array - { - $sql = sprintf( - 'DROP TABLE %s CASCADE', - $this->_driver->quoteIdentifier($schema->name()) - ); - - return [$sql]; - } -} +// @deprecated 4.1.0 Load new class location and alias for old location +class_exists('Cake\Database\Schema\PostgresSchemaDialect'); diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/PostgresSchemaDialect.php b/app/vendor/cakephp/cakephp/src/Database/Schema/PostgresSchemaDialect.php new file mode 100644 index 000000000..4b7806ee2 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/PostgresSchemaDialect.php @@ -0,0 +1,663 @@ + $col, 'length' => null]; + } + if (in_array($col, ['timestamptz', 'timestamp with time zone'], true)) { + return ['type' => TableSchema::TYPE_TIMESTAMP_TIMEZONE, 'length' => null]; + } + if (strpos($col, 'timestamp') !== false) { + return ['type' => TableSchema::TYPE_TIMESTAMP_FRACTIONAL, 'length' => null]; + } + if (strpos($col, 'time') !== false) { + return ['type' => TableSchema::TYPE_TIME, 'length' => null]; + } + if ($col === 'serial' || $col === 'integer') { + return ['type' => TableSchema::TYPE_INTEGER, 'length' => 10]; + } + if ($col === 'bigserial' || $col === 'bigint') { + return ['type' => TableSchema::TYPE_BIGINTEGER, 'length' => 20]; + } + if ($col === 'smallint') { + return ['type' => TableSchema::TYPE_SMALLINTEGER, 'length' => 5]; + } + if ($col === 'inet') { + return ['type' => TableSchema::TYPE_STRING, 'length' => 39]; + } + if ($col === 'uuid') { + return ['type' => TableSchema::TYPE_UUID, 'length' => null]; + } + if ($col === 'char') { + return ['type' => TableSchema::TYPE_CHAR, 'length' => $length]; + } + if (strpos($col, 'character') !== false) { + return ['type' => TableSchema::TYPE_STRING, 'length' => $length]; + } + // money is 'string' as it includes arbitrary text content + // before the number value. + if (strpos($col, 'money') !== false || $col === 'string') { + return ['type' => TableSchema::TYPE_STRING, 'length' => $length]; + } + if (strpos($col, 'text') !== false) { + return ['type' => TableSchema::TYPE_TEXT, 'length' => null]; + } + if ($col === 'bytea') { + return ['type' => TableSchema::TYPE_BINARY, 'length' => null]; + } + if ($col === 'real' || strpos($col, 'double') !== false) { + return ['type' => TableSchema::TYPE_FLOAT, 'length' => null]; + } + if ( + strpos($col, 'numeric') !== false || + strpos($col, 'decimal') !== false + ) { + return ['type' => TableSchema::TYPE_DECIMAL, 'length' => null]; + } + + if (strpos($col, 'json') !== false) { + return ['type' => TableSchema::TYPE_JSON, 'length' => null]; + } + + $length = is_numeric($length) ? $length : null; + + return ['type' => TableSchema::TYPE_STRING, 'length' => $length]; + } + + /** + * @inheritDoc + */ + public function convertColumnDescription(TableSchema $schema, array $row): void + { + $field = $this->_convertColumn($row['type']); + + if ($field['type'] === TableSchema::TYPE_BOOLEAN) { + if ($row['default'] === 'true') { + $row['default'] = 1; + } + if ($row['default'] === 'false') { + $row['default'] = 0; + } + } + if (!empty($row['has_serial'])) { + $field['autoIncrement'] = true; + } + + $field += [ + 'default' => $this->_defaultValue($row['default']), + 'null' => $row['null'] === 'YES', + 'collate' => $row['collation_name'], + 'comment' => $row['comment'], + ]; + $field['length'] = $row['char_length'] ?: $field['length']; + + if ($field['type'] === 'numeric' || $field['type'] === 'decimal') { + $field['length'] = $row['column_precision']; + $field['precision'] = $row['column_scale'] ?: null; + } + + if ($field['type'] === TableSchema::TYPE_TIMESTAMP_FRACTIONAL) { + $field['precision'] = $row['datetime_precision']; + if ($field['precision'] === 0) { + $field['type'] = TableSchema::TYPE_TIMESTAMP; + } + } + + if ($field['type'] === TableSchema::TYPE_TIMESTAMP_TIMEZONE) { + $field['precision'] = $row['datetime_precision']; + } + + $schema->addColumn($row['name'], $field); + } + + /** + * Manipulate the default value. + * + * Postgres includes sequence data and casting information in default values. + * We need to remove those. + * + * @param string|int|null $default The default value. + * @return string|int|null + */ + protected function _defaultValue($default) + { + if (is_numeric($default) || $default === null) { + return $default; + } + // Sequences + if (strpos($default, 'nextval') === 0) { + return null; + } + + if (strpos($default, 'NULL::') === 0) { + return null; + } + + // Remove quotes and postgres casts + return preg_replace( + "/^'(.*)'(?:::.*)$/", + '$1', + $default + ); + } + + /** + * @inheritDoc + */ + public function describeIndexSql(string $tableName, array $config): array + { + $sql = 'SELECT + c2.relname, + a.attname, + i.indisprimary, + i.indisunique + FROM pg_catalog.pg_namespace n + INNER JOIN pg_catalog.pg_class c ON (n.oid = c.relnamespace) + INNER JOIN pg_catalog.pg_index i ON (c.oid = i.indrelid) + INNER JOIN pg_catalog.pg_class c2 ON (c2.oid = i.indexrelid) + INNER JOIN pg_catalog.pg_attribute a ON (a.attrelid = c.oid AND i.indrelid::regclass = a.attrelid::regclass) + WHERE n.nspname = ? + AND a.attnum = ANY(i.indkey) + AND c.relname = ? + ORDER BY i.indisprimary DESC, i.indisunique DESC, c.relname, a.attnum'; + + $schema = 'public'; + if (!empty($config['schema'])) { + $schema = $config['schema']; + } + + return [$sql, [$schema, $tableName]]; + } + + /** + * @inheritDoc + */ + public function convertIndexDescription(TableSchema $schema, array $row): void + { + $type = TableSchema::INDEX_INDEX; + $name = $row['relname']; + if ($row['indisprimary']) { + $name = $type = TableSchema::CONSTRAINT_PRIMARY; + } + if ($row['indisunique'] && $type === TableSchema::INDEX_INDEX) { + $type = TableSchema::CONSTRAINT_UNIQUE; + } + if ($type === TableSchema::CONSTRAINT_PRIMARY || $type === TableSchema::CONSTRAINT_UNIQUE) { + $this->_convertConstraint($schema, $name, $type, $row); + + return; + } + $index = $schema->getIndex($name); + if (!$index) { + $index = [ + 'type' => $type, + 'columns' => [], + ]; + } + $index['columns'][] = $row['attname']; + $schema->addIndex($name, $index); + } + + /** + * Add/update a constraint into the schema object. + * + * @param \Cake\Database\Schema\TableSchema $schema The table to update. + * @param string $name The index name. + * @param string $type The index type. + * @param array $row The metadata record to update with. + * @return void + */ + protected function _convertConstraint(TableSchema $schema, string $name, string $type, array $row): void + { + $constraint = $schema->getConstraint($name); + if (!$constraint) { + $constraint = [ + 'type' => $type, + 'columns' => [], + ]; + } + $constraint['columns'][] = $row['attname']; + $schema->addConstraint($name, $constraint); + } + + /** + * @inheritDoc + */ + public function describeForeignKeySql(string $tableName, array $config): array + { + // phpcs:disable Generic.Files.LineLength + $sql = 'SELECT + c.conname AS name, + c.contype AS type, + a.attname AS column_name, + c.confmatchtype AS match_type, + c.confupdtype AS on_update, + c.confdeltype AS on_delete, + c.confrelid::regclass AS references_table, + ab.attname AS references_field + FROM pg_catalog.pg_namespace n + INNER JOIN pg_catalog.pg_class cl ON (n.oid = cl.relnamespace) + INNER JOIN pg_catalog.pg_constraint c ON (n.oid = c.connamespace) + INNER JOIN pg_catalog.pg_attribute a ON (a.attrelid = cl.oid AND c.conrelid = a.attrelid AND a.attnum = ANY(c.conkey)) + INNER JOIN pg_catalog.pg_attribute ab ON (a.attrelid = cl.oid AND c.confrelid = ab.attrelid AND ab.attnum = ANY(c.confkey)) + WHERE n.nspname = ? + AND cl.relname = ? + ORDER BY name, a.attnum, ab.attnum DESC'; + // phpcs:enable Generic.Files.LineLength + + $schema = empty($config['schema']) ? 'public' : $config['schema']; + + return [$sql, [$schema, $tableName]]; + } + + /** + * @inheritDoc + */ + public function convertForeignKeyDescription(TableSchema $schema, array $row): void + { + $data = [ + 'type' => TableSchema::CONSTRAINT_FOREIGN, + 'columns' => $row['column_name'], + 'references' => [$row['references_table'], $row['references_field']], + 'update' => $this->_convertOnClause($row['on_update']), + 'delete' => $this->_convertOnClause($row['on_delete']), + ]; + $schema->addConstraint($row['name'], $data); + } + + /** + * @inheritDoc + */ + protected function _convertOnClause(string $clause): string + { + if ($clause === 'r') { + return TableSchema::ACTION_RESTRICT; + } + if ($clause === 'a') { + return TableSchema::ACTION_NO_ACTION; + } + if ($clause === 'c') { + return TableSchema::ACTION_CASCADE; + } + + return TableSchema::ACTION_SET_NULL; + } + + /** + * @inheritDoc + */ + public function columnSql(TableSchema $schema, string $name): string + { + /** @var array $data */ + $data = $schema->getColumn($name); + $out = $this->_driver->quoteIdentifier($name); + $typeMap = [ + TableSchema::TYPE_TINYINTEGER => ' SMALLINT', + TableSchema::TYPE_SMALLINTEGER => ' SMALLINT', + TableSchema::TYPE_BINARY_UUID => ' UUID', + TableSchema::TYPE_BOOLEAN => ' BOOLEAN', + TableSchema::TYPE_FLOAT => ' FLOAT', + TableSchema::TYPE_DECIMAL => ' DECIMAL', + TableSchema::TYPE_DATE => ' DATE', + TableSchema::TYPE_TIME => ' TIME', + TableSchema::TYPE_DATETIME => ' TIMESTAMP', + TableSchema::TYPE_DATETIME_FRACTIONAL => ' TIMESTAMP', + TableSchema::TYPE_TIMESTAMP => ' TIMESTAMP', + TableSchema::TYPE_TIMESTAMP_FRACTIONAL => ' TIMESTAMP', + TableSchema::TYPE_TIMESTAMP_TIMEZONE => ' TIMESTAMPTZ', + TableSchema::TYPE_UUID => ' UUID', + TableSchema::TYPE_CHAR => ' CHAR', + TableSchema::TYPE_JSON => ' JSONB', + ]; + + if (isset($typeMap[$data['type']])) { + $out .= $typeMap[$data['type']]; + } + + if ($data['type'] === TableSchema::TYPE_INTEGER || $data['type'] === TableSchema::TYPE_BIGINTEGER) { + $type = $data['type'] === TableSchema::TYPE_INTEGER ? ' INTEGER' : ' BIGINT'; + if ($schema->getPrimaryKey() === [$name] || $data['autoIncrement'] === true) { + $type = $data['type'] === TableSchema::TYPE_INTEGER ? ' SERIAL' : ' BIGSERIAL'; + unset($data['null'], $data['default']); + } + $out .= $type; + } + + if ($data['type'] === TableSchema::TYPE_TEXT && $data['length'] !== TableSchema::LENGTH_TINY) { + $out .= ' TEXT'; + } + if ($data['type'] === TableSchema::TYPE_BINARY) { + $out .= ' BYTEA'; + } + + if ($data['type'] === TableSchema::TYPE_CHAR) { + $out .= '(' . $data['length'] . ')'; + } + + if ( + $data['type'] === TableSchema::TYPE_STRING || + ( + $data['type'] === TableSchema::TYPE_TEXT && + $data['length'] === TableSchema::LENGTH_TINY + ) + ) { + $out .= ' VARCHAR'; + if (isset($data['length']) && $data['length'] !== '') { + $out .= '(' . $data['length'] . ')'; + } + } + + $hasCollate = [TableSchema::TYPE_TEXT, TableSchema::TYPE_STRING, TableSchema::TYPE_CHAR]; + if (in_array($data['type'], $hasCollate, true) && isset($data['collate']) && $data['collate'] !== '') { + $out .= ' COLLATE "' . $data['collate'] . '"'; + } + + $hasPrecision = [ + TableSchema::TYPE_FLOAT, + TableSchema::TYPE_DATETIME, + TableSchema::TYPE_DATETIME_FRACTIONAL, + TableSchema::TYPE_TIMESTAMP, + TableSchema::TYPE_TIMESTAMP_FRACTIONAL, + TableSchema::TYPE_TIMESTAMP_TIMEZONE, + ]; + if (in_array($data['type'], $hasPrecision) && isset($data['precision'])) { + $out .= '(' . $data['precision'] . ')'; + } + + if ( + $data['type'] === TableSchema::TYPE_DECIMAL && + ( + isset($data['length']) || + isset($data['precision']) + ) + ) { + $out .= '(' . $data['length'] . ',' . (int)$data['precision'] . ')'; + } + + if (isset($data['null']) && $data['null'] === false) { + $out .= ' NOT NULL'; + } + + $datetimeTypes = [ + TableSchema::TYPE_DATETIME, + TableSchema::TYPE_DATETIME_FRACTIONAL, + TableSchema::TYPE_TIMESTAMP, + TableSchema::TYPE_TIMESTAMP_FRACTIONAL, + TableSchema::TYPE_TIMESTAMP_TIMEZONE, + ]; + if ( + isset($data['default']) && + in_array($data['type'], $datetimeTypes) && + strtolower($data['default']) === 'current_timestamp' + ) { + $out .= ' DEFAULT CURRENT_TIMESTAMP'; + } elseif (isset($data['default'])) { + $defaultValue = $data['default']; + if ($data['type'] === 'boolean') { + $defaultValue = (bool)$defaultValue; + } + $out .= ' DEFAULT ' . $this->_driver->schemaValue($defaultValue); + } elseif (isset($data['null']) && $data['null'] !== false) { + $out .= ' DEFAULT NULL'; + } + + return $out; + } + + /** + * @inheritDoc + */ + public function addConstraintSql(TableSchema $schema): array + { + $sqlPattern = 'ALTER TABLE %s ADD %s;'; + $sql = []; + + foreach ($schema->constraints() as $name) { + /** @var array $constraint */ + $constraint = $schema->getConstraint($name); + if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) { + $tableName = $this->_driver->quoteIdentifier($schema->name()); + $sql[] = sprintf($sqlPattern, $tableName, $this->constraintSql($schema, $name)); + } + } + + return $sql; + } + + /** + * @inheritDoc + */ + public function dropConstraintSql(TableSchema $schema): array + { + $sqlPattern = 'ALTER TABLE %s DROP CONSTRAINT %s;'; + $sql = []; + + foreach ($schema->constraints() as $name) { + /** @var array $constraint */ + $constraint = $schema->getConstraint($name); + if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) { + $tableName = $this->_driver->quoteIdentifier($schema->name()); + $constraintName = $this->_driver->quoteIdentifier($name); + $sql[] = sprintf($sqlPattern, $tableName, $constraintName); + } + } + + return $sql; + } + + /** + * @inheritDoc + */ + public function indexSql(TableSchema $schema, string $name): string + { + /** @var array $data */ + $data = $schema->getIndex($name); + $columns = array_map( + [$this->_driver, 'quoteIdentifier'], + $data['columns'] + ); + + return sprintf( + 'CREATE INDEX %s ON %s (%s)', + $this->_driver->quoteIdentifier($name), + $this->_driver->quoteIdentifier($schema->name()), + implode(', ', $columns) + ); + } + + /** + * @inheritDoc + */ + public function constraintSql(TableSchema $schema, string $name): string + { + /** @var array $data */ + $data = $schema->getConstraint($name); + $out = 'CONSTRAINT ' . $this->_driver->quoteIdentifier($name); + if ($data['type'] === TableSchema::CONSTRAINT_PRIMARY) { + $out = 'PRIMARY KEY'; + } + if ($data['type'] === TableSchema::CONSTRAINT_UNIQUE) { + $out .= ' UNIQUE'; + } + + return $this->_keySql($out, $data); + } + + /** + * Helper method for generating key SQL snippets. + * + * @param string $prefix The key prefix + * @param array $data Key data. + * @return string + */ + protected function _keySql(string $prefix, array $data): string + { + $columns = array_map( + [$this->_driver, 'quoteIdentifier'], + $data['columns'] + ); + if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) { + return $prefix . sprintf( + ' FOREIGN KEY (%s) REFERENCES %s (%s) ON UPDATE %s ON DELETE %s DEFERRABLE INITIALLY IMMEDIATE', + implode(', ', $columns), + $this->_driver->quoteIdentifier($data['references'][0]), + $this->_convertConstraintColumns($data['references'][1]), + $this->_foreignOnClause($data['update']), + $this->_foreignOnClause($data['delete']) + ); + } + + return $prefix . ' (' . implode(', ', $columns) . ')'; + } + + /** + * @inheritDoc + */ + public function createTableSql(TableSchema $schema, array $columns, array $constraints, array $indexes): array + { + $content = array_merge($columns, $constraints); + $content = implode(",\n", array_filter($content)); + $tableName = $this->_driver->quoteIdentifier($schema->name()); + $temporary = $schema->isTemporary() ? ' TEMPORARY ' : ' '; + $out = []; + $out[] = sprintf("CREATE%sTABLE %s (\n%s\n)", $temporary, $tableName, $content); + foreach ($indexes as $index) { + $out[] = $index; + } + foreach ($schema->columns() as $column) { + $columnData = $schema->getColumn($column); + if (isset($columnData['comment'])) { + $out[] = sprintf( + 'COMMENT ON COLUMN %s.%s IS %s', + $tableName, + $this->_driver->quoteIdentifier($column), + $this->_driver->schemaValue($columnData['comment']) + ); + } + } + + return $out; + } + + /** + * @inheritDoc + */ + public function truncateTableSql(TableSchema $schema): array + { + $name = $this->_driver->quoteIdentifier($schema->name()); + + return [ + sprintf('TRUNCATE %s RESTART IDENTITY CASCADE', $name), + ]; + } + + /** + * Generate the SQL to drop a table. + * + * @param \Cake\Database\Schema\TableSchema $schema Table instance + * @return array SQL statements to drop a table. + */ + public function dropTableSql(TableSchema $schema): array + { + $sql = sprintf( + 'DROP TABLE %s CASCADE', + $this->_driver->quoteIdentifier($schema->name()) + ); + + return [$sql]; + } +} + +// phpcs:disable +// Add backwards compatible alias. +class_alias('Cake\Database\Schema\PostgresSchemaDialect', 'Cake\Database\Schema\PostgresSchema'); +// phpcs:enable diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/SchemaDialect.php b/app/vendor/cakephp/cakephp/src/Database/Schema/SchemaDialect.php new file mode 100644 index 000000000..948134703 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/SchemaDialect.php @@ -0,0 +1,290 @@ +connect(); + $this->_driver = $driver; + } + + /** + * Generate an ON clause for a foreign key. + * + * @param string $on The on clause + * @return string + */ + protected function _foreignOnClause(string $on): string + { + if ($on === TableSchema::ACTION_SET_NULL) { + return 'SET NULL'; + } + if ($on === TableSchema::ACTION_SET_DEFAULT) { + return 'SET DEFAULT'; + } + if ($on === TableSchema::ACTION_CASCADE) { + return 'CASCADE'; + } + if ($on === TableSchema::ACTION_RESTRICT) { + return 'RESTRICT'; + } + if ($on === TableSchema::ACTION_NO_ACTION) { + return 'NO ACTION'; + } + + throw new InvalidArgumentException('Invalid value for "on": ' . $on); + } + + /** + * Convert string on clauses to the abstract ones. + * + * @param string $clause The on clause to convert. + * @return string + */ + protected function _convertOnClause(string $clause): string + { + if ($clause === 'CASCADE' || $clause === 'RESTRICT') { + return strtolower($clause); + } + if ($clause === 'NO ACTION') { + return TableSchema::ACTION_NO_ACTION; + } + + return TableSchema::ACTION_SET_NULL; + } + + /** + * Convert foreign key constraints references to a valid + * stringified list + * + * @param string|array $references The referenced columns of a foreign key constraint statement + * @return string + */ + protected function _convertConstraintColumns($references): string + { + if (is_string($references)) { + return $this->_driver->quoteIdentifier($references); + } + + return implode(', ', array_map( + [$this->_driver, 'quoteIdentifier'], + $references + )); + } + + /** + * Generate the SQL to drop a table. + * + * @param \Cake\Database\Schema\TableSchema $schema Schema instance + * @return array SQL statements to drop a table. + */ + public function dropTableSql(TableSchema $schema): array + { + $sql = sprintf( + 'DROP TABLE %s', + $this->_driver->quoteIdentifier($schema->name()) + ); + + return [$sql]; + } + + /** + * Generate the SQL to list the tables. + * + * @param array $config The connection configuration to use for + * getting tables from. + * @return array An array of (sql, params) to execute. + */ + abstract public function listTablesSql(array $config): array; + + /** + * Generate the SQL to describe a table. + * + * @param string $tableName The table name to get information on. + * @param array $config The connection configuration. + * @return array An array of (sql, params) to execute. + */ + abstract public function describeColumnSql(string $tableName, array $config): array; + + /** + * Generate the SQL to describe the indexes in a table. + * + * @param string $tableName The table name to get information on. + * @param array $config The connection configuration. + * @return array An array of (sql, params) to execute. + */ + abstract public function describeIndexSql(string $tableName, array $config): array; + + /** + * Generate the SQL to describe the foreign keys in a table. + * + * @param string $tableName The table name to get information on. + * @param array $config The connection configuration. + * @return array An array of (sql, params) to execute. + */ + abstract public function describeForeignKeySql(string $tableName, array $config): array; + + /** + * Generate the SQL to describe table options + * + * @param string $tableName Table name. + * @param array $config The connection configuration. + * @return array SQL statements to get options for a table. + */ + public function describeOptionsSql(string $tableName, array $config): array + { + return ['', '']; + } + + /** + * Convert field description results into abstract schema fields. + * + * @param \Cake\Database\Schema\TableSchema $schema The table object to append fields to. + * @param array $row The row data from `describeColumnSql`. + * @return void + */ + abstract public function convertColumnDescription(TableSchema $schema, array $row): void; + + /** + * Convert an index description results into abstract schema indexes or constraints. + * + * @param \Cake\Database\Schema\TableSchema $schema The table object to append + * an index or constraint to. + * @param array $row The row data from `describeIndexSql`. + * @return void + */ + abstract public function convertIndexDescription(TableSchema $schema, array $row): void; + + /** + * Convert a foreign key description into constraints on the Table object. + * + * @param \Cake\Database\Schema\TableSchema $schema The table object to append + * a constraint to. + * @param array $row The row data from `describeForeignKeySql`. + * @return void + */ + abstract public function convertForeignKeyDescription(TableSchema $schema, array $row): void; + + /** + * Convert options data into table options. + * + * @param \Cake\Database\Schema\TableSchema $schema Table instance. + * @param array $row The row of data. + * @return void + */ + public function convertOptionsDescription(TableSchema $schema, array $row): void + { + } + + /** + * Generate the SQL to create a table. + * + * @param \Cake\Database\Schema\TableSchema $schema Table instance. + * @param string[] $columns The columns to go inside the table. + * @param string[] $constraints The constraints for the table. + * @param string[] $indexes The indexes for the table. + * @return string[] SQL statements to create a table. + */ + abstract public function createTableSql( + TableSchema $schema, + array $columns, + array $constraints, + array $indexes + ): array; + + /** + * Generate the SQL fragment for a single column in a table. + * + * @param \Cake\Database\Schema\TableSchema $schema The table instance the column is in. + * @param string $name The name of the column. + * @return string SQL fragment. + */ + abstract public function columnSql(TableSchema $schema, string $name): string; + + /** + * Generate the SQL queries needed to add foreign key constraints to the table + * + * @param \Cake\Database\Schema\TableSchema $schema The table instance the foreign key constraints are. + * @return array SQL fragment. + */ + abstract public function addConstraintSql(TableSchema $schema): array; + + /** + * Generate the SQL queries needed to drop foreign key constraints from the table + * + * @param \Cake\Database\Schema\TableSchema $schema The table instance the foreign key constraints are. + * @return array SQL fragment. + */ + abstract public function dropConstraintSql(TableSchema $schema): array; + + /** + * Generate the SQL fragments for defining table constraints. + * + * @param \Cake\Database\Schema\TableSchema $schema The table instance the column is in. + * @param string $name The name of the column. + * @return string SQL fragment. + */ + abstract public function constraintSql(TableSchema $schema, string $name): string; + + /** + * Generate the SQL fragment for a single index in a table. + * + * @param \Cake\Database\Schema\TableSchema $schema The table object the column is in. + * @param string $name The name of the column. + * @return string SQL fragment. + */ + abstract public function indexSql(TableSchema $schema, string $name): string; + + /** + * Generate the SQL to truncate a table. + * + * @param \Cake\Database\Schema\TableSchema $schema Table instance. + * @return array SQL statements to truncate a table. + */ + abstract public function truncateTableSql(TableSchema $schema): array; +} + +// phpcs:disable +// Add backwards compatible alias. +class_alias('Cake\Database\Schema\SchemaDialect', 'Cake\Database\Schema\BaseSchema'); +// phpcs:enable diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/SqliteSchema.php b/app/vendor/cakephp/cakephp/src/Database/Schema/SqliteSchema.php index e7408b827..6c373fc36 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/SqliteSchema.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/SqliteSchema.php @@ -1,601 +1,5 @@ TableSchema::TYPE_BIGINTEGER, 'length' => $length, 'unsigned' => $unsigned]; - } - if ($col === 'smallint') { - return ['type' => TableSchema::TYPE_SMALLINTEGER, 'length' => $length, 'unsigned' => $unsigned]; - } - if ($col === 'tinyint') { - return ['type' => TableSchema::TYPE_TINYINTEGER, 'length' => $length, 'unsigned' => $unsigned]; - } - if (strpos($col, 'int') !== false) { - return ['type' => TableSchema::TYPE_INTEGER, 'length' => $length, 'unsigned' => $unsigned]; - } - if (strpos($col, 'decimal') !== false) { - return [ - 'type' => TableSchema::TYPE_DECIMAL, - 'length' => $length, - 'precision' => $precision, - 'unsigned' => $unsigned, - ]; - } - if (in_array($col, ['float', 'real', 'double'])) { - return [ - 'type' => TableSchema::TYPE_FLOAT, - 'length' => $length, - 'precision' => $precision, - 'unsigned' => $unsigned, - ]; - } - - if (strpos($col, 'boolean') !== false) { - return ['type' => TableSchema::TYPE_BOOLEAN, 'length' => null]; - } - - if ($col === 'char' && $length === 36) { - return ['type' => TableSchema::TYPE_UUID, 'length' => null]; - } - if ($col === 'char') { - return ['type' => TableSchema::TYPE_CHAR, 'length' => $length]; - } - if (strpos($col, 'char') !== false) { - return ['type' => TableSchema::TYPE_STRING, 'length' => $length]; - } - - if ($col === 'binary' && $length === 16) { - return ['type' => TableSchema::TYPE_BINARY_UUID, 'length' => null]; - } - if (in_array($col, ['blob', 'clob', 'binary', 'varbinary'])) { - return ['type' => TableSchema::TYPE_BINARY, 'length' => $length]; - } - - $datetimeTypes = [ - 'date', - 'time', - 'timestamp', - 'timestampfractional', - 'timestamptimezone', - 'datetime', - 'datetimefractional', - ]; - if (in_array($col, $datetimeTypes)) { - return ['type' => $col, 'length' => null]; - } - - return ['type' => TableSchema::TYPE_TEXT, 'length' => null]; - } - - /** - * @inheritDoc - */ - public function listTablesSql(array $config): array - { - return [ - 'SELECT name FROM sqlite_master WHERE type="table" ' . - 'AND name != "sqlite_sequence" ORDER BY name', - [], - ]; - } - - /** - * @inheritDoc - */ - public function describeColumnSql(string $tableName, array $config): array - { - $sql = sprintf( - 'PRAGMA table_info(%s)', - $this->_driver->quoteIdentifier($tableName) - ); - - return [$sql, []]; - } - - /** - * @inheritDoc - */ - public function convertColumnDescription(TableSchema $schema, array $row): void - { - $field = $this->_convertColumn($row['type']); - $field += [ - 'null' => !$row['notnull'], - 'default' => $this->_defaultValue($row['dflt_value']), - ]; - $primary = $schema->getConstraint('primary'); - - if ($row['pk'] && empty($primary)) { - $field['null'] = false; - $field['autoIncrement'] = true; - } - - // SQLite does not support autoincrement on composite keys. - if ($row['pk'] && !empty($primary)) { - $existingColumn = $primary['columns'][0]; - /** @psalm-suppress PossiblyNullOperand */ - $schema->addColumn($existingColumn, ['autoIncrement' => null] + $schema->getColumn($existingColumn)); - } - - $schema->addColumn($row['name'], $field); - if ($row['pk']) { - $constraint = (array)$schema->getConstraint('primary') + [ - 'type' => TableSchema::CONSTRAINT_PRIMARY, - 'columns' => [], - ]; - $constraint['columns'] = array_merge($constraint['columns'], [$row['name']]); - $schema->addConstraint('primary', $constraint); - } - } - - /** - * Manipulate the default value. - * - * Sqlite includes quotes and bared NULLs in default values. - * We need to remove those. - * - * @param string|int|null $default The default value. - * @return string|int|null - */ - protected function _defaultValue($default) - { - if ($default === 'NULL' || $default === null) { - return null; - } - - // Remove quotes - if (is_string($default) && preg_match("/^'(.*)'$/", $default, $matches)) { - return str_replace("''", "'", $matches[1]); - } - - return $default; - } - - /** - * @inheritDoc - */ - public function describeIndexSql(string $tableName, array $config): array - { - $sql = sprintf( - 'PRAGMA index_list(%s)', - $this->_driver->quoteIdentifier($tableName) - ); - - return [$sql, []]; - } - - /** - * {@inheritDoc} - * - * Since SQLite does not have a way to get metadata about all indexes at once, - * additional queries are done here. Sqlite constraint names are not - * stable, and the names for constraints will not match those used to create - * the table. This is a limitation in Sqlite's metadata features. - * - * @param \Cake\Database\Schema\TableSchema $schema The table object to append - * an index or constraint to. - * @param array $row The row data from `describeIndexSql`. - * @return void - */ - public function convertIndexDescription(TableSchema $schema, array $row): void - { - $sql = sprintf( - 'PRAGMA index_info(%s)', - $this->_driver->quoteIdentifier($row['name']) - ); - $statement = $this->_driver->prepare($sql); - $statement->execute(); - $columns = []; - /** @psalm-suppress PossiblyFalseIterator */ - foreach ($statement->fetchAll('assoc') as $column) { - $columns[] = $column['name']; - } - $statement->closeCursor(); - if ($row['unique']) { - $schema->addConstraint($row['name'], [ - 'type' => TableSchema::CONSTRAINT_UNIQUE, - 'columns' => $columns, - ]); - } else { - $schema->addIndex($row['name'], [ - 'type' => TableSchema::INDEX_INDEX, - 'columns' => $columns, - ]); - } - } - - /** - * @inheritDoc - */ - public function describeForeignKeySql(string $tableName, array $config): array - { - $sql = sprintf('PRAGMA foreign_key_list(%s)', $this->_driver->quoteIdentifier($tableName)); - - return [$sql, []]; - } - - /** - * @inheritDoc - */ - public function convertForeignKeyDescription(TableSchema $schema, array $row): void - { - $name = $row['from'] . '_fk'; - - $update = $row['on_update'] ?? ''; - $delete = $row['on_delete'] ?? ''; - $data = [ - 'type' => TableSchema::CONSTRAINT_FOREIGN, - 'columns' => [$row['from']], - 'references' => [$row['table'], $row['to']], - 'update' => $this->_convertOnClause($update), - 'delete' => $this->_convertOnClause($delete), - ]; - - if (isset($this->_constraintsIdMap[$schema->name()][$row['id']])) { - $name = $this->_constraintsIdMap[$schema->name()][$row['id']]; - } else { - $this->_constraintsIdMap[$schema->name()][$row['id']] = $name; - } - - $schema->addConstraint($name, $data); - } - - /** - * {@inheritDoc} - * - * @param \Cake\Database\Schema\TableSchema $schema The table instance the column is in. - * @param string $name The name of the column. - * @return string SQL fragment. - * @throws \Cake\Database\Exception when the column type is unknown - */ - public function columnSql(TableSchema $schema, string $name): string - { - /** @var array $data */ - $data = $schema->getColumn($name); - $typeMap = [ - TableSchema::TYPE_BINARY_UUID => ' BINARY(16)', - TableSchema::TYPE_UUID => ' CHAR(36)', - TableSchema::TYPE_CHAR => ' CHAR', - TableSchema::TYPE_TINYINTEGER => ' TINYINT', - TableSchema::TYPE_SMALLINTEGER => ' SMALLINT', - TableSchema::TYPE_INTEGER => ' INTEGER', - TableSchema::TYPE_BIGINTEGER => ' BIGINT', - TableSchema::TYPE_BOOLEAN => ' BOOLEAN', - TableSchema::TYPE_FLOAT => ' FLOAT', - TableSchema::TYPE_DECIMAL => ' DECIMAL', - TableSchema::TYPE_DATE => ' DATE', - TableSchema::TYPE_TIME => ' TIME', - TableSchema::TYPE_DATETIME => ' DATETIME', - TableSchema::TYPE_DATETIME_FRACTIONAL => ' DATETIMEFRACTIONAL', - TableSchema::TYPE_TIMESTAMP => ' TIMESTAMP', - TableSchema::TYPE_TIMESTAMP_FRACTIONAL => ' TIMESTAMPFRACTIONAL', - TableSchema::TYPE_TIMESTAMP_TIMEZONE => ' TIMESTAMPTIMEZONE', - TableSchema::TYPE_JSON => ' TEXT', - ]; - - $out = $this->_driver->quoteIdentifier($name); - $hasUnsigned = [ - TableSchema::TYPE_TINYINTEGER, - TableSchema::TYPE_SMALLINTEGER, - TableSchema::TYPE_INTEGER, - TableSchema::TYPE_BIGINTEGER, - TableSchema::TYPE_FLOAT, - TableSchema::TYPE_DECIMAL, - ]; - - if ( - in_array($data['type'], $hasUnsigned, true) && - isset($data['unsigned']) && - $data['unsigned'] === true - ) { - if ($data['type'] !== TableSchema::TYPE_INTEGER || (array)$schema->getPrimaryKey() !== [$name]) { - $out .= ' UNSIGNED'; - } - } - - if (isset($typeMap[$data['type']])) { - $out .= $typeMap[$data['type']]; - } - - if ($data['type'] === TableSchema::TYPE_TEXT && $data['length'] !== TableSchema::LENGTH_TINY) { - $out .= ' TEXT'; - } - - if ($data['type'] === TableSchema::TYPE_CHAR) { - $out .= '(' . $data['length'] . ')'; - } - - if ( - $data['type'] === TableSchema::TYPE_STRING || - ( - $data['type'] === TableSchema::TYPE_TEXT && - $data['length'] === TableSchema::LENGTH_TINY - ) - ) { - $out .= ' VARCHAR'; - - if (isset($data['length'])) { - $out .= '(' . $data['length'] . ')'; - } - } - - if ($data['type'] === TableSchema::TYPE_BINARY) { - if (isset($data['length'])) { - $out .= ' BLOB(' . $data['length'] . ')'; - } else { - $out .= ' BLOB'; - } - } - - $integerTypes = [ - TableSchema::TYPE_TINYINTEGER, - TableSchema::TYPE_SMALLINTEGER, - TableSchema::TYPE_INTEGER, - ]; - if ( - in_array($data['type'], $integerTypes, true) && - isset($data['length']) && - (array)$schema->getPrimaryKey() !== [$name] - ) { - $out .= '(' . (int)$data['length'] . ')'; - } - - $hasPrecision = [TableSchema::TYPE_FLOAT, TableSchema::TYPE_DECIMAL]; - if ( - in_array($data['type'], $hasPrecision, true) && - ( - isset($data['length']) || - isset($data['precision']) - ) - ) { - $out .= '(' . (int)$data['length'] . ',' . (int)$data['precision'] . ')'; - } - - if (isset($data['null']) && $data['null'] === false) { - $out .= ' NOT NULL'; - } - - if ($data['type'] === TableSchema::TYPE_INTEGER && (array)$schema->getPrimaryKey() === [$name]) { - $out .= ' PRIMARY KEY AUTOINCREMENT'; - } - - $timestampTypes = [ - TableSchema::TYPE_DATETIME, - TableSchema::TYPE_DATETIME_FRACTIONAL, - TableSchema::TYPE_TIMESTAMP, - TableSchema::TYPE_TIMESTAMP_FRACTIONAL, - TableSchema::TYPE_TIMESTAMP_TIMEZONE, - ]; - if (isset($data['null']) && $data['null'] === true && in_array($data['type'], $timestampTypes, true)) { - $out .= ' DEFAULT NULL'; - } - if (isset($data['default'])) { - $out .= ' DEFAULT ' . $this->_driver->schemaValue($data['default']); - } - - return $out; - } - - /** - * {@inheritDoc} - * - * Note integer primary keys will return ''. This is intentional as Sqlite requires - * that integer primary keys be defined in the column definition. - * - * @param \Cake\Database\Schema\TableSchema $schema The table instance the column is in. - * @param string $name The name of the column. - * @return string SQL fragment. - */ - public function constraintSql(TableSchema $schema, string $name): string - { - /** @var array $data */ - $data = $schema->getConstraint($name); - /** @psalm-suppress PossiblyNullArrayAccess */ - if ( - $data['type'] === TableSchema::CONSTRAINT_PRIMARY && - count($data['columns']) === 1 && - $schema->getColumn($data['columns'][0])['type'] === TableSchema::TYPE_INTEGER - ) { - return ''; - } - $clause = ''; - $type = ''; - if ($data['type'] === TableSchema::CONSTRAINT_PRIMARY) { - $type = 'PRIMARY KEY'; - } - if ($data['type'] === TableSchema::CONSTRAINT_UNIQUE) { - $type = 'UNIQUE'; - } - if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) { - $type = 'FOREIGN KEY'; - - $clause = sprintf( - ' REFERENCES %s (%s) ON UPDATE %s ON DELETE %s', - $this->_driver->quoteIdentifier($data['references'][0]), - $this->_convertConstraintColumns($data['references'][1]), - $this->_foreignOnClause($data['update']), - $this->_foreignOnClause($data['delete']) - ); - } - $columns = array_map( - [$this->_driver, 'quoteIdentifier'], - $data['columns'] - ); - - return sprintf( - 'CONSTRAINT %s %s (%s)%s', - $this->_driver->quoteIdentifier($name), - $type, - implode(', ', $columns), - $clause - ); - } - - /** - * {@inheritDoc} - * - * SQLite can not properly handle adding a constraint to an existing table. - * This method is no-op - * - * @param \Cake\Database\Schema\TableSchema $schema The table instance the foreign key constraints are. - * @return array SQL fragment. - */ - public function addConstraintSql(TableSchema $schema): array - { - return []; - } - - /** - * {@inheritDoc} - * - * SQLite can not properly handle dropping a constraint to an existing table. - * This method is no-op - * - * @param \Cake\Database\Schema\TableSchema $schema The table instance the foreign key constraints are. - * @return array SQL fragment. - */ - public function dropConstraintSql(TableSchema $schema): array - { - return []; - } - - /** - * @inheritDoc - */ - public function indexSql(TableSchema $schema, string $name): string - { - /** @var array $data */ - $data = $schema->getIndex($name); - $columns = array_map( - [$this->_driver, 'quoteIdentifier'], - $data['columns'] - ); - - return sprintf( - 'CREATE INDEX %s ON %s (%s)', - $this->_driver->quoteIdentifier($name), - $this->_driver->quoteIdentifier($schema->name()), - implode(', ', $columns) - ); - } - - /** - * @inheritDoc - */ - public function createTableSql(TableSchema $schema, array $columns, array $constraints, array $indexes): array - { - $lines = array_merge($columns, $constraints); - $content = implode(",\n", array_filter($lines)); - $temporary = $schema->isTemporary() ? ' TEMPORARY ' : ' '; - $table = sprintf("CREATE%sTABLE \"%s\" (\n%s\n)", $temporary, $schema->name(), $content); - $out = [$table]; - foreach ($indexes as $index) { - $out[] = $index; - } - - return $out; - } - - /** - * @inheritDoc - */ - public function truncateTableSql(TableSchema $schema): array - { - $name = $schema->name(); - $sql = []; - if ($this->hasSequences()) { - $sql[] = sprintf('DELETE FROM sqlite_sequence WHERE name="%s"', $name); - } - - $sql[] = sprintf('DELETE FROM "%s"', $name); - - return $sql; - } - - /** - * Returns whether there is any table in this connection to SQLite containing - * sequences - * - * @return bool - */ - public function hasSequences(): bool - { - $result = $this->_driver->prepare( - 'SELECT 1 FROM sqlite_master WHERE name = "sqlite_sequence"' - ); - $result->execute(); - $this->_hasSequences = (bool)$result->rowCount(); - $result->closeCursor(); - - return $this->_hasSequences; - } -} +// @deprecated 4.1.0 Load new class location and alias for old location +class_exists('Cake\Database\Schema\SqliteSchemaDialect'); diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/SqliteSchemaDialect.php b/app/vendor/cakephp/cakephp/src/Database/Schema/SqliteSchemaDialect.php new file mode 100644 index 000000000..77cf0e49b --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/SqliteSchemaDialect.php @@ -0,0 +1,612 @@ + TableSchema::TYPE_TEXT, 'length' => null]; + } + + preg_match('/(unsigned)?\s*([a-z]+)(?:\(([0-9,]+)\))?/i', $column, $matches); + if (empty($matches)) { + throw new DatabaseException(sprintf('Unable to parse column type from "%s"', $column)); + } + + $unsigned = false; + if (strtolower($matches[1]) === 'unsigned') { + $unsigned = true; + } + + $col = strtolower($matches[2]); + $length = $precision = null; + if (isset($matches[3])) { + $length = $matches[3]; + if (strpos($length, ',') !== false) { + [$length, $precision] = explode(',', $length); + } + $length = (int)$length; + $precision = (int)$precision; + } + + if ($col === 'bigint') { + return ['type' => TableSchema::TYPE_BIGINTEGER, 'length' => $length, 'unsigned' => $unsigned]; + } + if ($col === 'smallint') { + return ['type' => TableSchema::TYPE_SMALLINTEGER, 'length' => $length, 'unsigned' => $unsigned]; + } + if ($col === 'tinyint') { + return ['type' => TableSchema::TYPE_TINYINTEGER, 'length' => $length, 'unsigned' => $unsigned]; + } + if (strpos($col, 'int') !== false) { + return ['type' => TableSchema::TYPE_INTEGER, 'length' => $length, 'unsigned' => $unsigned]; + } + if (strpos($col, 'decimal') !== false) { + return [ + 'type' => TableSchema::TYPE_DECIMAL, + 'length' => $length, + 'precision' => $precision, + 'unsigned' => $unsigned, + ]; + } + if (in_array($col, ['float', 'real', 'double'])) { + return [ + 'type' => TableSchema::TYPE_FLOAT, + 'length' => $length, + 'precision' => $precision, + 'unsigned' => $unsigned, + ]; + } + + if (strpos($col, 'boolean') !== false) { + return ['type' => TableSchema::TYPE_BOOLEAN, 'length' => null]; + } + + if ($col === 'char' && $length === 36) { + return ['type' => TableSchema::TYPE_UUID, 'length' => null]; + } + if ($col === 'char') { + return ['type' => TableSchema::TYPE_CHAR, 'length' => $length]; + } + if (strpos($col, 'char') !== false) { + return ['type' => TableSchema::TYPE_STRING, 'length' => $length]; + } + + if ($col === 'binary' && $length === 16) { + return ['type' => TableSchema::TYPE_BINARY_UUID, 'length' => null]; + } + if (in_array($col, ['blob', 'clob', 'binary', 'varbinary'])) { + return ['type' => TableSchema::TYPE_BINARY, 'length' => $length]; + } + + $datetimeTypes = [ + 'date', + 'time', + 'timestamp', + 'timestampfractional', + 'timestamptimezone', + 'datetime', + 'datetimefractional', + ]; + if (in_array($col, $datetimeTypes)) { + return ['type' => $col, 'length' => null]; + } + + return ['type' => TableSchema::TYPE_TEXT, 'length' => null]; + } + + /** + * @inheritDoc + */ + public function listTablesSql(array $config): array + { + return [ + 'SELECT name FROM sqlite_master WHERE type="table" ' . + 'AND name != "sqlite_sequence" ORDER BY name', + [], + ]; + } + + /** + * @inheritDoc + */ + public function describeColumnSql(string $tableName, array $config): array + { + $sql = sprintf( + 'PRAGMA table_info(%s)', + $this->_driver->quoteIdentifier($tableName) + ); + + return [$sql, []]; + } + + /** + * @inheritDoc + */ + public function convertColumnDescription(TableSchema $schema, array $row): void + { + $field = $this->_convertColumn($row['type']); + $field += [ + 'null' => !$row['notnull'], + 'default' => $this->_defaultValue($row['dflt_value']), + ]; + $primary = $schema->getConstraint('primary'); + + if ($row['pk'] && empty($primary)) { + $field['null'] = false; + $field['autoIncrement'] = true; + } + + // SQLite does not support autoincrement on composite keys. + if ($row['pk'] && !empty($primary)) { + $existingColumn = $primary['columns'][0]; + /** @psalm-suppress PossiblyNullOperand */ + $schema->addColumn($existingColumn, ['autoIncrement' => null] + $schema->getColumn($existingColumn)); + } + + $schema->addColumn($row['name'], $field); + if ($row['pk']) { + $constraint = (array)$schema->getConstraint('primary') + [ + 'type' => TableSchema::CONSTRAINT_PRIMARY, + 'columns' => [], + ]; + $constraint['columns'] = array_merge($constraint['columns'], [$row['name']]); + $schema->addConstraint('primary', $constraint); + } + } + + /** + * Manipulate the default value. + * + * Sqlite includes quotes and bared NULLs in default values. + * We need to remove those. + * + * @param string|int|null $default The default value. + * @return string|int|null + */ + protected function _defaultValue($default) + { + if ($default === 'NULL' || $default === null) { + return null; + } + + // Remove quotes + if (is_string($default) && preg_match("/^'(.*)'$/", $default, $matches)) { + return str_replace("''", "'", $matches[1]); + } + + return $default; + } + + /** + * @inheritDoc + */ + public function describeIndexSql(string $tableName, array $config): array + { + $sql = sprintf( + 'PRAGMA index_list(%s)', + $this->_driver->quoteIdentifier($tableName) + ); + + return [$sql, []]; + } + + /** + * {@inheritDoc} + * + * Since SQLite does not have a way to get metadata about all indexes at once, + * additional queries are done here. Sqlite constraint names are not + * stable, and the names for constraints will not match those used to create + * the table. This is a limitation in Sqlite's metadata features. + * + * @param \Cake\Database\Schema\TableSchema $schema The table object to append + * an index or constraint to. + * @param array $row The row data from `describeIndexSql`. + * @return void + */ + public function convertIndexDescription(TableSchema $schema, array $row): void + { + $sql = sprintf( + 'PRAGMA index_info(%s)', + $this->_driver->quoteIdentifier($row['name']) + ); + $statement = $this->_driver->prepare($sql); + $statement->execute(); + $columns = []; + /** @psalm-suppress PossiblyFalseIterator */ + foreach ($statement->fetchAll('assoc') as $column) { + $columns[] = $column['name']; + } + $statement->closeCursor(); + if ($row['unique']) { + $schema->addConstraint($row['name'], [ + 'type' => TableSchema::CONSTRAINT_UNIQUE, + 'columns' => $columns, + ]); + } else { + $schema->addIndex($row['name'], [ + 'type' => TableSchema::INDEX_INDEX, + 'columns' => $columns, + ]); + } + } + + /** + * @inheritDoc + */ + public function describeForeignKeySql(string $tableName, array $config): array + { + $sql = sprintf('PRAGMA foreign_key_list(%s)', $this->_driver->quoteIdentifier($tableName)); + + return [$sql, []]; + } + + /** + * @inheritDoc + */ + public function convertForeignKeyDescription(TableSchema $schema, array $row): void + { + $name = $row['from'] . '_fk'; + + $update = $row['on_update'] ?? ''; + $delete = $row['on_delete'] ?? ''; + $data = [ + 'type' => TableSchema::CONSTRAINT_FOREIGN, + 'columns' => [$row['from']], + 'references' => [$row['table'], $row['to']], + 'update' => $this->_convertOnClause($update), + 'delete' => $this->_convertOnClause($delete), + ]; + + if (isset($this->_constraintsIdMap[$schema->name()][$row['id']])) { + $name = $this->_constraintsIdMap[$schema->name()][$row['id']]; + } else { + $this->_constraintsIdMap[$schema->name()][$row['id']] = $name; + } + + $schema->addConstraint($name, $data); + } + + /** + * {@inheritDoc} + * + * @param \Cake\Database\Schema\TableSchema $schema The table instance the column is in. + * @param string $name The name of the column. + * @return string SQL fragment. + * @throws \Cake\Database\Exception\DatabaseException when the column type is unknown + */ + public function columnSql(TableSchema $schema, string $name): string + { + /** @var array $data */ + $data = $schema->getColumn($name); + $typeMap = [ + TableSchema::TYPE_BINARY_UUID => ' BINARY(16)', + TableSchema::TYPE_UUID => ' CHAR(36)', + TableSchema::TYPE_CHAR => ' CHAR', + TableSchema::TYPE_TINYINTEGER => ' TINYINT', + TableSchema::TYPE_SMALLINTEGER => ' SMALLINT', + TableSchema::TYPE_INTEGER => ' INTEGER', + TableSchema::TYPE_BIGINTEGER => ' BIGINT', + TableSchema::TYPE_BOOLEAN => ' BOOLEAN', + TableSchema::TYPE_FLOAT => ' FLOAT', + TableSchema::TYPE_DECIMAL => ' DECIMAL', + TableSchema::TYPE_DATE => ' DATE', + TableSchema::TYPE_TIME => ' TIME', + TableSchema::TYPE_DATETIME => ' DATETIME', + TableSchema::TYPE_DATETIME_FRACTIONAL => ' DATETIMEFRACTIONAL', + TableSchema::TYPE_TIMESTAMP => ' TIMESTAMP', + TableSchema::TYPE_TIMESTAMP_FRACTIONAL => ' TIMESTAMPFRACTIONAL', + TableSchema::TYPE_TIMESTAMP_TIMEZONE => ' TIMESTAMPTIMEZONE', + TableSchema::TYPE_JSON => ' TEXT', + ]; + + $out = $this->_driver->quoteIdentifier($name); + $hasUnsigned = [ + TableSchema::TYPE_TINYINTEGER, + TableSchema::TYPE_SMALLINTEGER, + TableSchema::TYPE_INTEGER, + TableSchema::TYPE_BIGINTEGER, + TableSchema::TYPE_FLOAT, + TableSchema::TYPE_DECIMAL, + ]; + + if ( + in_array($data['type'], $hasUnsigned, true) && + isset($data['unsigned']) && + $data['unsigned'] === true + ) { + if ($data['type'] !== TableSchema::TYPE_INTEGER || $schema->getPrimaryKey() !== [$name]) { + $out .= ' UNSIGNED'; + } + } + + if (isset($typeMap[$data['type']])) { + $out .= $typeMap[$data['type']]; + } + + if ($data['type'] === TableSchema::TYPE_TEXT && $data['length'] !== TableSchema::LENGTH_TINY) { + $out .= ' TEXT'; + } + + if ($data['type'] === TableSchema::TYPE_CHAR) { + $out .= '(' . $data['length'] . ')'; + } + + if ( + $data['type'] === TableSchema::TYPE_STRING || + ( + $data['type'] === TableSchema::TYPE_TEXT && + $data['length'] === TableSchema::LENGTH_TINY + ) + ) { + $out .= ' VARCHAR'; + + if (isset($data['length'])) { + $out .= '(' . $data['length'] . ')'; + } + } + + if ($data['type'] === TableSchema::TYPE_BINARY) { + if (isset($data['length'])) { + $out .= ' BLOB(' . $data['length'] . ')'; + } else { + $out .= ' BLOB'; + } + } + + $integerTypes = [ + TableSchema::TYPE_TINYINTEGER, + TableSchema::TYPE_SMALLINTEGER, + TableSchema::TYPE_INTEGER, + ]; + if ( + in_array($data['type'], $integerTypes, true) && + isset($data['length']) && + $schema->getPrimaryKey() !== [$name] + ) { + $out .= '(' . (int)$data['length'] . ')'; + } + + $hasPrecision = [TableSchema::TYPE_FLOAT, TableSchema::TYPE_DECIMAL]; + if ( + in_array($data['type'], $hasPrecision, true) && + ( + isset($data['length']) || + isset($data['precision']) + ) + ) { + $out .= '(' . (int)$data['length'] . ',' . (int)$data['precision'] . ')'; + } + + if (isset($data['null']) && $data['null'] === false) { + $out .= ' NOT NULL'; + } + + if ($data['type'] === TableSchema::TYPE_INTEGER && $schema->getPrimaryKey() === [$name]) { + $out .= ' PRIMARY KEY AUTOINCREMENT'; + } + + $timestampTypes = [ + TableSchema::TYPE_DATETIME, + TableSchema::TYPE_DATETIME_FRACTIONAL, + TableSchema::TYPE_TIMESTAMP, + TableSchema::TYPE_TIMESTAMP_FRACTIONAL, + TableSchema::TYPE_TIMESTAMP_TIMEZONE, + ]; + if (isset($data['null']) && $data['null'] === true && in_array($data['type'], $timestampTypes, true)) { + $out .= ' DEFAULT NULL'; + } + if (isset($data['default'])) { + $out .= ' DEFAULT ' . $this->_driver->schemaValue($data['default']); + } + + return $out; + } + + /** + * {@inheritDoc} + * + * Note integer primary keys will return ''. This is intentional as Sqlite requires + * that integer primary keys be defined in the column definition. + * + * @param \Cake\Database\Schema\TableSchema $schema The table instance the column is in. + * @param string $name The name of the column. + * @return string SQL fragment. + */ + public function constraintSql(TableSchema $schema, string $name): string + { + /** @var array $data */ + $data = $schema->getConstraint($name); + /** @psalm-suppress PossiblyNullArrayAccess */ + if ( + $data['type'] === TableSchema::CONSTRAINT_PRIMARY && + count($data['columns']) === 1 && + $schema->getColumn($data['columns'][0])['type'] === TableSchema::TYPE_INTEGER + ) { + return ''; + } + $clause = ''; + $type = ''; + if ($data['type'] === TableSchema::CONSTRAINT_PRIMARY) { + $type = 'PRIMARY KEY'; + } + if ($data['type'] === TableSchema::CONSTRAINT_UNIQUE) { + $type = 'UNIQUE'; + } + if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) { + $type = 'FOREIGN KEY'; + + $clause = sprintf( + ' REFERENCES %s (%s) ON UPDATE %s ON DELETE %s', + $this->_driver->quoteIdentifier($data['references'][0]), + $this->_convertConstraintColumns($data['references'][1]), + $this->_foreignOnClause($data['update']), + $this->_foreignOnClause($data['delete']) + ); + } + $columns = array_map( + [$this->_driver, 'quoteIdentifier'], + $data['columns'] + ); + + return sprintf( + 'CONSTRAINT %s %s (%s)%s', + $this->_driver->quoteIdentifier($name), + $type, + implode(', ', $columns), + $clause + ); + } + + /** + * {@inheritDoc} + * + * SQLite can not properly handle adding a constraint to an existing table. + * This method is no-op + * + * @param \Cake\Database\Schema\TableSchema $schema The table instance the foreign key constraints are. + * @return array SQL fragment. + */ + public function addConstraintSql(TableSchema $schema): array + { + return []; + } + + /** + * {@inheritDoc} + * + * SQLite can not properly handle dropping a constraint to an existing table. + * This method is no-op + * + * @param \Cake\Database\Schema\TableSchema $schema The table instance the foreign key constraints are. + * @return array SQL fragment. + */ + public function dropConstraintSql(TableSchema $schema): array + { + return []; + } + + /** + * @inheritDoc + */ + public function indexSql(TableSchema $schema, string $name): string + { + /** @var array $data */ + $data = $schema->getIndex($name); + $columns = array_map( + [$this->_driver, 'quoteIdentifier'], + $data['columns'] + ); + + return sprintf( + 'CREATE INDEX %s ON %s (%s)', + $this->_driver->quoteIdentifier($name), + $this->_driver->quoteIdentifier($schema->name()), + implode(', ', $columns) + ); + } + + /** + * @inheritDoc + */ + public function createTableSql(TableSchema $schema, array $columns, array $constraints, array $indexes): array + { + $lines = array_merge($columns, $constraints); + $content = implode(",\n", array_filter($lines)); + $temporary = $schema->isTemporary() ? ' TEMPORARY ' : ' '; + $table = sprintf("CREATE%sTABLE \"%s\" (\n%s\n)", $temporary, $schema->name(), $content); + $out = [$table]; + foreach ($indexes as $index) { + $out[] = $index; + } + + return $out; + } + + /** + * @inheritDoc + */ + public function truncateTableSql(TableSchema $schema): array + { + $name = $schema->name(); + $sql = []; + if ($this->hasSequences()) { + $sql[] = sprintf('DELETE FROM sqlite_sequence WHERE name="%s"', $name); + } + + $sql[] = sprintf('DELETE FROM "%s"', $name); + + return $sql; + } + + /** + * Returns whether there is any table in this connection to SQLite containing + * sequences + * + * @return bool + */ + public function hasSequences(): bool + { + $result = $this->_driver->prepare( + 'SELECT 1 FROM sqlite_master WHERE name = "sqlite_sequence"' + ); + $result->execute(); + $this->_hasSequences = (bool)$result->rowCount(); + $result->closeCursor(); + + return $this->_hasSequences; + } +} + +// phpcs:disable +// Add backwards compatible alias. +class_alias('Cake\Database\Schema\SqliteSchemaDialect', 'Cake\Database\Schema\SqliteSchema'); +// phpcs:enable diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/SqlserverSchema.php b/app/vendor/cakephp/cakephp/src/Database/Schema/SqlserverSchema.php index b69d4a5de..dbe584efd 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/SqlserverSchema.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/SqlserverSchema.php @@ -1,658 +1,5 @@ $col, 'length' => null]; - } - - if ($col === 'datetime') { - // datetime cannot parse more than 3 digits of precision and isn't accurate - return ['type' => TableSchema::TYPE_DATETIME, 'length' => null]; - } - if (strpos($col, 'datetime') !== false) { - $typeName = TableSchema::TYPE_DATETIME; - if ($scale > 0) { - $typeName = TableSchema::TYPE_DATETIME_FRACTIONAL; - } - - return ['type' => $typeName, 'length' => null, 'precision' => $scale]; - } - - if ($col === 'char') { - return ['type' => TableSchema::TYPE_CHAR, 'length' => $length]; - } - - if ($col === 'tinyint') { - return ['type' => TableSchema::TYPE_TINYINTEGER, 'length' => $precision ?: 3]; - } - if ($col === 'smallint') { - return ['type' => TableSchema::TYPE_SMALLINTEGER, 'length' => $precision ?: 5]; - } - if ($col === 'int' || $col === 'integer') { - return ['type' => TableSchema::TYPE_INTEGER, 'length' => $precision ?: 10]; - } - if ($col === 'bigint') { - return ['type' => TableSchema::TYPE_BIGINTEGER, 'length' => $precision ?: 20]; - } - if ($col === 'bit') { - return ['type' => TableSchema::TYPE_BOOLEAN, 'length' => null]; - } - if ( - strpos($col, 'numeric') !== false || - strpos($col, 'money') !== false || - strpos($col, 'decimal') !== false - ) { - return ['type' => TableSchema::TYPE_DECIMAL, 'length' => $precision, 'precision' => $scale]; - } - - if ($col === 'real' || $col === 'float') { - return ['type' => TableSchema::TYPE_FLOAT, 'length' => null]; - } - // SqlServer schema reflection returns double length for unicode - // columns because internally it uses UTF16/UCS2 - if ($col === 'nvarchar' || $col === 'nchar' || $col === 'ntext') { - $length /= 2; - } - if (strpos($col, 'varchar') !== false && $length < 0) { - return ['type' => TableSchema::TYPE_TEXT, 'length' => null]; - } - - if (strpos($col, 'varchar') !== false) { - return ['type' => TableSchema::TYPE_STRING, 'length' => $length ?: 255]; - } - - if (strpos($col, 'char') !== false) { - return ['type' => TableSchema::TYPE_CHAR, 'length' => $length]; - } - - if (strpos($col, 'text') !== false) { - return ['type' => TableSchema::TYPE_TEXT, 'length' => null]; - } - - if ($col === 'image' || strpos($col, 'binary') !== false) { - // -1 is the value for MAX which we treat as a 'long' binary - if ($length == -1) { - $length = TableSchema::LENGTH_LONG; - } - - return ['type' => TableSchema::TYPE_BINARY, 'length' => $length]; - } - - if ($col === 'uniqueidentifier') { - return ['type' => TableSchema::TYPE_UUID]; - } - - return ['type' => TableSchema::TYPE_STRING, 'length' => null]; - } - - /** - * @inheritDoc - */ - public function convertColumnDescription(TableSchema $schema, array $row): void - { - $field = $this->_convertColumn( - $row['type'], - $row['char_length'] !== null ? (int)$row['char_length'] : null, - $row['precision'] !== null ? (int)$row['precision'] : null, - $row['scale'] !== null ? (int)$row['scale'] : null - ); - - if (!empty($row['autoincrement'])) { - $field['autoIncrement'] = true; - } - - $field += [ - 'null' => $row['null'] === '1', - 'default' => $this->_defaultValue($field['type'], $row['default']), - 'collate' => $row['collation_name'], - ]; - $schema->addColumn($row['name'], $field); - } - - /** - * Manipulate the default value. - * - * Removes () wrapping default values, extracts strings from - * N'' wrappers and collation text and converts NULL strings. - * - * @param string $type The schema type - * @param string|null $default The default value. - * @return string|int|null - */ - protected function _defaultValue($type, $default) - { - if ($default === null) { - return $default; - } - - // remove () surrounding value (NULL) but leave () at the end of functions - // integers might have two ((0)) wrapping value - if (preg_match('/^\(+(.*?(\(\))?)\)+$/', $default, $matches)) { - $default = $matches[1]; - } - - if ($default === 'NULL') { - return null; - } - - if ($type === TableSchema::TYPE_BOOLEAN) { - return (int)$default; - } - - // Remove quotes - if (preg_match("/^\(?N?'(.*)'\)?/", $default, $matches)) { - return str_replace("''", "'", $matches[1]); - } - - return $default; - } - - /** - * @inheritDoc - */ - public function describeIndexSql(string $tableName, array $config): array - { - $sql = "SELECT - I.[name] AS [index_name], - IC.[index_column_id] AS [index_order], - AC.[name] AS [column_name], - I.[is_unique], I.[is_primary_key], - I.[is_unique_constraint] - FROM sys.[tables] AS T - INNER JOIN sys.[schemas] S ON S.[schema_id] = T.[schema_id] - INNER JOIN sys.[indexes] I ON T.[object_id] = I.[object_id] - INNER JOIN sys.[index_columns] IC ON I.[object_id] = IC.[object_id] AND I.[index_id] = IC.[index_id] - INNER JOIN sys.[all_columns] AC ON T.[object_id] = AC.[object_id] AND IC.[column_id] = AC.[column_id] - WHERE T.[is_ms_shipped] = 0 AND I.[type_desc] <> 'HEAP' AND T.[name] = ? AND S.[name] = ? - ORDER BY I.[index_id], IC.[index_column_id]"; - - $schema = empty($config['schema']) ? static::DEFAULT_SCHEMA_NAME : $config['schema']; - - return [$sql, [$tableName, $schema]]; - } - - /** - * @inheritDoc - */ - public function convertIndexDescription(TableSchema $schema, array $row): void - { - $type = TableSchema::INDEX_INDEX; - $name = $row['index_name']; - if ($row['is_primary_key']) { - $name = $type = TableSchema::CONSTRAINT_PRIMARY; - } - if ($row['is_unique_constraint'] && $type === TableSchema::INDEX_INDEX) { - $type = TableSchema::CONSTRAINT_UNIQUE; - } - - if ($type === TableSchema::INDEX_INDEX) { - $existing = $schema->getIndex($name); - } else { - $existing = $schema->getConstraint($name); - } - - $columns = [$row['column_name']]; - if (!empty($existing)) { - $columns = array_merge($existing['columns'], $columns); - } - - if ($type === TableSchema::CONSTRAINT_PRIMARY || $type === TableSchema::CONSTRAINT_UNIQUE) { - $schema->addConstraint($name, [ - 'type' => $type, - 'columns' => $columns, - ]); - - return; - } - $schema->addIndex($name, [ - 'type' => $type, - 'columns' => $columns, - ]); - } - - /** - * @inheritDoc - */ - public function describeForeignKeySql(string $tableName, array $config): array - { - // phpcs:disable Generic.Files.LineLength - $sql = 'SELECT FK.[name] AS [foreign_key_name], FK.[delete_referential_action_desc] AS [delete_type], - FK.[update_referential_action_desc] AS [update_type], C.name AS [column], RT.name AS [reference_table], - RC.name AS [reference_column] - FROM sys.foreign_keys FK - INNER JOIN sys.foreign_key_columns FKC ON FKC.constraint_object_id = FK.object_id - INNER JOIN sys.tables T ON T.object_id = FKC.parent_object_id - INNER JOIN sys.tables RT ON RT.object_id = FKC.referenced_object_id - INNER JOIN sys.schemas S ON S.schema_id = T.schema_id AND S.schema_id = RT.schema_id - INNER JOIN sys.columns C ON C.column_id = FKC.parent_column_id AND C.object_id = FKC.parent_object_id - INNER JOIN sys.columns RC ON RC.column_id = FKC.referenced_column_id AND RC.object_id = FKC.referenced_object_id - WHERE FK.is_ms_shipped = 0 AND T.name = ? AND S.name = ?'; - // phpcs:enable Generic.Files.LineLength - - $schema = empty($config['schema']) ? static::DEFAULT_SCHEMA_NAME : $config['schema']; - - return [$sql, [$tableName, $schema]]; - } - - /** - * @inheritDoc - */ - public function convertForeignKeyDescription(TableSchema $schema, array $row): void - { - $data = [ - 'type' => TableSchema::CONSTRAINT_FOREIGN, - 'columns' => [$row['column']], - 'references' => [$row['reference_table'], $row['reference_column']], - 'update' => $this->_convertOnClause($row['update_type']), - 'delete' => $this->_convertOnClause($row['delete_type']), - ]; - $name = $row['foreign_key_name']; - $schema->addConstraint($name, $data); - } - - /** - * @inheritDoc - */ - protected function _foreignOnClause(string $on): string - { - $parent = parent::_foreignOnClause($on); - - return $parent === 'RESTRICT' ? parent::_foreignOnClause(TableSchema::ACTION_NO_ACTION) : $parent; - } - - /** - * @inheritDoc - */ - protected function _convertOnClause(string $clause): string - { - switch ($clause) { - case 'NO_ACTION': - return TableSchema::ACTION_NO_ACTION; - case 'CASCADE': - return TableSchema::ACTION_CASCADE; - case 'SET_NULL': - return TableSchema::ACTION_SET_NULL; - case 'SET_DEFAULT': - return TableSchema::ACTION_SET_DEFAULT; - } - - return TableSchema::ACTION_SET_NULL; - } - - /** - * @inheritDoc - */ - public function columnSql(TableSchema $schema, string $name): string - { - /** @var array $data */ - $data = $schema->getColumn($name); - $out = $this->_driver->quoteIdentifier($name); - $typeMap = [ - TableSchema::TYPE_TINYINTEGER => ' TINYINT', - TableSchema::TYPE_SMALLINTEGER => ' SMALLINT', - TableSchema::TYPE_INTEGER => ' INTEGER', - TableSchema::TYPE_BIGINTEGER => ' BIGINT', - TableSchema::TYPE_BINARY_UUID => ' UNIQUEIDENTIFIER', - TableSchema::TYPE_BOOLEAN => ' BIT', - TableSchema::TYPE_CHAR => ' NCHAR', - TableSchema::TYPE_FLOAT => ' FLOAT', - TableSchema::TYPE_DECIMAL => ' DECIMAL', - TableSchema::TYPE_DATE => ' DATE', - TableSchema::TYPE_TIME => ' TIME', - TableSchema::TYPE_DATETIME => ' DATETIME2', - TableSchema::TYPE_DATETIME_FRACTIONAL => ' DATETIME2', - TableSchema::TYPE_TIMESTAMP => ' DATETIME2', - TableSchema::TYPE_TIMESTAMP_FRACTIONAL => ' DATETIME2', - TableSchema::TYPE_TIMESTAMP_TIMEZONE => ' DATETIME2', - TableSchema::TYPE_UUID => ' UNIQUEIDENTIFIER', - TableSchema::TYPE_JSON => ' NVARCHAR(MAX)', - ]; - - if (isset($typeMap[$data['type']])) { - $out .= $typeMap[$data['type']]; - } - - if ($data['type'] === TableSchema::TYPE_INTEGER || $data['type'] === TableSchema::TYPE_BIGINTEGER) { - if ($schema->getPrimaryKey() === [$name] || $data['autoIncrement'] === true) { - unset($data['null'], $data['default']); - $out .= ' IDENTITY(1, 1)'; - } - } - - if ($data['type'] === TableSchema::TYPE_TEXT && $data['length'] !== TableSchema::LENGTH_TINY) { - $out .= ' NVARCHAR(MAX)'; - } - - if ($data['type'] === TableSchema::TYPE_CHAR) { - $out .= '(' . $data['length'] . ')'; - } - - if ($data['type'] === TableSchema::TYPE_BINARY) { - if ( - !isset($data['length']) - || in_array($data['length'], [TableSchema::LENGTH_MEDIUM, TableSchema::LENGTH_LONG], true) - ) { - $data['length'] = 'MAX'; - } - - if ($data['length'] === 1) { - $out .= ' BINARY(1)'; - } else { - $out .= ' VARBINARY'; - - $out .= sprintf('(%s)', $data['length']); - } - } - - if ( - $data['type'] === TableSchema::TYPE_STRING || - ( - $data['type'] === TableSchema::TYPE_TEXT && - $data['length'] === TableSchema::LENGTH_TINY - ) - ) { - $type = ' NVARCHAR'; - $length = $data['length'] ?? TableSchema::LENGTH_TINY; - $out .= sprintf('%s(%d)', $type, $length); - } - - $hasCollate = [TableSchema::TYPE_TEXT, TableSchema::TYPE_STRING, TableSchema::TYPE_CHAR]; - if (in_array($data['type'], $hasCollate, true) && isset($data['collate']) && $data['collate'] !== '') { - $out .= ' COLLATE ' . $data['collate']; - } - - $precisionTypes = [ - TableSchema::TYPE_FLOAT, - TableSchema::TYPE_DATETIME, - TableSchema::TYPE_DATETIME_FRACTIONAL, - TableSchema::TYPE_TIMESTAMP, - TableSchema::TYPE_TIMESTAMP_FRACTIONAL, - ]; - if (in_array($data['type'], $precisionTypes, true) && isset($data['precision'])) { - $out .= '(' . (int)$data['precision'] . ')'; - } - - if ( - $data['type'] === TableSchema::TYPE_DECIMAL && - ( - isset($data['length']) || - isset($data['precision']) - ) - ) { - $out .= '(' . (int)$data['length'] . ',' . (int)$data['precision'] . ')'; - } - - if (isset($data['null']) && $data['null'] === false) { - $out .= ' NOT NULL'; - } - - $dateTimeTypes = [ - TableSchema::TYPE_DATETIME, - TableSchema::TYPE_DATETIME_FRACTIONAL, - TableSchema::TYPE_TIMESTAMP, - TableSchema::TYPE_TIMESTAMP_FRACTIONAL, - ]; - $dateTimeDefaults = [ - 'current_timestamp', - 'getdate()', - 'getutcdate()', - 'sysdatetime()', - 'sysutcdatetime()', - 'sysdatetimeoffset()', - ]; - if ( - isset($data['default']) && - in_array($data['type'], $dateTimeTypes, true) && - in_array(strtolower($data['default']), $dateTimeDefaults, true) - ) { - $out .= ' DEFAULT ' . strtoupper($data['default']); - } elseif (isset($data['default'])) { - $default = is_bool($data['default']) - ? (int)$data['default'] - : $this->_driver->schemaValue($data['default']); - $out .= ' DEFAULT ' . $default; - } elseif (isset($data['null']) && $data['null'] !== false) { - $out .= ' DEFAULT NULL'; - } - - return $out; - } - - /** - * @inheritDoc - */ - public function addConstraintSql(TableSchema $schema): array - { - $sqlPattern = 'ALTER TABLE %s ADD %s;'; - $sql = []; - - foreach ($schema->constraints() as $name) { - /** @var array $constraint */ - $constraint = $schema->getConstraint($name); - if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) { - $tableName = $this->_driver->quoteIdentifier($schema->name()); - $sql[] = sprintf($sqlPattern, $tableName, $this->constraintSql($schema, $name)); - } - } - - return $sql; - } - - /** - * @inheritDoc - */ - public function dropConstraintSql(TableSchema $schema): array - { - $sqlPattern = 'ALTER TABLE %s DROP CONSTRAINT %s;'; - $sql = []; - - foreach ($schema->constraints() as $name) { - /** @var array $constraint */ - $constraint = $schema->getConstraint($name); - if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) { - $tableName = $this->_driver->quoteIdentifier($schema->name()); - $constraintName = $this->_driver->quoteIdentifier($name); - $sql[] = sprintf($sqlPattern, $tableName, $constraintName); - } - } - - return $sql; - } - - /** - * @inheritDoc - */ - public function indexSql(TableSchema $schema, string $name): string - { - /** @var array $data */ - $data = $schema->getIndex($name); - $columns = array_map( - [$this->_driver, 'quoteIdentifier'], - $data['columns'] - ); - - return sprintf( - 'CREATE INDEX %s ON %s (%s)', - $this->_driver->quoteIdentifier($name), - $this->_driver->quoteIdentifier($schema->name()), - implode(', ', $columns) - ); - } - - /** - * @inheritDoc - */ - public function constraintSql(TableSchema $schema, string $name): string - { - /** @var array $data */ - $data = $schema->getConstraint($name); - $out = 'CONSTRAINT ' . $this->_driver->quoteIdentifier($name); - if ($data['type'] === TableSchema::CONSTRAINT_PRIMARY) { - $out = 'PRIMARY KEY'; - } - if ($data['type'] === TableSchema::CONSTRAINT_UNIQUE) { - $out .= ' UNIQUE'; - } - - return $this->_keySql($out, $data); - } - - /** - * Helper method for generating key SQL snippets. - * - * @param string $prefix The key prefix - * @param array $data Key data. - * @return string - */ - protected function _keySql(string $prefix, array $data): string - { - $columns = array_map( - [$this->_driver, 'quoteIdentifier'], - $data['columns'] - ); - if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) { - return $prefix . sprintf( - ' FOREIGN KEY (%s) REFERENCES %s (%s) ON UPDATE %s ON DELETE %s', - implode(', ', $columns), - $this->_driver->quoteIdentifier($data['references'][0]), - $this->_convertConstraintColumns($data['references'][1]), - $this->_foreignOnClause($data['update']), - $this->_foreignOnClause($data['delete']) - ); - } - - return $prefix . ' (' . implode(', ', $columns) . ')'; - } - - /** - * @inheritDoc - */ - public function createTableSql(TableSchema $schema, array $columns, array $constraints, array $indexes): array - { - $content = array_merge($columns, $constraints); - $content = implode(",\n", array_filter($content)); - $tableName = $this->_driver->quoteIdentifier($schema->name()); - $out = []; - $out[] = sprintf("CREATE TABLE %s (\n%s\n)", $tableName, $content); - foreach ($indexes as $index) { - $out[] = $index; - } - - return $out; - } - - /** - * @inheritDoc - */ - public function truncateTableSql(TableSchema $schema): array - { - $name = $this->_driver->quoteIdentifier($schema->name()); - $queries = [ - sprintf('DELETE FROM %s', $name), - ]; - - // Restart identity sequences - $pk = $schema->getPrimaryKey(); - if (count($pk) === 1) { - /** @var array $column */ - $column = $schema->getColumn($pk[0]); - if (in_array($column['type'], ['integer', 'biginteger'])) { - $queries[] = sprintf( - "DBCC CHECKIDENT('%s', RESEED, 0)", - $schema->name() - ); - } - } - - return $queries; - } -} +// @deprecated 4.1.0 Load new class location and alias for old location +class_exists('Cake\Database\Schema\SqlServerSchemaDialect'); diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/SqlserverSchemaDialect.php b/app/vendor/cakephp/cakephp/src/Database/Schema/SqlserverSchemaDialect.php new file mode 100644 index 000000000..2178e3ab3 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/SqlserverSchemaDialect.php @@ -0,0 +1,667 @@ + $col, 'length' => null]; + } + + if ($col === 'datetime') { + // datetime cannot parse more than 3 digits of precision and isn't accurate + return ['type' => TableSchema::TYPE_DATETIME, 'length' => null]; + } + if (strpos($col, 'datetime') !== false) { + $typeName = TableSchema::TYPE_DATETIME; + if ($scale > 0) { + $typeName = TableSchema::TYPE_DATETIME_FRACTIONAL; + } + + return ['type' => $typeName, 'length' => null, 'precision' => $scale]; + } + + if ($col === 'char') { + return ['type' => TableSchema::TYPE_CHAR, 'length' => $length]; + } + + if ($col === 'tinyint') { + return ['type' => TableSchema::TYPE_TINYINTEGER, 'length' => $precision ?: 3]; + } + if ($col === 'smallint') { + return ['type' => TableSchema::TYPE_SMALLINTEGER, 'length' => $precision ?: 5]; + } + if ($col === 'int' || $col === 'integer') { + return ['type' => TableSchema::TYPE_INTEGER, 'length' => $precision ?: 10]; + } + if ($col === 'bigint') { + return ['type' => TableSchema::TYPE_BIGINTEGER, 'length' => $precision ?: 20]; + } + if ($col === 'bit') { + return ['type' => TableSchema::TYPE_BOOLEAN, 'length' => null]; + } + if ( + strpos($col, 'numeric') !== false || + strpos($col, 'money') !== false || + strpos($col, 'decimal') !== false + ) { + return ['type' => TableSchema::TYPE_DECIMAL, 'length' => $precision, 'precision' => $scale]; + } + + if ($col === 'real' || $col === 'float') { + return ['type' => TableSchema::TYPE_FLOAT, 'length' => null]; + } + // SqlServer schema reflection returns double length for unicode + // columns because internally it uses UTF16/UCS2 + if ($col === 'nvarchar' || $col === 'nchar' || $col === 'ntext') { + $length /= 2; + } + if (strpos($col, 'varchar') !== false && $length < 0) { + return ['type' => TableSchema::TYPE_TEXT, 'length' => null]; + } + + if (strpos($col, 'varchar') !== false) { + return ['type' => TableSchema::TYPE_STRING, 'length' => $length ?: 255]; + } + + if (strpos($col, 'char') !== false) { + return ['type' => TableSchema::TYPE_CHAR, 'length' => $length]; + } + + if (strpos($col, 'text') !== false) { + return ['type' => TableSchema::TYPE_TEXT, 'length' => null]; + } + + if ($col === 'image' || strpos($col, 'binary') !== false) { + // -1 is the value for MAX which we treat as a 'long' binary + if ($length == -1) { + $length = TableSchema::LENGTH_LONG; + } + + return ['type' => TableSchema::TYPE_BINARY, 'length' => $length]; + } + + if ($col === 'uniqueidentifier') { + return ['type' => TableSchema::TYPE_UUID]; + } + + return ['type' => TableSchema::TYPE_STRING, 'length' => null]; + } + + /** + * @inheritDoc + */ + public function convertColumnDescription(TableSchema $schema, array $row): void + { + $field = $this->_convertColumn( + $row['type'], + $row['char_length'] !== null ? (int)$row['char_length'] : null, + $row['precision'] !== null ? (int)$row['precision'] : null, + $row['scale'] !== null ? (int)$row['scale'] : null + ); + + if (!empty($row['autoincrement'])) { + $field['autoIncrement'] = true; + } + + $field += [ + 'null' => $row['null'] === '1', + 'default' => $this->_defaultValue($field['type'], $row['default']), + 'collate' => $row['collation_name'], + ]; + $schema->addColumn($row['name'], $field); + } + + /** + * Manipulate the default value. + * + * Removes () wrapping default values, extracts strings from + * N'' wrappers and collation text and converts NULL strings. + * + * @param string $type The schema type + * @param string|null $default The default value. + * @return string|int|null + */ + protected function _defaultValue($type, $default) + { + if ($default === null) { + return $default; + } + + // remove () surrounding value (NULL) but leave () at the end of functions + // integers might have two ((0)) wrapping value + if (preg_match('/^\(+(.*?(\(\))?)\)+$/', $default, $matches)) { + $default = $matches[1]; + } + + if ($default === 'NULL') { + return null; + } + + if ($type === TableSchema::TYPE_BOOLEAN) { + return (int)$default; + } + + // Remove quotes + if (preg_match("/^\(?N?'(.*)'\)?/", $default, $matches)) { + return str_replace("''", "'", $matches[1]); + } + + return $default; + } + + /** + * @inheritDoc + */ + public function describeIndexSql(string $tableName, array $config): array + { + $sql = "SELECT + I.[name] AS [index_name], + IC.[index_column_id] AS [index_order], + AC.[name] AS [column_name], + I.[is_unique], I.[is_primary_key], + I.[is_unique_constraint] + FROM sys.[tables] AS T + INNER JOIN sys.[schemas] S ON S.[schema_id] = T.[schema_id] + INNER JOIN sys.[indexes] I ON T.[object_id] = I.[object_id] + INNER JOIN sys.[index_columns] IC ON I.[object_id] = IC.[object_id] AND I.[index_id] = IC.[index_id] + INNER JOIN sys.[all_columns] AC ON T.[object_id] = AC.[object_id] AND IC.[column_id] = AC.[column_id] + WHERE T.[is_ms_shipped] = 0 AND I.[type_desc] <> 'HEAP' AND T.[name] = ? AND S.[name] = ? + ORDER BY I.[index_id], IC.[index_column_id]"; + + $schema = empty($config['schema']) ? static::DEFAULT_SCHEMA_NAME : $config['schema']; + + return [$sql, [$tableName, $schema]]; + } + + /** + * @inheritDoc + */ + public function convertIndexDescription(TableSchema $schema, array $row): void + { + $type = TableSchema::INDEX_INDEX; + $name = $row['index_name']; + if ($row['is_primary_key']) { + $name = $type = TableSchema::CONSTRAINT_PRIMARY; + } + if ($row['is_unique_constraint'] && $type === TableSchema::INDEX_INDEX) { + $type = TableSchema::CONSTRAINT_UNIQUE; + } + + if ($type === TableSchema::INDEX_INDEX) { + $existing = $schema->getIndex($name); + } else { + $existing = $schema->getConstraint($name); + } + + $columns = [$row['column_name']]; + if (!empty($existing)) { + $columns = array_merge($existing['columns'], $columns); + } + + if ($type === TableSchema::CONSTRAINT_PRIMARY || $type === TableSchema::CONSTRAINT_UNIQUE) { + $schema->addConstraint($name, [ + 'type' => $type, + 'columns' => $columns, + ]); + + return; + } + $schema->addIndex($name, [ + 'type' => $type, + 'columns' => $columns, + ]); + } + + /** + * @inheritDoc + */ + public function describeForeignKeySql(string $tableName, array $config): array + { + // phpcs:disable Generic.Files.LineLength + $sql = 'SELECT FK.[name] AS [foreign_key_name], FK.[delete_referential_action_desc] AS [delete_type], + FK.[update_referential_action_desc] AS [update_type], C.name AS [column], RT.name AS [reference_table], + RC.name AS [reference_column] + FROM sys.foreign_keys FK + INNER JOIN sys.foreign_key_columns FKC ON FKC.constraint_object_id = FK.object_id + INNER JOIN sys.tables T ON T.object_id = FKC.parent_object_id + INNER JOIN sys.tables RT ON RT.object_id = FKC.referenced_object_id + INNER JOIN sys.schemas S ON S.schema_id = T.schema_id AND S.schema_id = RT.schema_id + INNER JOIN sys.columns C ON C.column_id = FKC.parent_column_id AND C.object_id = FKC.parent_object_id + INNER JOIN sys.columns RC ON RC.column_id = FKC.referenced_column_id AND RC.object_id = FKC.referenced_object_id + WHERE FK.is_ms_shipped = 0 AND T.name = ? AND S.name = ?'; + // phpcs:enable Generic.Files.LineLength + + $schema = empty($config['schema']) ? static::DEFAULT_SCHEMA_NAME : $config['schema']; + + return [$sql, [$tableName, $schema]]; + } + + /** + * @inheritDoc + */ + public function convertForeignKeyDescription(TableSchema $schema, array $row): void + { + $data = [ + 'type' => TableSchema::CONSTRAINT_FOREIGN, + 'columns' => [$row['column']], + 'references' => [$row['reference_table'], $row['reference_column']], + 'update' => $this->_convertOnClause($row['update_type']), + 'delete' => $this->_convertOnClause($row['delete_type']), + ]; + $name = $row['foreign_key_name']; + $schema->addConstraint($name, $data); + } + + /** + * @inheritDoc + */ + protected function _foreignOnClause(string $on): string + { + $parent = parent::_foreignOnClause($on); + + return $parent === 'RESTRICT' ? parent::_foreignOnClause(TableSchema::ACTION_NO_ACTION) : $parent; + } + + /** + * @inheritDoc + */ + protected function _convertOnClause(string $clause): string + { + switch ($clause) { + case 'NO_ACTION': + return TableSchema::ACTION_NO_ACTION; + case 'CASCADE': + return TableSchema::ACTION_CASCADE; + case 'SET_NULL': + return TableSchema::ACTION_SET_NULL; + case 'SET_DEFAULT': + return TableSchema::ACTION_SET_DEFAULT; + } + + return TableSchema::ACTION_SET_NULL; + } + + /** + * @inheritDoc + */ + public function columnSql(TableSchema $schema, string $name): string + { + /** @var array $data */ + $data = $schema->getColumn($name); + $out = $this->_driver->quoteIdentifier($name); + $typeMap = [ + TableSchema::TYPE_TINYINTEGER => ' TINYINT', + TableSchema::TYPE_SMALLINTEGER => ' SMALLINT', + TableSchema::TYPE_INTEGER => ' INTEGER', + TableSchema::TYPE_BIGINTEGER => ' BIGINT', + TableSchema::TYPE_BINARY_UUID => ' UNIQUEIDENTIFIER', + TableSchema::TYPE_BOOLEAN => ' BIT', + TableSchema::TYPE_CHAR => ' NCHAR', + TableSchema::TYPE_FLOAT => ' FLOAT', + TableSchema::TYPE_DECIMAL => ' DECIMAL', + TableSchema::TYPE_DATE => ' DATE', + TableSchema::TYPE_TIME => ' TIME', + TableSchema::TYPE_DATETIME => ' DATETIME2', + TableSchema::TYPE_DATETIME_FRACTIONAL => ' DATETIME2', + TableSchema::TYPE_TIMESTAMP => ' DATETIME2', + TableSchema::TYPE_TIMESTAMP_FRACTIONAL => ' DATETIME2', + TableSchema::TYPE_TIMESTAMP_TIMEZONE => ' DATETIME2', + TableSchema::TYPE_UUID => ' UNIQUEIDENTIFIER', + TableSchema::TYPE_JSON => ' NVARCHAR(MAX)', + ]; + + if (isset($typeMap[$data['type']])) { + $out .= $typeMap[$data['type']]; + } + + if ($data['type'] === TableSchema::TYPE_INTEGER || $data['type'] === TableSchema::TYPE_BIGINTEGER) { + if ($schema->getPrimaryKey() === [$name] || $data['autoIncrement'] === true) { + unset($data['null'], $data['default']); + $out .= ' IDENTITY(1, 1)'; + } + } + + if ($data['type'] === TableSchema::TYPE_TEXT && $data['length'] !== TableSchema::LENGTH_TINY) { + $out .= ' NVARCHAR(MAX)'; + } + + if ($data['type'] === TableSchema::TYPE_CHAR) { + $out .= '(' . $data['length'] . ')'; + } + + if ($data['type'] === TableSchema::TYPE_BINARY) { + if ( + !isset($data['length']) + || in_array($data['length'], [TableSchema::LENGTH_MEDIUM, TableSchema::LENGTH_LONG], true) + ) { + $data['length'] = 'MAX'; + } + + if ($data['length'] === 1) { + $out .= ' BINARY(1)'; + } else { + $out .= ' VARBINARY'; + + $out .= sprintf('(%s)', $data['length']); + } + } + + if ( + $data['type'] === TableSchema::TYPE_STRING || + ( + $data['type'] === TableSchema::TYPE_TEXT && + $data['length'] === TableSchema::LENGTH_TINY + ) + ) { + $type = ' NVARCHAR'; + $length = $data['length'] ?? TableSchema::LENGTH_TINY; + $out .= sprintf('%s(%d)', $type, $length); + } + + $hasCollate = [TableSchema::TYPE_TEXT, TableSchema::TYPE_STRING, TableSchema::TYPE_CHAR]; + if (in_array($data['type'], $hasCollate, true) && isset($data['collate']) && $data['collate'] !== '') { + $out .= ' COLLATE ' . $data['collate']; + } + + $precisionTypes = [ + TableSchema::TYPE_FLOAT, + TableSchema::TYPE_DATETIME, + TableSchema::TYPE_DATETIME_FRACTIONAL, + TableSchema::TYPE_TIMESTAMP, + TableSchema::TYPE_TIMESTAMP_FRACTIONAL, + ]; + if (in_array($data['type'], $precisionTypes, true) && isset($data['precision'])) { + $out .= '(' . (int)$data['precision'] . ')'; + } + + if ( + $data['type'] === TableSchema::TYPE_DECIMAL && + ( + isset($data['length']) || + isset($data['precision']) + ) + ) { + $out .= '(' . (int)$data['length'] . ',' . (int)$data['precision'] . ')'; + } + + if (isset($data['null']) && $data['null'] === false) { + $out .= ' NOT NULL'; + } + + $dateTimeTypes = [ + TableSchema::TYPE_DATETIME, + TableSchema::TYPE_DATETIME_FRACTIONAL, + TableSchema::TYPE_TIMESTAMP, + TableSchema::TYPE_TIMESTAMP_FRACTIONAL, + ]; + $dateTimeDefaults = [ + 'current_timestamp', + 'getdate()', + 'getutcdate()', + 'sysdatetime()', + 'sysutcdatetime()', + 'sysdatetimeoffset()', + ]; + if ( + isset($data['default']) && + in_array($data['type'], $dateTimeTypes, true) && + in_array(strtolower($data['default']), $dateTimeDefaults, true) + ) { + $out .= ' DEFAULT ' . strtoupper($data['default']); + } elseif (isset($data['default'])) { + $default = is_bool($data['default']) + ? (int)$data['default'] + : $this->_driver->schemaValue($data['default']); + $out .= ' DEFAULT ' . $default; + } elseif (isset($data['null']) && $data['null'] !== false) { + $out .= ' DEFAULT NULL'; + } + + return $out; + } + + /** + * @inheritDoc + */ + public function addConstraintSql(TableSchema $schema): array + { + $sqlPattern = 'ALTER TABLE %s ADD %s;'; + $sql = []; + + foreach ($schema->constraints() as $name) { + /** @var array $constraint */ + $constraint = $schema->getConstraint($name); + if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) { + $tableName = $this->_driver->quoteIdentifier($schema->name()); + $sql[] = sprintf($sqlPattern, $tableName, $this->constraintSql($schema, $name)); + } + } + + return $sql; + } + + /** + * @inheritDoc + */ + public function dropConstraintSql(TableSchema $schema): array + { + $sqlPattern = 'ALTER TABLE %s DROP CONSTRAINT %s;'; + $sql = []; + + foreach ($schema->constraints() as $name) { + /** @var array $constraint */ + $constraint = $schema->getConstraint($name); + if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) { + $tableName = $this->_driver->quoteIdentifier($schema->name()); + $constraintName = $this->_driver->quoteIdentifier($name); + $sql[] = sprintf($sqlPattern, $tableName, $constraintName); + } + } + + return $sql; + } + + /** + * @inheritDoc + */ + public function indexSql(TableSchema $schema, string $name): string + { + /** @var array $data */ + $data = $schema->getIndex($name); + $columns = array_map( + [$this->_driver, 'quoteIdentifier'], + $data['columns'] + ); + + return sprintf( + 'CREATE INDEX %s ON %s (%s)', + $this->_driver->quoteIdentifier($name), + $this->_driver->quoteIdentifier($schema->name()), + implode(', ', $columns) + ); + } + + /** + * @inheritDoc + */ + public function constraintSql(TableSchema $schema, string $name): string + { + /** @var array $data */ + $data = $schema->getConstraint($name); + $out = 'CONSTRAINT ' . $this->_driver->quoteIdentifier($name); + if ($data['type'] === TableSchema::CONSTRAINT_PRIMARY) { + $out = 'PRIMARY KEY'; + } + if ($data['type'] === TableSchema::CONSTRAINT_UNIQUE) { + $out .= ' UNIQUE'; + } + + return $this->_keySql($out, $data); + } + + /** + * Helper method for generating key SQL snippets. + * + * @param string $prefix The key prefix + * @param array $data Key data. + * @return string + */ + protected function _keySql(string $prefix, array $data): string + { + $columns = array_map( + [$this->_driver, 'quoteIdentifier'], + $data['columns'] + ); + if ($data['type'] === TableSchema::CONSTRAINT_FOREIGN) { + return $prefix . sprintf( + ' FOREIGN KEY (%s) REFERENCES %s (%s) ON UPDATE %s ON DELETE %s', + implode(', ', $columns), + $this->_driver->quoteIdentifier($data['references'][0]), + $this->_convertConstraintColumns($data['references'][1]), + $this->_foreignOnClause($data['update']), + $this->_foreignOnClause($data['delete']) + ); + } + + return $prefix . ' (' . implode(', ', $columns) . ')'; + } + + /** + * @inheritDoc + */ + public function createTableSql(TableSchema $schema, array $columns, array $constraints, array $indexes): array + { + $content = array_merge($columns, $constraints); + $content = implode(",\n", array_filter($content)); + $tableName = $this->_driver->quoteIdentifier($schema->name()); + $out = []; + $out[] = sprintf("CREATE TABLE %s (\n%s\n)", $tableName, $content); + foreach ($indexes as $index) { + $out[] = $index; + } + + return $out; + } + + /** + * @inheritDoc + */ + public function truncateTableSql(TableSchema $schema): array + { + $name = $this->_driver->quoteIdentifier($schema->name()); + $queries = [ + sprintf('DELETE FROM %s', $name), + ]; + + // Restart identity sequences + $pk = $schema->getPrimaryKey(); + if (count($pk) === 1) { + /** @var array $column */ + $column = $schema->getColumn($pk[0]); + if (in_array($column['type'], ['integer', 'biginteger'])) { + $queries[] = sprintf( + "IF EXISTS (SELECT * FROM sys.identity_columns WHERE OBJECT_NAME(OBJECT_ID) = '%s' AND " . + "last_value IS NOT NULL) DBCC CHECKIDENT('%s', RESEED, 0)", + $schema->name(), + $schema->name() + ); + } + } + + return $queries; + } +} + +// phpcs:disable +// Add backwards compatible alias. +class_alias('Cake\Database\Schema\SqlserverSchemaDialect', 'Cake\Database\Schema\SqlserverSchema'); +// phpcs:enable diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchema.php b/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchema.php index 5b0f16fac..4c43492a8 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchema.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchema.php @@ -17,7 +17,7 @@ namespace Cake\Database\Schema; use Cake\Database\Connection; -use Cake\Database\Exception; +use Cake\Database\Exception\DatabaseException; use Cake\Database\TypeFactory; /** @@ -468,7 +468,7 @@ public function addIndex(string $name, $attrs) unset($attrs['references'], $attrs['update'], $attrs['delete']); if (!in_array($attrs['type'], static::$_validIndexTypes, true)) { - throw new Exception(sprintf( + throw new DatabaseException(sprintf( 'Invalid index type "%s" in index "%s" in table "%s".', $attrs['type'], $name, @@ -476,7 +476,7 @@ public function addIndex(string $name, $attrs) )); } if (empty($attrs['columns'])) { - throw new Exception(sprintf( + throw new DatabaseException(sprintf( 'Index "%s" in table "%s" must have at least one column.', $name, $this->_table @@ -492,7 +492,7 @@ public function addIndex(string $name, $attrs) $this->_table, $field ); - throw new Exception($msg); + throw new DatabaseException($msg); } } $this->_indexes[$name] = $attrs; @@ -529,6 +529,8 @@ public function getIndex(string $name): ?array */ public function primaryKey(): array { + deprecationWarning('`TableSchema::primaryKey()` is deprecated. Use `TableSchema::getPrimaryKey()`.'); + return $this->getPrimarykey(); } @@ -557,10 +559,17 @@ public function addConstraint(string $name, $attrs) $attrs = array_intersect_key($attrs, static::$_indexKeys); $attrs += static::$_indexKeys; if (!in_array($attrs['type'], static::$_validConstraintTypes, true)) { - throw new Exception(sprintf('Invalid constraint type "%s" in table "%s".', $attrs['type'], $this->_table)); + throw new DatabaseException(sprintf( + 'Invalid constraint type "%s" in table "%s".', + $attrs['type'], + $this->_table + )); } if (empty($attrs['columns'])) { - throw new Exception(sprintf('Constraints in table "%s" must have at least one column.', $this->_table)); + throw new DatabaseException(sprintf( + 'Constraints in table "%s" must have at least one column.', + $this->_table + )); } $attrs['columns'] = (array)$attrs['columns']; foreach ($attrs['columns'] as $field) { @@ -571,7 +580,7 @@ public function addConstraint(string $name, $attrs) $field, $this->_table ); - throw new Exception($msg); + throw new DatabaseException($msg); } } @@ -635,21 +644,21 @@ public function hasAutoincrement(): bool * * @param array $attrs Attributes to set. * @return array - * @throws \Cake\Database\Exception When foreign key definition is not valid. + * @throws \Cake\Database\Exception\DatabaseException When foreign key definition is not valid. */ protected function _checkForeignKey(array $attrs): array { if (count($attrs['references']) < 2) { - throw new Exception('References must contain a table and column.'); + throw new DatabaseException('References must contain a table and column.'); } if (!in_array($attrs['update'], static::$_validForeignKeyActions)) { - throw new Exception(sprintf( + throw new DatabaseException(sprintf( 'Update action is invalid. Must be one of %s', implode(',', static::$_validForeignKeyActions) )); } if (!in_array($attrs['delete'], static::$_validForeignKeyActions)) { - throw new Exception(sprintf( + throw new DatabaseException(sprintf( 'Delete action is invalid. Must be one of %s', implode(',', static::$_validForeignKeyActions) )); @@ -701,7 +710,7 @@ public function getOptions(): array */ public function setTemporary(bool $temporary) { - $this->_temporary = (bool)$temporary; + $this->_temporary = $temporary; return $this; } diff --git a/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchemaInterface.php b/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchemaInterface.php index fec79dbc4..7aa2f8d12 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchemaInterface.php +++ b/app/vendor/cakephp/cakephp/src/Database/Schema/TableSchemaInterface.php @@ -215,7 +215,7 @@ public function getPrimaryKey(): array; * @param array|string $attrs The attributes for the index. * If string it will be used as `type`. * @return $this - * @throws \Cake\Database\Exception + * @throws \Cake\Database\Exception\DatabaseException */ public function addIndex(string $name, $attrs); @@ -254,7 +254,7 @@ public function indexes(): array; * @param array|string $attrs The attributes for the constraint. * If string it will be used as `type`. * @return $this - * @throws \Cake\Database\Exception + * @throws \Cake\Database\Exception\DatabaseException */ public function addConstraint(string $name, $attrs); diff --git a/app/vendor/cakephp/cakephp/src/Database/SqlDialectTrait.php b/app/vendor/cakephp/cakephp/src/Database/SqlDialectTrait.php index f5800a667..38ef7bc08 100644 --- a/app/vendor/cakephp/cakephp/src/Database/SqlDialectTrait.php +++ b/app/vendor/cakephp/cakephp/src/Database/SqlDialectTrait.php @@ -1,303 +1,5 @@ _startQuote . $identifier . $this->_endQuote; - } - - // string.string - if (preg_match('/^[\w-]+\.[^ \*]*$/u', $identifier)) { - $items = explode('.', $identifier); - - return $this->_startQuote . implode($this->_endQuote . '.' . $this->_startQuote, $items) . $this->_endQuote; - } - - // string.* - if (preg_match('/^[\w-]+\.\*$/u', $identifier)) { - return $this->_startQuote . str_replace('.*', $this->_endQuote . '.*', $identifier); - } - - // Functions - if (preg_match('/^([\w-]+)\((.*)\)$/', $identifier, $matches)) { - return $matches[1] . '(' . $this->quoteIdentifier($matches[2]) . ')'; - } - - // Alias.field AS thing - if (preg_match('/^([\w-]+(\.[\w\s-]+|\(.*\))*)\s+AS\s*([\w-]+)$/ui', $identifier, $matches)) { - return $this->quoteIdentifier($matches[1]) . ' AS ' . $this->quoteIdentifier($matches[3]); - } - - // string.string with spaces - if (preg_match('/^([\w-]+\.[\w][\w\s\-]*[\w])(.*)/u', $identifier, $matches)) { - $items = explode('.', $matches[1]); - $field = implode($this->_endQuote . '.' . $this->_startQuote, $items); - - return $this->_startQuote . $field . $this->_endQuote . $matches[2]; - } - - if (preg_match('/^[\w_\s-]*[\w_-]+/u', $identifier)) { - return $this->_startQuote . $identifier . $this->_endQuote; - } - - return $identifier; - } - - /** - * Returns a callable function that will be used to transform a passed Query object. - * This function, in turn, will return an instance of a Query object that has been - * transformed to accommodate any specificities of the SQL dialect in use. - * - * @param string $type the type of query to be transformed - * (select, insert, update, delete) - * @return \Closure - */ - public function queryTranslator(string $type): Closure - { - return function ($query) use ($type) { - if ($this->isAutoQuotingEnabled()) { - $query = (new IdentifierQuoter($this))->quote($query); - } - - /** @var \Cake\ORM\Query $query */ - $query = $this->{'_' . $type . 'QueryTranslator'}($query); - $translators = $this->_expressionTranslators(); - if (!$translators) { - return $query; - } - - $query->traverseExpressions(function ($expression) use ($translators, $query): void { - foreach ($translators as $class => $method) { - if ($expression instanceof $class) { - $this->{$method}($expression, $query); - } - } - }); - - return $query; - }; - } - - /** - * Returns an associative array of methods that will transform Expression - * objects to conform with the specific SQL dialect. Keys are class names - * and values a method in this class. - * - * @return array - */ - protected function _expressionTranslators(): array - { - return []; - } - - /** - * Apply translation steps to select queries. - * - * @param \Cake\Database\Query $query The query to translate - * @return \Cake\Database\Query The modified query - */ - protected function _selectQueryTranslator(Query $query): Query - { - return $this->_transformDistinct($query); - } - - /** - * Returns the passed query after rewriting the DISTINCT clause, so that drivers - * that do not support the "ON" part can provide the actual way it should be done - * - * @param \Cake\Database\Query $query The query to be transformed - * @return \Cake\Database\Query - */ - protected function _transformDistinct(Query $query): Query - { - if (is_array($query->clause('distinct'))) { - $query->group($query->clause('distinct'), true); - $query->distinct(false); - } - - return $query; - } - - /** - * Apply translation steps to delete queries. - * - * Chops out aliases on delete query conditions as most database dialects do not - * support aliases in delete queries. This also removes aliases - * in table names as they frequently don't work either. - * - * We are intentionally not supporting deletes with joins as they have even poorer support. - * - * @param \Cake\Database\Query $query The query to translate - * @return \Cake\Database\Query The modified query - */ - protected function _deleteQueryTranslator(Query $query): Query - { - $hadAlias = false; - $tables = []; - foreach ($query->clause('from') as $alias => $table) { - if (is_string($alias)) { - $hadAlias = true; - } - $tables[] = $table; - } - if ($hadAlias) { - $query->from($tables, true); - } - - if (!$hadAlias) { - return $query; - } - - return $this->_removeAliasesFromConditions($query); - } - - /** - * Apply translation steps to update queries. - * - * Chops out aliases on update query conditions as not all database dialects do support - * aliases in update queries. - * - * Just like for delete queries, joins are currently not supported for update queries. - * - * @param \Cake\Database\Query $query The query to translate - * @return \Cake\Database\Query The modified query - */ - protected function _updateQueryTranslator(Query $query): Query - { - return $this->_removeAliasesFromConditions($query); - } - - /** - * Removes aliases from the `WHERE` clause of a query. - * - * @param \Cake\Database\Query $query The query to process. - * @return \Cake\Database\Query The modified query. - * @throws \RuntimeException In case the processed query contains any joins, as removing - * aliases from the conditions can break references to the joined tables. - */ - protected function _removeAliasesFromConditions(Query $query): Query - { - if ($query->clause('join')) { - throw new RuntimeException( - 'Aliases are being removed from conditions for UPDATE/DELETE queries, ' . - 'this can break references to joined tables.' - ); - } - - $conditions = $query->clause('where'); - if ($conditions) { - $conditions->traverse(function ($expression) { - if ($expression instanceof Comparison) { - $field = $expression->getField(); - if ( - is_string($field) && - strpos($field, '.') !== false - ) { - [, $unaliasedField] = explode('.', $field, 2); - $expression->setField($unaliasedField); - } - - return $expression; - } - - if ($expression instanceof IdentifierExpression) { - $identifier = $expression->getIdentifier(); - if (strpos($identifier, '.') !== false) { - [, $unaliasedIdentifier] = explode('.', $identifier, 2); - $expression->setIdentifier($unaliasedIdentifier); - } - - return $expression; - } - - return $expression; - }); - } - - return $query; - } - - /** - * Apply translation steps to insert queries. - * - * @param \Cake\Database\Query $query The query to translate - * @return \Cake\Database\Query The modified query - */ - protected function _insertQueryTranslator(Query $query): Query - { - return $query; - } - - /** - * Returns a SQL snippet for creating a new transaction savepoint - * - * @param string|int $name save point name - * @return string - */ - public function savePointSQL($name): string - { - return 'SAVEPOINT LEVEL' . $name; - } - - /** - * Returns a SQL snippet for releasing a previously created save point - * - * @param string|int $name save point name - * @return string - */ - public function releaseSavePointSQL($name): string - { - return 'RELEASE SAVEPOINT LEVEL' . $name; - } - - /** - * Returns a SQL snippet for rollbacking a previously created save point - * - * @param string|int $name save point name - * @return string - */ - public function rollbackSavePointSQL($name): string - { - return 'ROLLBACK TO SAVEPOINT LEVEL' . $name; - } -} +// @deprecated 4.1.0 Add backwards compatible alias. +class_alias('Cake\Database\Driver\SqlDialectTrait', 'Cake\Database\SqlDialectTrait'); diff --git a/app/vendor/cakephp/cakephp/src/Database/SqlserverCompiler.php b/app/vendor/cakephp/cakephp/src/Database/SqlserverCompiler.php index 7f1cfc19f..cc6e14238 100644 --- a/app/vendor/cakephp/cakephp/src/Database/SqlserverCompiler.php +++ b/app/vendor/cakephp/cakephp/src/Database/SqlserverCompiler.php @@ -16,6 +16,9 @@ */ namespace Cake\Database; +use Cake\Database\Exception\DatabaseException; +use Cake\Database\Expression\FunctionExpression; + /** * Responsible for compiling a Query object into its SQL representation * for SQL Server @@ -37,8 +40,7 @@ class SqlserverCompiler extends QueryCompiler protected $_templates = [ 'delete' => 'DELETE', 'where' => ' WHERE %s', - 'group' => ' GROUP BY %s ', - 'having' => ' HAVING %s ', + 'group' => ' GROUP BY %s', 'order' => ' %s', 'offset' => ' OFFSET %s ROWS', 'epilog' => ' %s', @@ -48,10 +50,30 @@ class SqlserverCompiler extends QueryCompiler * @inheritDoc */ protected $_selectParts = [ - 'select', 'from', 'join', 'where', 'group', 'having', 'order', 'offset', - 'limit', 'union', 'epilog', + 'with', 'select', 'from', 'join', 'where', 'group', 'having', 'window', 'order', + 'offset', 'limit', 'union', 'epilog', ]; + /** + * Helper function used to build the string representation of a `WITH` clause, + * it constructs the CTE definitions list without generating the `RECURSIVE` + * keyword that is neither required nor valid. + * + * @param array $parts List of CTEs to be transformed to string + * @param \Cake\Database\Query $query The query that is being compiled + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder + * @return string + */ + protected function _buildWithPart(array $parts, Query $query, ValueBinder $binder): string + { + $expressions = []; + foreach ($parts as $cte) { + $expressions[] = $cte->sql($binder); + } + + return sprintf('WITH %s ', implode(', ', $expressions)); + } + /** * Generates the INSERT part of a SQL query * @@ -61,14 +83,20 @@ class SqlserverCompiler extends QueryCompiler * * @param array $parts The parts to build * @param \Cake\Database\Query $query The query that is being compiled - * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder * @return string */ - protected function _buildInsertPart(array $parts, Query $query, ValueBinder $generator): string + protected function _buildInsertPart(array $parts, Query $query, ValueBinder $binder): string { + if (!isset($parts[0])) { + throw new DatabaseException( + 'Could not compile insert query. No table was specified. ' . + 'Use `into()` to define a table.' + ); + } $table = $parts[0]; - $columns = $this->_stringifyExpressions($parts[1], $generator); - $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $generator); + $columns = $this->_stringifyExpressions($parts[1], $binder); + $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $binder); return sprintf( 'INSERT%s INTO %s (%s) OUTPUT INSERTED.*', @@ -93,4 +121,47 @@ protected function _buildLimitPart(int $limit, Query $query): string return sprintf(' FETCH FIRST %d ROWS ONLY', $limit); } + + /** + * Helper function used to build the string representation of a HAVING clause, + * it constructs the field list taking care of aliasing and + * converting expression objects to string. + * + * @param array $parts list of fields to be transformed to string + * @param \Cake\Database\Query $query The query that is being compiled + * @param \Cake\Database\ValueBinder $binder Value binder used to generate parameter placeholder + * @return string + */ + protected function _buildHavingPart($parts, $query, $binder) + { + $selectParts = $query->clause('select'); + + foreach ($selectParts as $selectKey => $selectPart) { + if (!$selectPart instanceof FunctionExpression) { + continue; + } + foreach ($parts as $k => $p) { + if (!is_string($p)) { + continue; + } + preg_match_all( + '/\b' . trim($selectKey, '[]') . '\b/i', + $p, + $matches + ); + + if (empty($matches[0])) { + continue; + } + + $parts[$k] = preg_replace( + ['/\[|\]/', '/\b' . trim($selectKey, '[]') . '\b/i'], + ['', $selectPart->sql($binder)], + $p + ); + } + } + + return sprintf(' HAVING %s', implode(', ', $parts)); + } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Statement/BufferResultsTrait.php b/app/vendor/cakephp/cakephp/src/Database/Statement/BufferResultsTrait.php index aa8554ff5..e4e690f33 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Statement/BufferResultsTrait.php +++ b/app/vendor/cakephp/cakephp/src/Database/Statement/BufferResultsTrait.php @@ -38,7 +38,7 @@ trait BufferResultsTrait */ public function bufferResults(bool $buffer) { - $this->_bufferResults = (bool)$buffer; + $this->_bufferResults = $buffer; return $this; } diff --git a/app/vendor/cakephp/cakephp/src/Database/Statement/MysqlStatement.php b/app/vendor/cakephp/cakephp/src/Database/Statement/MysqlStatement.php index 3efa15e8f..2ad4ec008 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Statement/MysqlStatement.php +++ b/app/vendor/cakephp/cakephp/src/Database/Statement/MysqlStatement.php @@ -19,7 +19,7 @@ use PDO; /** - * Statement class meant to be used by a Mysql PDO driver + * Statement class meant to be used by a MySQL PDO driver * * @internal */ diff --git a/app/vendor/cakephp/cakephp/src/Database/Statement/PDOStatement.php b/app/vendor/cakephp/cakephp/src/Database/Statement/PDOStatement.php index 75e9abd7a..fbc8349da 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Statement/PDOStatement.php +++ b/app/vendor/cakephp/cakephp/src/Database/Statement/PDOStatement.php @@ -16,7 +16,7 @@ */ namespace Cake\Database\Statement; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Database\DriverInterface; use PDO; use PDOStatement as Statement; @@ -112,7 +112,7 @@ public function fetch($type = parent::FETCH_TYPE_NUM) } if (!is_int($type)) { - throw new Exception(sprintf( + throw new CakeException(sprintf( 'Fetch type for PDOStatement must be an integer, found `%s` instead', getTypeName($type) )); @@ -149,7 +149,7 @@ public function fetchAll($type = parent::FETCH_TYPE_NUM) } if (!is_int($type)) { - throw new Exception(sprintf( + throw new CakeException(sprintf( 'Fetch type for PDOStatement must be an integer, found `%s` instead', getTypeName($type) )); diff --git a/app/vendor/cakephp/cakephp/src/Database/Statement/StatementDecorator.php b/app/vendor/cakephp/cakephp/src/Database/Statement/StatementDecorator.php index 723034b62..b7eb3c36a 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Statement/StatementDecorator.php +++ b/app/vendor/cakephp/cakephp/src/Database/Statement/StatementDecorator.php @@ -326,6 +326,7 @@ public function bind(array $params, array $types): void /** @psalm-suppress InvalidOperand */ $index += $offset; } + /** @psalm-suppress InvalidScalarArgument */ $this->bindValue($index, $value, $type); } } diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/BinaryType.php b/app/vendor/cakephp/cakephp/src/Database/Type/BinaryType.php index 7a669b4ec..059e24123 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/BinaryType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/BinaryType.php @@ -16,7 +16,7 @@ */ namespace Cake\Database\Type; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Database\DriverInterface; use PDO; @@ -48,7 +48,7 @@ public function toDatabase($value, DriverInterface $driver) * @param mixed $value The value to convert. * @param \Cake\Database\DriverInterface $driver The driver instance to convert with. * @return resource|null - * @throws \Cake\Core\Exception\Exception + * @throws \Cake\Core\Exception\CakeException */ public function toPHP($value, DriverInterface $driver) { @@ -61,7 +61,7 @@ public function toPHP($value, DriverInterface $driver) if (is_resource($value)) { return $value; } - throw new Exception(sprintf('Unable to convert %s into binary.', gettype($value))); + throw new CakeException(sprintf('Unable to convert %s into binary.', gettype($value))); } /** diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/BinaryUuidType.php b/app/vendor/cakephp/cakephp/src/Database/Type/BinaryUuidType.php index 090297461..66e2b192e 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/BinaryUuidType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/BinaryUuidType.php @@ -16,7 +16,7 @@ */ namespace Cake\Database\Type; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Database\DriverInterface; use Cake\Utility\Text; use PDO; @@ -36,15 +36,20 @@ class BinaryUuidType extends BaseType * * @param mixed $value The value to convert. * @param \Cake\Database\DriverInterface $driver The driver instance to convert with. - * @return string|resource + * @return string|resource|null */ public function toDatabase($value, DriverInterface $driver) { - if (is_string($value)) { - return $this->convertStringToBinaryUuid($value); + if (!is_string($value)) { + return $value; } - return $value; + $length = strlen($value); + if ($length !== 36 && $length !== 32) { + return null; + } + + return $this->convertStringToBinaryUuid($value); } /** @@ -63,7 +68,7 @@ public function newId(): string * @param mixed $value The value to convert. * @param \Cake\Database\DriverInterface $driver The driver instance to convert with. * @return resource|string|null - * @throws \Cake\Core\Exception\Exception + * @throws \Cake\Core\Exception\CakeException */ public function toPHP($value, DriverInterface $driver) { @@ -77,7 +82,7 @@ public function toPHP($value, DriverInterface $driver) return $value; } - throw new Exception(sprintf('Unable to convert %s into binary uuid.', gettype($value))); + throw new CakeException(sprintf('Unable to convert %s into binary uuid.', gettype($value))); } /** @@ -126,7 +131,7 @@ protected function convertBinaryUuidToString($binary): string } /** - * Converts a string uuid to a binary representation + * Converts a string UUID (36 or 32 char) to a binary representation. * * @param string $string The value to convert. * @return string Converted value. diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/DateTimeType.php b/app/vendor/cakephp/cakephp/src/Database/Type/DateTimeType.php index d78a5a01a..cd3802599 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/DateTimeType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/DateTimeType.php @@ -159,9 +159,12 @@ public function toDatabase($value, DriverInterface $driver): ?string * * @param string|\DateTimeZone|null $timezone Database timezone. * @return $this + * @deprecated 4.1.0 Use {@link setDatabaseTimezone()} instead. */ public function setTimezone($timezone) { + deprecationWarning('DateTimeType::setTimezone() is deprecated. Use setDatabaseTimezone() instead.'); + return $this->setDatabaseTimezone($timezone); } @@ -448,9 +451,9 @@ public function useMutable() * @param string $value The value to parse and convert to an object. * @return \Cake\I18n\I18nDateTimeInterface|null */ - protected function _parseLocaleValue(string $value) + protected function _parseLocaleValue(string $value): ?I18nDateTimeInterface { - /** @var \Cake\I18n\I18nDateTimeInterface $class */ + /** @psalm-var class-string<\Cake\I18n\I18nDateTimeInterface> $class */ $class = $this->_className; return $class::parseDateTime($value, $this->_localeMarshalFormat); @@ -463,9 +466,8 @@ protected function _parseLocaleValue(string $value) * @param string $value The value to parse and convert to an object. * @return \DateTimeInterface|null */ - protected function _parseValue(string $value) + protected function _parseValue(string $value): ?DateTimeInterface { - /** @var \DateTime|\DateTimeImmutable $class */ $class = $this->_className; foreach ($this->_marshalFormats as $format) { diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/DateType.php b/app/vendor/cakephp/cakephp/src/Database/Type/DateType.php index 7bbc02184..5df8416da 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/DateType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/DateType.php @@ -18,6 +18,7 @@ use Cake\I18n\Date; use Cake\I18n\FrozenDate; +use Cake\I18n\I18nDateTimeInterface; use DateTime; use DateTimeImmutable; use DateTimeInterface; @@ -80,8 +81,10 @@ public function useMutable() public function marshal($value): ?DateTimeInterface { $date = parent::marshal($value); - if ($date instanceof DateTime) { - $date->setTime(0, 0, 0); + /** @psalm-var \DateTime|\DateTimeImmutable|null $date */ + if ($date && !$date instanceof I18nDateTimeInterface) { + // Clear time manually when I18n types aren't available and raw DateTime used + $date = $date->setTime(0, 0, 0); } return $date; @@ -90,9 +93,9 @@ public function marshal($value): ?DateTimeInterface /** * @inheritDoc */ - protected function _parseLocaleValue(string $value) + protected function _parseLocaleValue(string $value): ?I18nDateTimeInterface { - /** @var \Cake\I18n\I18nDateTimeInterface $class */ + /** @psalm-var class-string<\Cake\I18n\I18nDateTimeInterface> $class */ $class = $this->_className; return $class::parseDate($value, $this->_localeMarshalFormat); diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/ExpressionTypeCasterTrait.php b/app/vendor/cakephp/cakephp/src/Database/Type/ExpressionTypeCasterTrait.php index 2743b42f8..597c18ad6 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/ExpressionTypeCasterTrait.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/ExpressionTypeCasterTrait.php @@ -29,7 +29,7 @@ trait ExpressionTypeCasterTrait * if the type class implements the ExpressionTypeInterface. Otherwise, * returns the value unmodified. * - * @param mixed $value The value to converto to ExpressionInterface + * @param mixed $value The value to convert to ExpressionInterface * @param string|null $type The type name * @return mixed */ @@ -49,7 +49,7 @@ protected function _castToExpression($value, ?string $type = null) $multi = $type !== $baseType; if ($multi) { - /** @psalm-suppress InvalidArgument */ + /** @psalm-var \Cake\Database\Type\ExpressionTypeInterface $converter */ return array_map([$converter, 'toExpression'], $value); } diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/FloatType.php b/app/vendor/cakephp/cakephp/src/Database/Type/FloatType.php index f1cbf460a..0e257b495 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/FloatType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/FloatType.php @@ -65,7 +65,7 @@ public function toDatabase($value, DriverInterface $driver): ?float * @param mixed $value The value to convert. * @param \Cake\Database\DriverInterface $driver The driver instance to convert with. * @return float|null - * @throws \Cake\Core\Exception\Exception + * @throws \Cake\Core\Exception\CakeException */ public function toPHP($value, DriverInterface $driver): ?float { @@ -93,7 +93,7 @@ public function manyToPHP(array $values, array $fields, DriverInterface $driver) } /** - * Get the correct PDO binding type for integer data. + * Get the correct PDO binding type for float data. * * @param mixed $value The value being bound. * @param \Cake\Database\DriverInterface $driver The driver. diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/JsonType.php b/app/vendor/cakephp/cakephp/src/Database/Type/JsonType.php index 0969c5cfb..bd9544926 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/JsonType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/JsonType.php @@ -21,9 +21,9 @@ use PDO; /** - * Json type converter. + * JSON type converter. * - * Use to convert json data between PHP and the database types. + * Use to convert JSON data between PHP and the database types. */ class JsonType extends BaseType implements BatchCastingInterface { diff --git a/app/vendor/cakephp/cakephp/src/Database/Type/TimeType.php b/app/vendor/cakephp/cakephp/src/Database/Type/TimeType.php index 65d78f1f6..4c021e214 100644 --- a/app/vendor/cakephp/cakephp/src/Database/Type/TimeType.php +++ b/app/vendor/cakephp/cakephp/src/Database/Type/TimeType.php @@ -16,6 +16,8 @@ */ namespace Cake\Database\Type; +use Cake\I18n\I18nDateTimeInterface; + /** * Time type converter. * @@ -33,14 +35,15 @@ class TimeType extends DateTimeType */ protected $_marshalFormats = [ 'H:i:s', + 'H:i', ]; /** * @inheritDoc */ - protected function _parseLocaleValue(string $value) + protected function _parseLocaleValue(string $value): ?I18nDateTimeInterface { - /** @var \Cake\I18n\I18nDateTimeInterface $class */ + /** @psalm-var class-string<\Cake\I18n\I18nDateTimeInterface> $class */ $class = $this->_className; /** @psalm-suppress PossiblyInvalidArgument */ diff --git a/app/vendor/cakephp/cakephp/src/Database/TypeMapTrait.php b/app/vendor/cakephp/cakephp/src/Database/TypeMapTrait.php index 7e9829e2e..e893c5930 100644 --- a/app/vendor/cakephp/cakephp/src/Database/TypeMapTrait.php +++ b/app/vendor/cakephp/cakephp/src/Database/TypeMapTrait.php @@ -25,7 +25,7 @@ trait TypeMapTrait { /** - * @var \Cake\Database\TypeMap + * @var \Cake\Database\TypeMap|null */ protected $_typeMap; diff --git a/app/vendor/cakephp/cakephp/src/Datasource/ConnectionInterface.php b/app/vendor/cakephp/cakephp/src/Datasource/ConnectionInterface.php index 9b9ba8eaa..fcf96b65d 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/ConnectionInterface.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/ConnectionInterface.php @@ -95,12 +95,12 @@ public function config(): array; * }); * ``` * - * @param callable $transaction The callback to execute within a transaction. + * @param callable $callback The callback to execute within a transaction. * @return mixed The return value of the callback. * @throws \Exception Will re-throw any exception raised in $callback after * rolling back the transaction. */ - public function transactional(callable $transaction); + public function transactional(callable $callback); /** * Run an operation with constraints disabled. @@ -115,20 +115,20 @@ public function transactional(callable $transaction); * }); * ``` * - * @param callable $operation The callback to execute within a transaction. + * @param callable $callback The callback to execute within a transaction. * @return mixed The return value of the callback. * @throws \Exception Will re-throw any exception raised in $callback after * rolling back the transaction. */ - public function disableConstraints(callable $operation); + public function disableConstraints(callable $callback); /** * Enable/disable query logging * - * @param bool $value Enable/disable query logging + * @param bool $enable Enable/disable query logging * @return $this */ - public function enableQueryLogging(bool $value = true); + public function enableQueryLogging(bool $enable = true); /** * Disable query logging diff --git a/app/vendor/cakephp/cakephp/src/Datasource/ConnectionManager.php b/app/vendor/cakephp/cakephp/src/Datasource/ConnectionManager.php index eb908be51..44bf79ec3 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/ConnectionManager.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/ConnectionManager.php @@ -75,7 +75,7 @@ class ConnectionManager * @param string|array $key The name of the connection config, or an array of multiple configs. * @param array|null $config An array of name => config data for adapter. * @return void - * @throws \Cake\Core\Exception\Exception When trying to modify an existing config. + * @throws \Cake\Core\Exception\CakeException When trying to modify an existing config. * @see \Cake\Core\StaticConfigTrait::config() */ public static function setConfig($key, $config = null): void diff --git a/app/vendor/cakephp/cakephp/src/Datasource/ConnectionRegistry.php b/app/vendor/cakephp/cakephp/src/Datasource/ConnectionRegistry.php index 01b7b5c56..9bedc0166 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/ConnectionRegistry.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/ConnectionRegistry.php @@ -70,10 +70,10 @@ protected function _throwMissingClassError(string $class, ?string $plugin): void * * @param string|\Cake\Datasource\ConnectionInterface|callable $class The classname or object to make. * @param string $alias The alias of the object. - * @param array $settings An array of settings to use for the datasource. + * @param array $config An array of settings to use for the datasource. * @return \Cake\Datasource\ConnectionInterface A connection with the correct settings. */ - protected function _create($class, string $alias, array $settings) + protected function _create($class, string $alias, array $config) { if (is_callable($class)) { return $class($alias); @@ -83,11 +83,10 @@ protected function _create($class, string $alias, array $settings) return $class; } - unset($settings['className']); + unset($config['className']); - // phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.InvalidFormat /** @var \Cake\Datasource\ConnectionInterface */ - return new $class($settings); + return new $class($config); } /** diff --git a/app/vendor/cakephp/cakephp/src/Datasource/EntityInterface.php b/app/vendor/cakephp/cakephp/src/Datasource/EntityInterface.php index 53bca0618..146ad0dcd 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/EntityInterface.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/EntityInterface.php @@ -24,6 +24,7 @@ * comply with. * * @property mixed $id Alias for commonly used primary key. + * @method bool[] getAccessible() Accessible configuration for this entity. */ interface EntityInterface extends ArrayAccess, JsonSerializable { diff --git a/app/vendor/cakephp/cakephp/src/Datasource/EntityTrait.php b/app/vendor/cakephp/cakephp/src/Datasource/EntityTrait.php index 08802956f..7c54bce0e 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/EntityTrait.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/EntityTrait.php @@ -54,7 +54,7 @@ trait EntityTrait /** * List of computed or virtual fields that **should** be included in JSON or array * representations of this Entity. If a field is present in both _hidden and _virtual - * the field will **not** be in the array/json versions of the entity. + * the field will **not** be in the array/JSON versions of the entity. * * @var string[] */ @@ -104,10 +104,10 @@ trait EntityTrait * means no fields are accessible * * The special field '\*' can also be mapped, meaning that any other field - * not defined in the map will take its value. For example, `'\*' => true` + * not defined in the map will take its value. For example, `'*' => true` * means that any field not defined in the map will be accessible by default * - * @var array + * @var bool[] */ protected $_accessible = ['*' => true]; @@ -235,6 +235,7 @@ public function set($field, $value = null, array $options = []) $options += ['setter' => true, 'guard' => $guard]; foreach ($field as $name => $value) { + $name = (string)$name; if ($options['guard'] === true && !$this->isAccessible($name)) { continue; } @@ -393,7 +394,7 @@ public function isEmpty(string $field): bool empty($value) || ( is_string($value) && - empty($value) + $value === '' ) ) ) { @@ -456,6 +457,8 @@ public function unset($field) */ public function unsetProperty($field) { + deprecationWarning('EntityTrait::unsetProperty() is deprecated. Use unset() instead.'); + return $this->unset($field); } @@ -1149,20 +1152,31 @@ public function setAccess($field, bool $set) { if ($field === '*') { $this->_accessible = array_map(function ($p) use ($set) { - return (bool)$set; + return $set; }, $this->_accessible); - $this->_accessible['*'] = (bool)$set; + $this->_accessible['*'] = $set; return $this; } foreach ((array)$field as $prop) { - $this->_accessible[$prop] = (bool)$set; + $this->_accessible[$prop] = $set; } return $this; } + /** + * Returns the raw accessible configuration for this entity. + * The `*` wildcard refers to all fields. + * + * @return bool[] + */ + public function getAccessible(): array + { + return $this->_accessible; + } + /** * Checks if a field is accessible * @@ -1177,8 +1191,7 @@ public function setAccess($field, bool $set) */ public function isAccessible(string $field): bool { - $value = $this->_accessible[$field] ?? - null; + $value = $this->_accessible[$field] ?? null; return ($value === null && !empty($this->_accessible['*'])) || $value; } diff --git a/app/vendor/cakephp/cakephp/src/Datasource/Exception/InvalidPrimaryKeyException.php b/app/vendor/cakephp/cakephp/src/Datasource/Exception/InvalidPrimaryKeyException.php index 5263361f2..ac7a72b1b 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/Exception/InvalidPrimaryKeyException.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/Exception/InvalidPrimaryKeyException.php @@ -16,15 +16,11 @@ */ namespace Cake\Datasource\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Exception raised when the provided primary key does not match the table primary key */ -class InvalidPrimaryKeyException extends Exception +class InvalidPrimaryKeyException extends CakeException { - /** - * @inheritDoc - */ - protected $_defaultCode = 404; } diff --git a/app/vendor/cakephp/cakephp/src/Datasource/Exception/MissingDatasourceConfigException.php b/app/vendor/cakephp/cakephp/src/Datasource/Exception/MissingDatasourceConfigException.php index 8ac697858..aa9a7111d 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/Exception/MissingDatasourceConfigException.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/Exception/MissingDatasourceConfigException.php @@ -14,12 +14,12 @@ */ namespace Cake\Datasource\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Exception class to be thrown when a datasource configuration is not found */ -class MissingDatasourceConfigException extends Exception +class MissingDatasourceConfigException extends CakeException { /** * @var string diff --git a/app/vendor/cakephp/cakephp/src/Datasource/Exception/MissingDatasourceException.php b/app/vendor/cakephp/cakephp/src/Datasource/Exception/MissingDatasourceException.php index 65ab48f3e..decb529ed 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/Exception/MissingDatasourceException.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/Exception/MissingDatasourceException.php @@ -14,12 +14,12 @@ */ namespace Cake\Datasource\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Used when a datasource cannot be found. */ -class MissingDatasourceException extends Exception +class MissingDatasourceException extends CakeException { /** * @var string diff --git a/app/vendor/cakephp/cakephp/src/Datasource/Exception/MissingModelException.php b/app/vendor/cakephp/cakephp/src/Datasource/Exception/MissingModelException.php index 71fa7293a..0e13432d1 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/Exception/MissingModelException.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/Exception/MissingModelException.php @@ -16,12 +16,12 @@ */ namespace Cake\Datasource\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Used when a model cannot be found. */ -class MissingModelException extends Exception +class MissingModelException extends CakeException { /** * @var string diff --git a/app/vendor/cakephp/cakephp/src/Datasource/Exception/PageOutOfBoundsException.php b/app/vendor/cakephp/cakephp/src/Datasource/Exception/PageOutOfBoundsException.php index 7be153737..787365b14 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/Exception/PageOutOfBoundsException.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/Exception/PageOutOfBoundsException.php @@ -14,20 +14,15 @@ */ namespace Cake\Datasource\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Exception raised when requested page number does not exist. */ -class PageOutOfBoundsException extends Exception +class PageOutOfBoundsException extends CakeException { /** * @inheritDoc */ protected $_messageTemplate = 'Page number %s could not be found.'; - - /** - * @inheritDoc - */ - protected $_defaultCode = 404; } diff --git a/app/vendor/cakephp/cakephp/src/Datasource/Exception/RecordNotFoundException.php b/app/vendor/cakephp/cakephp/src/Datasource/Exception/RecordNotFoundException.php index 50416b72c..337b92592 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/Exception/RecordNotFoundException.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/Exception/RecordNotFoundException.php @@ -16,15 +16,11 @@ */ namespace Cake\Datasource\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Exception raised when a particular record was not found */ -class RecordNotFoundException extends Exception +class RecordNotFoundException extends CakeException { - /** - * @inheritDoc - */ - protected $_defaultCode = 404; } diff --git a/app/vendor/cakephp/cakephp/src/Datasource/FactoryLocator.php b/app/vendor/cakephp/cakephp/src/Datasource/FactoryLocator.php index cdf1eac99..f410e88ab 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/FactoryLocator.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/FactoryLocator.php @@ -16,7 +16,8 @@ */ namespace Cake\Datasource; -use Cake\ORM\TableRegistry; +use Cake\Datasource\Locator\LocatorInterface; +use Cake\ORM\Locator\TableLocator; use InvalidArgumentException; /** @@ -27,7 +28,7 @@ class FactoryLocator /** * A list of model factory functions. * - * @var callable[] + * @var (callable|\Cake\Datasource\Locator\LocatorInterface)[] */ protected static $_modelFactories = []; @@ -35,11 +36,19 @@ class FactoryLocator * Register a callable to generate repositories of a given type. * * @param string $type The name of the repository type the factory function is for. - * @param callable $factory The factory function used to create instances. + * @param callable|\Cake\Datasource\Locator\LocatorInterface $factory The factory function used to create instances. * @return void */ - public static function add(string $type, callable $factory): void + public static function add(string $type, $factory): void { + if (!$factory instanceof LocatorInterface && !is_callable($factory)) { + throw new InvalidArgumentException(sprintf( + '`$factory` must be an instance of Cake\Datasource\Locator\LocatorInterface or a callable.' + . ' Got type `%s` instead.', + getTypeName($factory) + )); + } + static::$_modelFactories[$type] = $factory; } @@ -59,12 +68,12 @@ public static function drop(string $type): void * * @param string $type The repository type to get the factory for. * @throws \InvalidArgumentException If the specified repository type has no factory. - * @return callable The factory for the repository type. + * @return callable|\Cake\Datasource\Locator\LocatorInterface The factory for the repository type. */ - public static function get(string $type): callable + public static function get(string $type) { if (!isset(static::$_modelFactories['Table'])) { - static::$_modelFactories['Table'] = [TableRegistry::getTableLocator(), 'get']; + static::$_modelFactories['Table'] = new TableLocator(); } if (!isset(static::$_modelFactories[$type])) { diff --git a/app/vendor/cakephp/cakephp/src/Datasource/FixtureInterface.php b/app/vendor/cakephp/cakephp/src/Datasource/FixtureInterface.php index d0671a2fc..64c28e4fc 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/FixtureInterface.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/FixtureInterface.php @@ -24,38 +24,38 @@ interface FixtureInterface /** * Create the fixture schema/mapping/definition * - * @param \Cake\Datasource\ConnectionInterface $db An instance of the connection the fixture should be created on. + * @param \Cake\Datasource\ConnectionInterface $connection An instance of the connection the fixture should be created on. * @return bool True on success, false on failure. */ - public function create(ConnectionInterface $db): bool; + public function create(ConnectionInterface $connection): bool; /** * Run after all tests executed, should remove the table/collection from the connection. * - * @param \Cake\Datasource\ConnectionInterface $db An instance of the connection the fixture should be removed from. + * @param \Cake\Datasource\ConnectionInterface $connection An instance of the connection the fixture should be removed from. * @return bool True on success, false on failure. */ - public function drop(ConnectionInterface $db): bool; + public function drop(ConnectionInterface $connection): bool; /** * Run before each test is executed. * * Should insert all the records into the test database. * - * @param \Cake\Datasource\ConnectionInterface $db An instance of the connection + * @param \Cake\Datasource\ConnectionInterface $connection An instance of the connection * into which the records will be inserted. * @return \Cake\Database\StatementInterface|bool on success or if there are no records to insert, * or false on failure. */ - public function insert(ConnectionInterface $db); + public function insert(ConnectionInterface $connection); /** * Truncates the current fixture. * - * @param \Cake\Datasource\ConnectionInterface $db A reference to a db instance + * @param \Cake\Datasource\ConnectionInterface $connection A reference to a db instance * @return bool */ - public function truncate(ConnectionInterface $db): bool; + public function truncate(ConnectionInterface $connection): bool; /** * Get the connection name this fixture should be inserted into. diff --git a/app/vendor/cakephp/cakephp/src/Datasource/LICENSE.txt b/app/vendor/cakephp/cakephp/src/Datasource/LICENSE.txt index 0b3b94303..b938c9e8e 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/LICENSE.txt +++ b/app/vendor/cakephp/cakephp/src/Datasource/LICENSE.txt @@ -1,7 +1,7 @@ The MIT License (MIT) CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) -Copyright (c) 2005-2019, Cake Software Foundation, Inc. (https://cakefoundation.org) +Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/vendor/cakephp/cakephp/src/Datasource/Locator/AbstractLocator.php b/app/vendor/cakephp/cakephp/src/Datasource/Locator/AbstractLocator.php new file mode 100644 index 000000000..5fa87a709 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Datasource/Locator/AbstractLocator.php @@ -0,0 +1,109 @@ +instances[$alias])) { + if (!empty($storeOptions) && $this->options[$alias] !== $storeOptions) { + throw new RuntimeException(sprintf( + 'You cannot configure "%s", it already exists in the registry.', + $alias + )); + } + + return $this->instances[$alias]; + } + + $this->options[$alias] = $storeOptions; + + return $this->instances[$alias] = $this->createInstance($alias, $options); + } + + /** + * Create an instance of a given classname. + * + * @param string $alias Repository alias. + * @param array $options The options you want to build the instance with. + * @return \Cake\Datasource\RepositoryInterface + */ + abstract protected function createInstance(string $alias, array $options); + + /** + * @inheritDoc + */ + public function set(string $alias, RepositoryInterface $repository) + { + return $this->instances[$alias] = $repository; + } + + /** + * @inheritDoc + */ + public function exists(string $alias): bool + { + return isset($this->instances[$alias]); + } + + /** + * @inheritDoc + */ + public function remove(string $alias): void + { + unset( + $this->instances[$alias], + $this->options[$alias] + ); + } + + /** + * @inheritDoc + */ + public function clear(): void + { + $this->instances = []; + $this->options = []; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Datasource/Locator/LocatorInterface.php b/app/vendor/cakephp/cakephp/src/Datasource/Locator/LocatorInterface.php new file mode 100644 index 000000000..1d63a4560 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Datasource/Locator/LocatorInterface.php @@ -0,0 +1,68 @@ +modelClass; - } + $modelClass = $modelClass ?? $this->modelClass; if (empty($modelClass)) { throw new UnexpectedValueException('Default modelClass is empty'); } - if ($modelType === null) { - $modelType = $this->getModelType(); - } + $modelType = $modelType ?? $this->getModelType(); $options = []; if (strpos($modelClass, '\\') === false) { @@ -119,13 +117,13 @@ public function loadModel(?string $modelClass = null, ?string $modelType = null) return $this->{$alias}; } - if (isset($this->_modelFactories[$modelType])) { - $factory = $this->_modelFactories[$modelType]; - } - if (!isset($factory)) { - $factory = FactoryLocator::get($modelType); + $factory = $this->_modelFactories[$modelType] ?? FactoryLocator::get($modelType); + if ($factory instanceof LocatorInterface) { + $this->{$alias} = $factory->get($modelClass, $options); + } else { + $this->{$alias} = $factory($modelClass, $options); } - $this->{$alias} = $factory($modelClass, $options); + if (!$this->{$alias}) { throw new MissingModelException([$modelClass, $modelType]); } @@ -137,11 +135,19 @@ public function loadModel(?string $modelClass = null, ?string $modelType = null) * Override a existing callable to generate repositories of a given type. * * @param string $type The name of the repository type the factory function is for. - * @param callable $factory The factory function used to create instances. + * @param callable|\Cake\Datasource\Locator\LocatorInterface $factory The factory function used to create instances. * @return void */ - public function modelFactory(string $type, callable $factory): void + public function modelFactory(string $type, $factory): void { + if (!$factory instanceof LocatorInterface && !is_callable($factory)) { + throw new InvalidArgumentException(sprintf( + '`$factory` must be an instance of Cake\Datasource\Locator\LocatorInterface or a callable.' + . ' Got type `%s` instead.', + getTypeName($factory) + )); + } + $this->_modelFactories[$type] = $factory; } diff --git a/app/vendor/cakephp/cakephp/src/Datasource/Paginator.php b/app/vendor/cakephp/cakephp/src/Datasource/Paginator.php index 118a7ad28..3cabf61de 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/Paginator.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/Paginator.php @@ -16,7 +16,7 @@ */ namespace Cake\Datasource; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Core\InstanceConfigTrait; use Cake\Datasource\Exception\PageOutOfBoundsException; @@ -36,7 +36,7 @@ class Paginator implements PaginatorInterface * - `maxLimit` - The maximum limit users can choose to view. Defaults to 100 * - `limit` - The initial number of items per page. Defaults to 20. * - `page` - The starting page, defaults to 1. - * - `whitelist` - A list of parameters users are allowed to set using request + * - `allowedParameters` - A list of parameters users are allowed to set using request * parameters. Modifying this list will allow users to have more influence * over pagination, be careful with what you permit. * @@ -46,7 +46,7 @@ class Paginator implements PaginatorInterface 'page' => 1, 'limit' => 20, 'maxLimit' => 100, - 'whitelist' => ['limit', 'sort', 'page', 'direction'], + 'allowedParameters' => ['limit', 'sort', 'page', 'direction'], ]; /** @@ -99,19 +99,19 @@ class Paginator implements PaginatorInterface * By default CakePHP will automatically allow sorting on any column on the * repository object being paginated. Often times you will want to allow * sorting on either associated columns or calculated fields. In these cases - * you will need to define a whitelist of all the columns you wish to allow - * sorting on. You can define the whitelist in the `$settings` parameter: + * you will need to define an allowed list of all the columns you wish to allow + * sorting on. You can define the allowed sort fields in the `$settings` parameter: * * ``` * $settings = [ * 'Articles' => [ * 'finder' => 'custom', - * 'sortWhitelist' => ['title', 'author_id', 'comment_count'], + * 'sortableFields' => ['title', 'author_id', 'comment_count'], * ] * ]; * ``` * - * Passing an empty array as whitelist disallows sorting altogether. + * Passing an empty array as sortableFields disallows sorting altogether. * * ### Paginating with custom finders * @@ -169,7 +169,7 @@ public function paginate(object $object, array $params = [], array $settings = [ $query = $object; $object = $query->getRepository(); if ($object === null) { - throw new Exception('No repository set for query.'); + throw new CakeException('No repository set for query.'); } } @@ -367,7 +367,7 @@ protected function addSortingParams(array $params, array $data): array $params += [ 'sort' => $data['options']['sort'], - 'direction' => isset($data['options']['sort']) ? current($order) : null, + 'direction' => isset($data['options']['sort']) && count($order) ? current($order) : null, 'sortDefault' => $sortDefault, 'directionDefault' => $directionDefault, 'completeSort' => $order, @@ -406,6 +406,47 @@ public function getPagingParams(): array return $this->_pagingParams; } + /** + * Shim method for reading the deprecated whitelist or allowedParameters options + * + * @return string[] + */ + protected function getAllowedParameters(): array + { + $allowed = $this->getConfig('allowedParameters'); + if (!$allowed) { + $allowed = []; + } + $whitelist = $this->getConfig('whitelist'); + if ($whitelist) { + deprecationWarning('The `whitelist` option is deprecated. Use the `allowedParameters` option instead.'); + + return array_merge($allowed, $whitelist); + } + + return $allowed; + } + + /** + * Shim method for reading the deprecated sortWhitelist or sortableFields options. + * + * @param array $config The configuration data to coalesce and emit warnings on. + * @return string[]|null + */ + protected function getSortableFields(array $config): ?array + { + $allowed = $config['sortableFields'] ?? null; + if ($allowed !== null) { + return $allowed; + } + $deprecated = $config['sortWhitelist'] ?? null; + if ($deprecated !== null) { + deprecationWarning('The `sortWhitelist` option is deprecated. Use `sortableFields` instead.'); + } + + return $deprecated; + } + /** * Merges the various options that Paginator uses. * Pulls settings together from the following places: @@ -415,7 +456,7 @@ public function getPagingParams(): array * - Request parameters * * The result of this method is the aggregate of all the option sets - * combined together. You can change config value `whitelist` to modify + * combined together. You can change config value `allowedParameters` to modify * which options/values can be set using request parameters. * * @param array $params Request params. @@ -428,7 +469,9 @@ public function mergeOptions(array $params, array $settings): array $scope = $settings['scope']; $params = !empty($params[$scope]) ? (array)$params[$scope] : []; } - $params = array_intersect_key($params, array_flip($this->getConfig('whitelist'))); + + $allowed = $this->getAllowedParameters(); + $params = array_intersect_key($params, array_flip($allowed)); return array_merge($settings, $params); } @@ -449,6 +492,8 @@ public function getDefaults(string $alias, array $settings): array } $defaults = $this->getConfig(); + $defaults['whitelist'] = $defaults['allowedParameters'] = $this->getAllowedParameters(); + $maxLimit = $settings['maxLimit'] ?? $defaults['maxLimit']; $limit = $settings['limit'] ?? $defaults['limit']; @@ -469,14 +514,14 @@ public function getDefaults(string $alias, array $settings): array * also be sanitized. Lastly sort + direction keys will be converted into * the model friendly order key. * - * You can use the whitelist parameter to control which columns/fields are + * You can use the allowedParameters option to control which columns/fields are * available for sorting via URL parameters. This helps prevent users from ordering large * result sets on un-indexed values. * * If you need to sort on associated columns or synthetic properties you - * will need to use a whitelist. + * will need to use the `sortableFields` option. * - * Any columns listed in the sort whitelist will be implicitly trusted. + * Any columns listed in the allowed sort fields will be implicitly trusted. * You can use this to sort on synthetic columns, or columns added in custom * find operations that may not exist in the schema. * @@ -517,11 +562,14 @@ public function validateSort(RepositoryInterface $object, array $options): array return $options; } - $inWhitelist = false; - if (isset($options['sortWhitelist'])) { + $sortAllowed = false; + $allowed = $this->getSortableFields($options); + if ($allowed !== null) { + $options['sortableFields'] = $options['sortWhitelist'] = $allowed; + $field = key($options['order']); - $inWhitelist = in_array($field, $options['sortWhitelist'], true); - if (!$inWhitelist) { + $sortAllowed = in_array($field, $allowed, true); + if (!$sortAllowed) { $options['order'] = []; $options['sort'] = null; @@ -537,7 +585,7 @@ public function validateSort(RepositoryInterface $object, array $options): array $options['sort'] = key($options['order']); } - $options['order'] = $this->_prefix($object, $options['order'], $inWhitelist); + $options['order'] = $this->_prefix($object, $options['order'], $sortAllowed); return $options; } @@ -576,10 +624,10 @@ protected function _removeAliases(array $fields, string $model): array * * @param \Cake\Datasource\RepositoryInterface $object Repository object. * @param array $order Order array. - * @param bool $whitelisted Whether or not the field was whitelisted. + * @param bool $allowed Whether or not the field was allowed. * @return array Final order array. */ - protected function _prefix(RepositoryInterface $object, array $order, bool $whitelisted = false): array + protected function _prefix(RepositoryInterface $object, array $order, bool $allowed = false): array { $tableAlias = $object->getAlias(); $tableOrder = []; @@ -596,7 +644,7 @@ protected function _prefix(RepositoryInterface $object, array $order, bool $whit } $correctAlias = ($tableAlias === $alias); - if ($correctAlias && $whitelisted) { + if ($correctAlias && $allowed) { // Disambiguate fields in schema. As id is quite common. if ($object->hasField($field)) { $field = $alias . '.' . $field; @@ -604,7 +652,7 @@ protected function _prefix(RepositoryInterface $object, array $order, bool $whit $tableOrder[$field] = $value; } elseif ($correctAlias && $object->hasField($field)) { $tableOrder[$tableAlias . '.' . $field] = $value; - } elseif (!$correctAlias && $whitelisted) { + } elseif (!$correctAlias && $allowed) { $tableOrder[$alias . '.' . $field] = $value; } } @@ -621,7 +669,7 @@ protected function _prefix(RepositoryInterface $object, array $order, bool $whit public function checkLimit(array $options): array { $options['limit'] = (int)$options['limit']; - if (empty($options['limit']) || $options['limit'] < 1) { + if ($options['limit'] < 1) { $options['limit'] = 1; } $options['limit'] = max(min($options['limit'], $options['maxLimit']), 1); diff --git a/app/vendor/cakephp/cakephp/src/Datasource/QueryCacher.php b/app/vendor/cakephp/cakephp/src/Datasource/QueryCacher.php index 68696a715..ab5ddf540 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/QueryCacher.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/QueryCacher.php @@ -96,7 +96,7 @@ public function store(object $query, Traversable $results): bool $key = $this->_resolveKey($query); $storage = $this->_resolveCacher(); - return (bool)$storage->set($key, $results); + return $storage->set($key, $results); } /** diff --git a/app/vendor/cakephp/cakephp/src/Datasource/QueryInterface.php b/app/vendor/cakephp/cakephp/src/Datasource/QueryInterface.php index 55dba5a97..9b3fc8f62 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/QueryInterface.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/QueryInterface.php @@ -221,7 +221,9 @@ public function offset($num); * `ORDER BY title DESC, author_id ASC` * * ``` - * $query->order(['title' => 'DESC NULLS FIRST'])->order('author_id'); + * $query + * ->order(['title' => $query->newExpr('DESC NULLS FIRST')]) + * ->order('author_id'); * ``` * * Will generate: diff --git a/app/vendor/cakephp/cakephp/src/Datasource/QueryTrait.php b/app/vendor/cakephp/cakephp/src/Datasource/QueryTrait.php index a9d1d4b98..729cbdc61 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/QueryTrait.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/QueryTrait.php @@ -87,12 +87,12 @@ trait QueryTrait * Set the default Table object that will be used by this query * and form the `FROM` clause. * - * @param \Cake\Datasource\RepositoryInterface|\Cake\ORM\Table $table The default table object to use + * @param \Cake\Datasource\RepositoryInterface|\Cake\ORM\Table $repository The default table object to use * @return $this */ - public function repository(RepositoryInterface $table) + public function repository(RepositoryInterface $repository) { - $this->_repository = $table; + $this->_repository = $repository; return $this; } @@ -285,10 +285,11 @@ public function all(): ResultSetInterface return $this->_results; } + $results = null; if ($this->_cache) { $results = $this->_cache->fetch($this); } - if (!isset($results)) { + if ($results === null) { $results = $this->_decorateResults($this->_execute()); if ($this->_cache) { $this->_cache->store($this, $results); @@ -356,30 +357,86 @@ public function getMapReducers(): array * Registers a new formatter callback function that is to be executed when trying * to fetch the results from the database. * - * Formatting callbacks will get a first parameter, an object implementing - * `\Cake\Collection\CollectionInterface`, that can be traversed and modified at will. + * If the second argument is set to true, it will erase previous formatters + * and replace them with the passed first argument. * * Callbacks are required to return an iterator object, which will be used as * the return value for this query's result. Formatter functions are applied * after all the `MapReduce` routines for this query have been executed. * - * If the second argument is set to true, it will erase previous formatters - * and replace them with the passed first argument. + * Formatting callbacks will receive two arguments, the first one being an object + * implementing `\Cake\Collection\CollectionInterface`, that can be traversed and + * modified at will. The second one being the query instance on which the formatter + * callback is being applied. * - * ### Example: + * Usually the query instance received by the formatter callback is the same query + * instance on which the callback was attached to, except for in a joined + * association, in that case the callback will be invoked on the association source + * side query, and it will receive that query instance instead of the one on which + * the callback was originally attached to - see the examples below! + * + * ### Examples: + * + * Return all results from the table indexed by id: * * ``` - * // Return all results from the table indexed by id * $query->select(['id', 'name'])->formatResults(function ($results) { - * return $results->indexBy('id'); + * return $results->indexBy('id'); * }); + * ``` + * + * Add a new column to the ResultSet: * - * // Add a new column to the ResultSet + * ``` * $query->select(['name', 'birth_date'])->formatResults(function ($results) { - * return $results->map(function ($row) { - * $row['age'] = $row['birth_date']->diff(new DateTime)->y; - * return $row; - * }); + * return $results->map(function ($row) { + * $row['age'] = $row['birth_date']->diff(new DateTime)->y; + * + * return $row; + * }); + * }); + * ``` + * + * Add a new column to the results with respect to the query's hydration configuration: + * + * ``` + * $query->formatResults(function ($results, $query) { + * return $results->map(function ($row) use ($query) { + * $data = [ + * 'bar' => 'baz', + * ]; + * + * if ($query->isHydrationEnabled()) { + * $row['foo'] = new Foo($data) + * } else { + * $row['foo'] = $data; + * } + * + * return $row; + * }); + * }); + * ``` + * + * Retaining access to the association target query instance of joined associations, + * by inheriting the contain callback's query argument: + * + * ``` + * // Assuming a `Articles belongsTo Authors` association that uses the join strategy + * + * $articlesQuery->contain('Authors', function ($authorsQuery) { + * return $authorsQuery->formatResults(function ($results, $query) use ($authorsQuery) { + * // Here `$authorsQuery` will always be the instance + * // where the callback was attached to. + * + * // The instance passed to the callback in the second + * // argument (`$query`), will be the one where the + * // callback is actually being applied to, in this + * // example that would be `$articlesQuery`. + * + * // ... + * + * return $results; + * }); * }); * ``` * @@ -453,7 +510,6 @@ public function firstOrFail() { $entity = $this->first(); if (!$entity) { - /** @var \Cake\ORM\Table $table */ $table = $this->getRepository(); throw new RecordNotFoundException(sprintf( 'Record not found in table "%s"', @@ -478,6 +534,7 @@ public function firstOrFail() * @see \Cake\Datasource\QueryInterface::applyOptions() to read about the options that will * be processed by this class and not returned by this function * @return array + * @see applyOptions() */ public function getOptions(): array { @@ -539,7 +596,7 @@ protected function _decorateResults(Traversable $result): ResultSetInterface } foreach ($this->_formatters as $formatter) { - $result = $formatter($result); + $result = $formatter($result, $this); } if (!empty($this->_formatters) && !($result instanceof $decorator)) { diff --git a/app/vendor/cakephp/cakephp/src/Datasource/README.md b/app/vendor/cakephp/cakephp/src/Datasource/README.md index 093a6c26f..38626ea63 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/README.md +++ b/app/vendor/cakephp/cakephp/src/Datasource/README.md @@ -27,7 +27,7 @@ Additionally, this package provides a few traits and classes you can use in your * `EntityTrait` - Contains the default implementation for the `EntityInterface`. * `QueryTrait` - Exposes the methods for creating a query object capable of returning decoratable collections. -* `ResultSetDecorator` - Decorates any traversable object so it complies with `ResultSetInterface`. +* `ResultSetDecorator` - Decorates any traversable object, so it complies with `ResultSetInterface`. ## Connections @@ -79,4 +79,4 @@ $conn = ConnectionManager::config('other', $connectionInstance); ## Documentation -Please make sure you check the [official API documentation](https://api.cakephp.org/3.x/namespace-Cake.Datasource.html) +Please make sure you check the [official API documentation](https://api.cakephp.org/4.x/namespace-Cake.Datasource.html) diff --git a/app/vendor/cakephp/cakephp/src/Datasource/RulesAwareTrait.php b/app/vendor/cakephp/cakephp/src/Datasource/RulesAwareTrait.php index af4287db2..0e70f3750 100644 --- a/app/vendor/cakephp/cakephp/src/Datasource/RulesAwareTrait.php +++ b/app/vendor/cakephp/cakephp/src/Datasource/RulesAwareTrait.php @@ -97,6 +97,7 @@ public function rulesChecker(): RulesChecker } /** @psalm-var class-string<\Cake\Datasource\RulesChecker> $class */ $class = defined('static::RULES_CLASS') ? static::RULES_CLASS : RulesChecker::class; + /** @psalm-suppress ArgumentTypeCoercion */ $this->_rulesChecker = $this->buildRules(new $class(['repository' => $this])); $this->dispatchEvent('Model.buildRules', ['rules' => $this->_rulesChecker]); diff --git a/app/vendor/cakephp/cakephp/src/Error/BaseErrorHandler.php b/app/vendor/cakephp/cakephp/src/Error/BaseErrorHandler.php index b73639325..6f55b2947 100644 --- a/app/vendor/cakephp/cakephp/src/Error/BaseErrorHandler.php +++ b/app/vendor/cakephp/cakephp/src/Error/BaseErrorHandler.php @@ -18,9 +18,9 @@ use Cake\Core\Configure; use Cake\Core\InstanceConfigTrait; -use Cake\Log\Log; use Cake\Routing\Router; use Psr\Http\Message\ServerRequestInterface; +use RuntimeException; use Throwable; /** @@ -54,7 +54,7 @@ abstract class BaseErrorHandler /** * Exception logger instance. * - * @var \Cake\Error\ErrorLogger|null + * @var \Cake\Error\ErrorLoggerInterface|null */ protected $logger; @@ -148,7 +148,7 @@ public function handleError( ?int $line = null, ?array $context = null ): bool { - if (error_reporting() === 0) { + if (!(error_reporting() & $code)) { return false; } $this->_handled = true; @@ -166,7 +166,7 @@ public function handleError( 'line' => $line, ]; - $debug = Configure::read('debug'); + $debug = (bool)Configure::read('debug'); if ($debug) { // By default trim 3 frames off for the public and protected methods // used by ErrorHandler instances. @@ -309,22 +309,16 @@ protected function _logError($level, array $data): bool $data['file'], $data['line'] ); + $context = []; if (!empty($this->_config['trace'])) { - /** @var string $trace */ - $trace = Debugger::trace([ + $context['trace'] = Debugger::trace([ 'start' => 1, 'format' => 'log', ]); - - $request = Router::getRequest(); - if ($request) { - $message .= $this->getLogger()->getRequestContext($request); - } - $message .= "\nTrace:\n" . $trace . "\n"; + $context['request'] = Router::getRequest(); } - $message .= "\n\n"; - return Log::write($level, $message); + return $this->getLogger()->logMessage($level, $message, $context); } /** @@ -346,13 +340,22 @@ public function logException(Throwable $exception, ?ServerRequestInterface $requ /** * Get exception logger. * - * @return \Cake\Error\ErrorLogger + * @return \Cake\Error\ErrorLoggerInterface */ public function getLogger() { if ($this->logger === null) { - /** @var \Cake\Error\ErrorLogger $logger */ + /** @var \Cake\Error\ErrorLoggerInterface $logger */ $logger = new $this->_config['errorLogger']($this->_config); + + if (!$logger instanceof ErrorLoggerInterface) { + // Set the logger so that the next error can be logged. + $this->logger = new ErrorLogger($this->_config); + + $interface = ErrorLoggerInterface::class; + $type = getTypeName($logger); + throw new RuntimeException("Cannot create logger. `{$type}` does not implement `{$interface}`."); + } $this->logger = $logger; } diff --git a/app/vendor/cakephp/cakephp/src/Error/ConsoleErrorHandler.php b/app/vendor/cakephp/cakephp/src/Error/ConsoleErrorHandler.php index 1ece35cf1..6eff9ec5c 100644 --- a/app/vendor/cakephp/cakephp/src/Error/ConsoleErrorHandler.php +++ b/app/vendor/cakephp/cakephp/src/Error/ConsoleErrorHandler.php @@ -16,7 +16,9 @@ */ namespace Cake\Error; +use Cake\Command\Command; use Cake\Console\ConsoleOutput; +use Cake\Console\Exception\ConsoleException; use Throwable; /** @@ -61,9 +63,12 @@ public function handleException(Throwable $exception): void { $this->_displayException($exception); $this->logException($exception); - $code = $exception->getCode(); - $code = $code && is_int($code) ? $code : 1; - $this->_stop($code); + + $exitCode = Command::CODE_ERROR; + if ($exception instanceof ConsoleException) { + $exitCode = $exception->getCode(); + } + $this->_stop($exitCode); } /** diff --git a/app/vendor/cakephp/cakephp/src/Error/Debug/ArrayItemNode.php b/app/vendor/cakephp/cakephp/src/Error/Debug/ArrayItemNode.php new file mode 100644 index 000000000..4051dd475 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Error/Debug/ArrayItemNode.php @@ -0,0 +1,73 @@ +key = $key; + $this->value = $value; + } + + /** + * Get the value + * + * @return \Cake\Error\Debug\NodeInterface + */ + public function getValue() + { + return $this->value; + } + + /** + * Get the key + * + * @return \Cake\Error\Debug\NodeInterface + */ + public function getKey() + { + return $this->key; + } + + /** + * @inheritDoc + */ + public function getChildren(): array + { + return [$this->value]; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Error/Debug/ArrayNode.php b/app/vendor/cakephp/cakephp/src/Error/Debug/ArrayNode.php new file mode 100644 index 000000000..115a40f16 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Error/Debug/ArrayNode.php @@ -0,0 +1,72 @@ +items = []; + foreach ($items as $item) { + $this->add($item); + } + } + + /** + * Add an item + * + * @param \Cake\Error\Debug\ArrayItemNode $node The item to add. + * @return void + */ + public function add(ArrayItemNode $node): void + { + $this->items[] = $node; + } + + /** + * Get the contained items + * + * @return \Cake\Error\Debug\ArrayItemNode[] + */ + public function getValue(): array + { + return $this->items; + } + + /** + * Get Item nodes + * + * @return \Cake\Error\Debug\ArrayItemNode[] + */ + public function getChildren(): array + { + return $this->items; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Error/Debug/ClassNode.php b/app/vendor/cakephp/cakephp/src/Error/Debug/ClassNode.php new file mode 100644 index 000000000..d97ea328f --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Error/Debug/ClassNode.php @@ -0,0 +1,91 @@ +class = $class; + $this->id = $id; + } + + /** + * Add a property + * + * @param \Cake\Error\Debug\PropertyNode $node The property to add. + * @return void + */ + public function addProperty(PropertyNode $node): void + { + $this->properties[] = $node; + } + + /** + * Get the class name + * + * @return string + */ + public function getValue(): string + { + return $this->class; + } + + /** + * Get the reference id + * + * @return int + */ + public function getId(): int + { + return $this->id; + } + + /** + * Get property nodes + * + * @return \Cake\Error\Debug\PropertyNode[] + */ + public function getChildren(): array + { + return $this->properties; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Error/Debug/ConsoleFormatter.php b/app/vendor/cakephp/cakephp/src/Error/Debug/ConsoleFormatter.php new file mode 100644 index 000000000..5f1c6bd89 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Error/Debug/ConsoleFormatter.php @@ -0,0 +1,243 @@ + '1;33', + // green + 'string' => '0;32', + // bold blue + 'number' => '1;34', + // cyan + 'class' => '0;36', + // grey + 'punct' => '0;90', + // default foreground + 'property' => '0;39', + // magenta + 'visibility' => '0;35', + // red + 'special' => '0;31', + ]; + + /** + * Check if the current environment supports ANSI output. + * + * @return bool + */ + public static function environmentMatches(): bool + { + if (PHP_SAPI !== 'cli') { + return false; + } + // NO_COLOR in environment means no color. + if (env('NO_COLOR')) { + return false; + } + // Windows environment checks + if ( + DIRECTORY_SEPARATOR === '\\' && + strpos(strtolower(php_uname('v')), 'windows 10') === false && + strpos(strtolower((string)env('SHELL')), 'bash.exe') === false && + !(bool)env('ANSICON') && + env('ConEmuANSI') !== 'ON' + ) { + return false; + } + + return true; + } + + /** + * @inheritDoc + */ + public function formatWrapper(string $contents, array $location): string + { + $lineInfo = ''; + if (isset($location['file'], $location['file'])) { + $lineInfo = sprintf('%s (line %s)', $location['file'], $location['line']); + } + $parts = [ + $this->style('const', $lineInfo), + $this->style('special', '########## DEBUG ##########'), + $contents, + $this->style('special', '###########################'), + '', + ]; + + return implode("\n", $parts); + } + + /** + * Convert a tree of NodeInterface objects into a plain text string. + * + * @param \Cake\Error\Debug\NodeInterface $node The node tree to dump. + * @return string + */ + public function dump(NodeInterface $node): string + { + $indent = 0; + + return $this->export($node, $indent); + } + + /** + * Convert a tree of NodeInterface objects into a plain text string. + * + * @param \Cake\Error\Debug\NodeInterface $var The node tree to dump. + * @param int $indent The current indentation level. + * @return string + */ + protected function export(NodeInterface $var, int $indent): string + { + if ($var instanceof ScalarNode) { + switch ($var->getType()) { + case 'bool': + return $this->style('const', $var->getValue() ? 'true' : 'false'); + case 'null': + return $this->style('const', 'null'); + case 'string': + return $this->style('string', "'" . (string)$var->getValue() . "'"); + case 'int': + case 'float': + return $this->style('visibility', "({$var->getType()})") . + ' ' . $this->style('number', "{$var->getValue()}"); + default: + return "({$var->getType()}) {$var->getValue()}"; + } + } + if ($var instanceof ArrayNode) { + return $this->exportArray($var, $indent + 1); + } + if ($var instanceof ClassNode || $var instanceof ReferenceNode) { + return $this->exportObject($var, $indent + 1); + } + if ($var instanceof SpecialNode) { + return $this->style('special', $var->getValue()); + } + throw new RuntimeException('Unknown node received ' . get_class($var)); + } + + /** + * Export an array type object + * + * @param \Cake\Error\Debug\ArrayNode $var The array to export. + * @param int $indent The current indentation level. + * @return string Exported array. + */ + protected function exportArray(ArrayNode $var, int $indent): string + { + $out = $this->style('punct', '['); + $break = "\n" . str_repeat(' ', $indent); + $end = "\n" . str_repeat(' ', $indent - 1); + $vars = []; + + $arrow = $this->style('punct', ' => '); + foreach ($var->getChildren() as $item) { + $val = $item->getValue(); + $vars[] = $break . $this->export($item->getKey(), $indent) . $arrow . $this->export($val, $indent); + } + + $close = $this->style('punct', ']'); + if (count($vars)) { + return $out . implode($this->style('punct', ','), $vars) . $end . $close; + } + + return $out . $close; + } + + /** + * Handles object to string conversion. + * + * @param \Cake\Error\Debug\ClassNode|\Cake\Error\Debug\ReferenceNode $var Object to convert. + * @param int $indent Current indentation level. + * @return string + * @see \Cake\Error\Debugger::exportVar() + */ + protected function exportObject($var, int $indent): string + { + $props = []; + + if ($var instanceof ReferenceNode) { + return $this->style('punct', 'object(') . + $this->style('class', $var->getValue()) . + $this->style('punct', ') id:') . + $this->style('number', (string)$var->getId()) . + $this->style('punct', ' {}'); + } + + $out = $this->style('punct', 'object(') . + $this->style('class', $var->getValue()) . + $this->style('punct', ') id:') . + $this->style('number', (string)$var->getId()) . + $this->style('punct', ' {'); + + $break = "\n" . str_repeat(' ', $indent); + $end = "\n" . str_repeat(' ', $indent - 1) . $this->style('punct', '}'); + + $arrow = $this->style('punct', ' => '); + foreach ($var->getChildren() as $property) { + $visibility = $property->getVisibility(); + $name = $property->getName(); + if ($visibility && $visibility !== 'public') { + $props[] = $this->style('visibility', $visibility) . + ' ' . + $this->style('property', $name) . + $arrow . + $this->export($property->getValue(), $indent); + } else { + $props[] = $this->style('property', $name) . + $arrow . + $this->export($property->getValue(), $indent); + } + } + if (count($props)) { + return $out . $break . implode($break, $props) . $end; + } + + return $out . $this->style('punct', '}'); + } + + /** + * Style text with ANSI escape codes. + * + * @param string $style The style name to use. + * @param string $text The text to style. + * @return string The styled output. + */ + protected function style(string $style, string $text): string + { + $code = $this->styles[$style]; + + return "\033[{$code}m{$text}\033[0m"; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Error/Debug/DebugContext.php b/app/vendor/cakephp/cakephp/src/Error/Debug/DebugContext.php new file mode 100644 index 000000000..7eb21bf57 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Error/Debug/DebugContext.php @@ -0,0 +1,110 @@ +maxDepth = $maxDepth; + $this->refs = new SplObjectStorage(); + } + + /** + * Return a clone with increased depth. + * + * @return static + */ + public function withAddedDepth() + { + $new = clone $this; + $new->depth += 1; + + return $new; + } + + /** + * Get the remaining depth levels + * + * @return int + */ + public function remainingDepth(): int + { + return $this->maxDepth - $this->depth; + } + + /** + * Get the reference ID for an object. + * + * If this object does not exist in the reference storage, + * it will be added and the id will be returned. + * + * @param object $object The object to get a reference for. + * @return int + */ + public function getReferenceId(object $object): int + { + if ($this->refs->contains($object)) { + return $this->refs[$object]; + } + $refId = $this->refs->count(); + $this->refs->attach($object, $refId); + + return $refId; + } + + /** + * Check whether an object has been seen before. + * + * @param object $object The object to get a reference for. + * @return bool + */ + public function hasReference(object $object): bool + { + return $this->refs->contains($object); + } +} diff --git a/app/vendor/cakephp/cakephp/src/Error/Debug/FormatterInterface.php b/app/vendor/cakephp/cakephp/src/Error/Debug/FormatterInterface.php new file mode 100644 index 000000000..bacf883d3 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Error/Debug/FormatterInterface.php @@ -0,0 +1,42 @@ +id = uniqid('', true); + } + + /** + * Check if the current environment is not a CLI context + * + * @return bool + */ + public static function environmentMatches(): bool + { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + return false; + } + + return true; + } + + /** + * @inheritDoc + */ + public function formatWrapper(string $contents, array $location): string + { + $lineInfo = ''; + if (isset($location['file'], $location['file'])) { + $lineInfo = sprintf( + '%s (line %s)', + $location['file'], + $location['line'] + ); + } + $parts = [ + '
', + $lineInfo, + $contents, + '
', + ]; + + return implode("\n", $parts); + } + + /** + * Generate the CSS and Javascript for dumps + * + * Only output once per process as we don't need it more than once. + * + * @return string + */ + protected function dumpHeader(): string + { + ob_start(); + include __DIR__ . DIRECTORY_SEPARATOR . 'dumpHeader.html'; + + return ob_get_clean(); + } + + /** + * Convert a tree of NodeInterface objects into HTML + * + * @param \Cake\Error\Debug\NodeInterface $node The node tree to dump. + * @return string + */ + public function dump(NodeInterface $node): string + { + $html = $this->export($node, 0); + $head = ''; + if (!static::$outputHeader) { + static::$outputHeader = true; + $head = $this->dumpHeader(); + } + + return $head . '
' . $html . '
'; + } + + /** + * Convert a tree of NodeInterface objects into HTML + * + * @param \Cake\Error\Debug\NodeInterface $var The node tree to dump. + * @param int $indent The current indentation level. + * @return string + */ + protected function export(NodeInterface $var, int $indent): string + { + if ($var instanceof ScalarNode) { + switch ($var->getType()) { + case 'bool': + return $this->style('const', $var->getValue() ? 'true' : 'false'); + case 'null': + return $this->style('const', 'null'); + case 'string': + return $this->style('string', "'" . (string)$var->getValue() . "'"); + case 'int': + case 'float': + return $this->style('visibility', "({$var->getType()})") . + ' ' . $this->style('number', "{$var->getValue()}"); + default: + return "({$var->getType()}) {$var->getValue()}"; + } + } + if ($var instanceof ArrayNode) { + return $this->exportArray($var, $indent + 1); + } + if ($var instanceof ClassNode || $var instanceof ReferenceNode) { + return $this->exportObject($var, $indent + 1); + } + if ($var instanceof SpecialNode) { + return $this->style('special', $var->getValue()); + } + throw new RuntimeException('Unknown node received ' . get_class($var)); + } + + /** + * Export an array type object + * + * @param \Cake\Error\Debug\ArrayNode $var The array to export. + * @param int $indent The current indentation level. + * @return string Exported array. + */ + protected function exportArray(ArrayNode $var, int $indent): string + { + $open = '' . + $this->style('punct', '[') . + ''; + $vars = []; + $break = "\n" . str_repeat(' ', $indent); + $endBreak = "\n" . str_repeat(' ', $indent - 1); + + $arrow = $this->style('punct', ' => '); + foreach ($var->getChildren() as $item) { + $val = $item->getValue(); + $vars[] = $break . '' . + $this->export($item->getKey(), $indent) . $arrow . $this->export($val, $indent) . + $this->style('punct', ',') . + ''; + } + + $close = '' . + $endBreak . + $this->style('punct', ']') . + ''; + + return $open . implode('', $vars) . $close; + } + + /** + * Handles object to string conversion. + * + * @param \Cake\Error\Debug\ClassNode|\Cake\Error\Debug\ReferenceNode $var Object to convert. + * @param int $indent The current indentation level. + * @return string + * @see \Cake\Error\Debugger::exportVar() + */ + protected function exportObject($var, int $indent): string + { + $objectId = "cake-db-object-{$this->id}-{$var->getId()}"; + $out = sprintf( + '', + $objectId + ); + $break = "\n" . str_repeat(' ', $indent); + $endBreak = "\n" . str_repeat(' ', $indent - 1); + + if ($var instanceof ReferenceNode) { + $link = sprintf( + '
id: %s', + $objectId, + $var->getId() + ); + + return '' . + $this->style('punct', 'object(') . + $this->style('class', $var->getValue()) . + $this->style('punct', ') ') . + $link . + $this->style('punct', ' {}') . + ''; + } + + $out .= $this->style('punct', 'object(') . + $this->style('class', $var->getValue()) . + $this->style('punct', ') id:') . + $this->style('number', (string)$var->getId()) . + $this->style('punct', ' {') . + ''; + + $props = []; + foreach ($var->getChildren() as $property) { + $arrow = $this->style('punct', ' => '); + $visibility = $property->getVisibility(); + $name = $property->getName(); + if ($visibility && $visibility !== 'public') { + $props[] = $break . + '' . + $this->style('visibility', $visibility) . + ' ' . + $this->style('property', $name) . + $arrow . + $this->export($property->getValue(), $indent) . + ''; + } else { + $props[] = $break . + '' . + $this->style('property', $name) . + $arrow . + $this->export($property->getValue(), $indent) . + ''; + } + } + + $end = '' . + $endBreak . + $this->style('punct', '}') . + ''; + + if (count($props)) { + return $out . implode('', $props) . $end; + } + + return $out . $end; + } + + /** + * Style text with HTML class names + * + * @param string $style The style name to use. + * @param string $text The text to style. + * @return string The styled output. + */ + protected function style(string $style, string $text): string + { + return sprintf( + '%s', + $style, + h($text) + ); + } +} diff --git a/app/vendor/cakephp/cakephp/src/Error/Debug/NodeInterface.php b/app/vendor/cakephp/cakephp/src/Error/Debug/NodeInterface.php new file mode 100644 index 000000000..82e25c484 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Error/Debug/NodeInterface.php @@ -0,0 +1,39 @@ +name = $name; + $this->visibility = $visibility; + $this->value = $value; + } + + /** + * Get the value + * + * @return \Cake\Error\Debug\NodeInterface + */ + public function getValue(): NodeInterface + { + return $this->value; + } + + /** + * Get the property visibility + * + * @return string + */ + public function getVisibility(): ?string + { + return $this->visibility; + } + + /** + * Get the property name + * + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @inheritDoc + */ + public function getChildren(): array + { + return [$this->value]; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Error/Debug/ReferenceNode.php b/app/vendor/cakephp/cakephp/src/Error/Debug/ReferenceNode.php new file mode 100644 index 000000000..008428f42 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Error/Debug/ReferenceNode.php @@ -0,0 +1,77 @@ +class = $class; + $this->id = $id; + } + + /** + * Get the class name/value + * + * @return string + */ + public function getValue(): string + { + return $this->class; + } + + /** + * Get the reference id for this node. + * + * @return int + */ + public function getId(): int + { + return $this->id; + } + + /** + * @inheritDoc + */ + public function getChildren(): array + { + return []; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Error/Debug/ScalarNode.php b/app/vendor/cakephp/cakephp/src/Error/Debug/ScalarNode.php new file mode 100644 index 000000000..cb4098d86 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Error/Debug/ScalarNode.php @@ -0,0 +1,73 @@ +type = $type; + $this->value = $value; + } + + /** + * Get the type of value + * + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * Get the value + * + * @return string|int|float|bool|null + */ + public function getValue() + { + return $this->value; + } + + /** + * @inheritDoc + */ + public function getChildren(): array + { + return []; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Error/Debug/SpecialNode.php b/app/vendor/cakephp/cakephp/src/Error/Debug/SpecialNode.php new file mode 100644 index 000000000..34b38f0b8 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Error/Debug/SpecialNode.php @@ -0,0 +1,56 @@ +value = $value; + } + + /** + * Get the message/value + * + * @return string + */ + public function getValue(): string + { + return $this->value; + } + + /** + * @inheritDoc + */ + public function getChildren(): array + { + return []; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Error/Debug/TextFormatter.php b/app/vendor/cakephp/cakephp/src/Error/Debug/TextFormatter.php new file mode 100644 index 000000000..ef60b74d1 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Error/Debug/TextFormatter.php @@ -0,0 +1,158 @@ +export($node, $indent); + } + + /** + * Convert a tree of NodeInterface objects into a plain text string. + * + * @param \Cake\Error\Debug\NodeInterface $var The node tree to dump. + * @param int $indent The current indentation level. + * @return string + */ + protected function export(NodeInterface $var, int $indent): string + { + if ($var instanceof ScalarNode) { + switch ($var->getType()) { + case 'bool': + return $var->getValue() ? 'true' : 'false'; + case 'null': + return 'null'; + case 'string': + return "'" . (string)$var->getValue() . "'"; + default: + return "({$var->getType()}) {$var->getValue()}"; + } + } + if ($var instanceof ArrayNode) { + return $this->exportArray($var, $indent + 1); + } + if ($var instanceof ClassNode || $var instanceof ReferenceNode) { + return $this->exportObject($var, $indent + 1); + } + if ($var instanceof SpecialNode) { + return $var->getValue(); + } + throw new RuntimeException('Unknown node received ' . get_class($var)); + } + + /** + * Export an array type object + * + * @param \Cake\Error\Debug\ArrayNode $var The array to export. + * @param int $indent The current indentation level. + * @return string Exported array. + */ + protected function exportArray(ArrayNode $var, int $indent): string + { + $out = '['; + $break = "\n" . str_repeat(' ', $indent); + $end = "\n" . str_repeat(' ', $indent - 1); + $vars = []; + + foreach ($var->getChildren() as $item) { + $val = $item->getValue(); + $vars[] = $break . $this->export($item->getKey(), $indent) . ' => ' . $this->export($val, $indent); + } + if (count($vars)) { + return $out . implode(',', $vars) . $end . ']'; + } + + return $out . ']'; + } + + /** + * Handles object to string conversion. + * + * @param \Cake\Error\Debug\ClassNode|\Cake\Error\Debug\ReferenceNode $var Object to convert. + * @param int $indent Current indentation level. + * @return string + * @see \Cake\Error\Debugger::exportVar() + */ + protected function exportObject($var, int $indent): string + { + $out = ''; + $props = []; + + if ($var instanceof ReferenceNode) { + return "object({$var->getValue()}) id:{$var->getId()} {}"; + } + + $out .= "object({$var->getValue()}) id:{$var->getId()} {"; + $break = "\n" . str_repeat(' ', $indent); + $end = "\n" . str_repeat(' ', $indent - 1) . '}'; + + foreach ($var->getChildren() as $property) { + $visibility = $property->getVisibility(); + $name = $property->getName(); + if ($visibility && $visibility !== 'public') { + $props[] = "[{$visibility}] {$name} => " . $this->export($property->getValue(), $indent); + } else { + $props[] = "{$name} => " . $this->export($property->getValue(), $indent); + } + } + if (count($props)) { + return $out . $break . implode($break, $props) . $end; + } + + return $out . '}'; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Error/Debug/dumpHeader.html b/app/vendor/cakephp/cakephp/src/Error/Debug/dumpHeader.html new file mode 100644 index 000000000..501b2d0ad --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Error/Debug/dumpHeader.html @@ -0,0 +1,275 @@ + + diff --git a/app/vendor/cakephp/cakephp/src/Error/Debugger.php b/app/vendor/cakephp/cakephp/src/Error/Debugger.php index b66e1ffc1..e4bac37e5 100644 --- a/app/vendor/cakephp/cakephp/src/Error/Debugger.php +++ b/app/vendor/cakephp/cakephp/src/Error/Debugger.php @@ -16,21 +16,38 @@ */ namespace Cake\Error; +use Cake\Core\Configure; use Cake\Core\InstanceConfigTrait; +use Cake\Error\Debug\ArrayItemNode; +use Cake\Error\Debug\ArrayNode; +use Cake\Error\Debug\ClassNode; +use Cake\Error\Debug\ConsoleFormatter; +use Cake\Error\Debug\DebugContext; +use Cake\Error\Debug\FormatterInterface; +use Cake\Error\Debug\HtmlFormatter; +use Cake\Error\Debug\NodeInterface; +use Cake\Error\Debug\PropertyNode; +use Cake\Error\Debug\ReferenceNode; +use Cake\Error\Debug\ScalarNode; +use Cake\Error\Debug\SpecialNode; +use Cake\Error\Debug\TextFormatter; use Cake\Log\Log; use Cake\Utility\Hash; use Cake\Utility\Security; use Cake\Utility\Text; +use Closure; use Exception; use InvalidArgumentException; use ReflectionObject; use ReflectionProperty; +use RuntimeException; use Throwable; /** * Provide custom logging and error handling. * - * Debugger overrides PHP's default error handling to provide stack traces and enhanced logging + * Debugger extends PHP's default error handling and gives + * simpler to use more powerful interfaces. * * @link https://book.cakephp.org/4/en/development/debugging.html#namespace-Cake\Error */ @@ -45,6 +62,8 @@ class Debugger */ protected $_defaultConfig = [ 'outputMask' => [], + 'exportFormatter' => null, + 'editor' => 'phpstorm', ]; /** @@ -91,6 +110,21 @@ class Debugger ], ]; + /** + * A map of editors to their link templates. + * + * @var array + */ + protected $editors = [ + 'atom' => 'atom://core/open/file?filename={file}&line={line}', + 'emacs' => 'emacs://open?url=file://{file}&line={line}', + 'macvim' => 'mvim://open/?url=file://{file}&line={line}', + 'phpstorm' => 'phpstorm://open?file={file}&line={line}', + 'sublime' => 'subl://open?url=file://{file}&line={line}', + 'textmate' => 'txmt://open?url=file://{file}&line={line}', + 'vscode' => 'vscode://file/{file}:{line}', + ]; + /** * Holds current output data when outputFormat is false. * @@ -104,7 +138,6 @@ class Debugger public function __construct() { $docRef = ini_get('docref_root'); - if (empty($docRef) && function_exists('ini_set')) { ini_set('docref_root', 'https://secure.php.net/'); } @@ -112,6 +145,9 @@ public function __construct() define('E_RECOVERABLE_ERROR', 4096); } + $config = array_intersect_key((array)Configure::read('Debugger'), $this->_defaultConfig); + $this->setConfig($config); + $e = '
';
         $e .= '_templates['js']['context'] = '
_templates['js']['context'] .= 'style="display: none;">{:context}
'; $this->_templates['js']['code'] = '
_templates['html']['error'] = $e;
 
-        $this->_templates['html']['context'] = '
Context ';
+        $this->_templates['html']['context'] = '
Context ';
         $this->_templates['html']['context'] .= '

{:context}

'; } @@ -182,7 +218,7 @@ public static function getInstance(?string $class = null) * @param mixed|null $value The value to set. * @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true. * @return mixed Config value being read, or the object itself on write operations. - * @throws \Cake\Core\Exception\Exception When trying to set a key that is invalid. + * @throws \Cake\Core\Exception\CakeException When trying to set a key that is invalid. */ public static function configInstance($key = null, $value = null, bool $merge = true) { @@ -223,18 +259,78 @@ public static function setOutputMask(array $value, bool $merge = true): void static::configInstance('outputMask', $value, $merge); } + /** + * Add an editor link format + * + * Template strings can use the `{file}` and `{line}` placeholders. + * Closures templates must return a string, and accept two parameters: + * The file and line. + * + * @param string $name The name of the editor. + * @param string|\Closure $template The string template or closure + * @return void + */ + public static function addEditor(string $name, $template): void + { + $instance = static::getInstance(); + if (!is_string($template) && !($template instanceof Closure)) { + $type = getTypeName($template); + throw new RuntimeException("Invalid editor type of `{$type}`. Expected string or Closure."); + } + $instance->editors[$name] = $template; + } + + /** + * Choose the editor link style you want to use. + * + * @param string $name The editor name. + * @return void + */ + public static function setEditor(string $name): void + { + $instance = static::getInstance(); + if (!isset($instance->editors[$name])) { + $known = implode(', ', array_keys($instance->editors)); + throw new RuntimeException("Unknown editor `{$name}`. Known editors are {$known}"); + } + $instance->setConfig('editor', $name); + } + + /** + * Get a formatted URL for the active editor. + * + * @param string $file The file to create a link for. + * @param int $line The line number to create a link for. + * @return string The formatted URL. + */ + public static function editorUrl(string $file, int $line): string + { + $instance = static::getInstance(); + $editor = $instance->getConfig('editor'); + if (!isset($instance->editors[$editor])) { + throw new RuntimeException("Cannot format editor URL `{$editor}` is not a known editor."); + } + + $template = $instance->editors[$editor]; + if (is_string($template)) { + return str_replace(['{file}', '{line}'], [$file, (string)$line], $template); + } + + return $template($file, $line); + } + /** * Recursively formats and outputs the contents of the supplied variable. * * @param mixed $var The variable to dump. - * @param int $depth The depth to output to. Defaults to 3. + * @param int $maxDepth The depth to output to. Defaults to 3. * @return void * @see \Cake\Error\Debugger::exportVar() * @link https://book.cakephp.org/4/en/development/debugging.html#outputting-values */ - public static function dump($var, int $depth = 3): void + public static function dump($var, int $maxDepth = 3): void { - pr(static::exportVar($var, $depth)); + pr(static::exportVar($var, $maxDepth)); } /** @@ -243,16 +339,16 @@ public static function dump($var, int $depth = 3): void * * @param mixed $var Variable or content to log. * @param int|string $level Type of log to use. Defaults to 'debug'. - * @param int $depth The depth to output to. Defaults to 3. + * @param int $maxDepth The depth to output to. Defaults to 3. * @return void */ - public static function log($var, $level = 'debug', int $depth = 3): void + public static function log($var, $level = 'debug', int $maxDepth = 3): void { /** @var string $source */ $source = static::trace(['start' => 1]); $source .= "\n"; - Log::write($level, "\n" . $source . static::exportVar($var, $depth)); + Log::write($level, "\n" . $source . static::exportVar($var, $maxDepth)); } /** @@ -364,6 +460,7 @@ public static function formatTrace($backtrace, array $options = []) return $back; } + /** @psalm-suppress InvalidArgument */ return implode("\n", $back); } @@ -471,6 +568,36 @@ protected static function _highlight(string $str): string return $highlight; } + /** + * Get the configured export formatter or infer one based on the environment. + * + * @return \Cake\Error\Debug\FormatterInterface + * @unstable This method is not stable and may change in the future. + * @since 4.1.0 + */ + public function getExportFormatter(): FormatterInterface + { + $instance = static::getInstance(); + $class = $instance->getConfig('exportFormatter'); + if (!$class) { + if (ConsoleFormatter::environmentMatches()) { + $class = ConsoleFormatter::class; + } elseif (HtmlFormatter::environmentMatches()) { + $class = HtmlFormatter::class; + } else { + $class = TextFormatter::class; + } + } + $instance = new $class(); + if (!$instance instanceof FormatterInterface) { + throw new RuntimeException( + "The `{$class}` formatter does not implement " . FormatterInterface::class + ); + } + + return $instance; + } + /** * Converts a variable to a string for debug output. * @@ -489,47 +616,59 @@ protected static function _highlight(string $str): string * shown in an error message if CakePHP is deployed in development mode. * * @param mixed $var Variable to convert. - * @param int $depth The depth to output to. Defaults to 3. + * @param int $maxDepth The depth to output to. Defaults to 3. * @return string Variable as a formatted string */ - public static function exportVar($var, int $depth = 3): string + public static function exportVar($var, int $maxDepth = 3): string { - return static::_export($var, $depth, 0); + $context = new DebugContext($maxDepth); + $node = static::export($var, $context); + + return static::getInstance()->getExportFormatter()->dump($node); + } + + /** + * Convert the variable to the internal node tree. + * + * The node tree can be manipulated and serialized more easily + * than many object graphs can. + * + * @param mixed $var Variable to convert. + * @param int $maxDepth The depth to generate nodes to. Defaults to 3. + * @return \Cake\Error\Debug\NodeInterface The root node of the tree. + */ + public static function exportVarAsNodes($var, int $maxDepth = 3): NodeInterface + { + return static::export($var, new DebugContext($maxDepth)); } /** * Protected export function used to keep track of indentation and recursion. * * @param mixed $var The variable to dump. - * @param int $depth The remaining depth. - * @param int $indent The current indentation level. - * @return string The dumped variable. + * @param \Cake\Error\Debug\DebugContext $context Dump context + * @return \Cake\Error\Debug\NodeInterface The dumped variable. */ - protected static function _export($var, int $depth, int $indent): string + protected static function export($var, DebugContext $context): NodeInterface { - switch (static::getType($var)) { - case 'boolean': - return $var ? 'true' : 'false'; - case 'integer': - return '(int) ' . $var; + $type = static::getType($var); + switch ($type) { case 'float': - return '(float) ' . $var; case 'string': - if (trim($var) === '' && ctype_space($var) === false) { - return "''"; - } - - return "'" . $var . "'"; - case 'array': - return static::_array($var, $depth - 1, $indent + 1); case 'resource': - return strtolower(gettype($var)); + case 'resource (closed)': case 'null': - return 'null'; + return new ScalarNode($type, $var); + case 'boolean': + return new ScalarNode('bool', $var); + case 'integer': + return new ScalarNode('int', $var); + case 'array': + return static::exportArray($var, $context->withAddedDepth()); case 'unknown': - return 'unknown'; + return new SpecialNode('(unknown)'); default: - return static::_object($var, $depth - 1, $indent + 1); + return static::exportObject($var, $context->withAddedDepth()); } } @@ -547,81 +686,81 @@ protected static function _export($var, int $depth, int $indent): string * - schema * * @param array $var The array to export. - * @param int $depth The current depth, used for recursion tracking. - * @param int $indent The current indentation level. - * @return string Exported array. + * @param \Cake\Error\Debug\DebugContext $context The current dump context. + * @return \Cake\Error\Debug\ArrayNode Exported array. */ - protected static function _array(array $var, int $depth, int $indent): string + protected static function exportArray(array $var, DebugContext $context): ArrayNode { - $out = '['; - $break = $end = ''; - if (!empty($var)) { - $break = "\n" . str_repeat("\t", $indent); - $end = "\n" . str_repeat("\t", $indent - 1); - } - $vars = []; + $items = []; - if ($depth >= 0) { - $outputMask = (array)static::outputMask(); + $remaining = $context->remainingDepth(); + if ($remaining >= 0) { + $outputMask = static::outputMask(); foreach ($var as $key => $val) { - // Sniff for globals as !== explodes in < 5.4 - if ($key === 'GLOBALS' && is_array($val) && isset($val['GLOBALS'])) { - $val = '[recursion]'; - } elseif (array_key_exists($key, $outputMask)) { - $val = (string)$outputMask[$key]; + if (array_key_exists($key, $outputMask)) { + $node = new ScalarNode('string', $outputMask[$key]); } elseif ($val !== $var) { - $val = static::_export($val, $depth, $indent); + // Dump all the items without increasing depth. + $node = static::export($val, $context); + } else { + // Likely recursion, so we increase depth. + $node = static::export($val, $context->withAddedDepth()); } - $vars[] = $break . static::exportVar($key) . - ' => ' . - $val; + $items[] = new ArrayItemNode(static::export($key, $context), $node); } } else { - $vars[] = $break . '[maximum depth reached]'; + $items[] = new ArrayItemNode( + new ScalarNode('string', ''), + new SpecialNode('[maximum depth reached]') + ); } - return $out . implode(',', $vars) . $end . ']'; + return new ArrayNode($items); } /** - * Handles object to string conversion. + * Handles object to node conversion. * * @param object $var Object to convert. - * @param int $depth The current depth, used for tracking recursion. - * @param int $indent The current indentation level. - * @return string + * @param \Cake\Error\Debug\DebugContext $context The dump context. + * @return \Cake\Error\Debug\NodeInterface * @see \Cake\Error\Debugger::exportVar() */ - protected static function _object(object $var, int $depth, int $indent): string + protected static function exportObject(object $var, DebugContext $context): NodeInterface { - $out = ''; - $props = []; + $isRef = $context->hasReference($var); + $refNum = $context->getReferenceId($var); $className = get_class($var); - $out .= 'object(' . $className . ') {'; - $break = "\n" . str_repeat("\t", $indent); - $end = "\n" . str_repeat("\t", $indent - 1); - - if ($depth > 0 && method_exists($var, '__debugInfo')) { - try { - return $out . "\n" . - substr(static::_array($var->__debugInfo(), $depth - 1, $indent), 1, -1) . - $end . '}'; - } catch (Exception $e) { - $message = $e->getMessage(); - - return $out . "\n(unable to export object: $message)\n }"; - } + if ($isRef) { + return new ReferenceNode($className, $refNum); } + $node = new ClassNode($className, $refNum); + + $remaining = $context->remainingDepth(); + if ($remaining > 0) { + if (method_exists($var, '__debugInfo')) { + try { + foreach ($var->__debugInfo() as $key => $val) { + $node->addProperty(new PropertyNode("'{$key}'", null, static::export($val, $context))); + } - if ($depth > 0) { - $outputMask = (array)static::outputMask(); + return $node; + } catch (Exception $e) { + return new SpecialNode("(unable to export object: {$e->getMessage()})"); + } + } + + $outputMask = static::outputMask(); $objectVars = get_object_vars($var); foreach ($objectVars as $key => $value) { - $value = array_key_exists($key, $outputMask) - ? $outputMask[$key] - : static::_export($value, $depth - 1, $indent); - $props[] = "$key => " . $value; + if (array_key_exists($key, $outputMask)) { + $value = $outputMask[$key]; + } + /** @psalm-suppress RedundantCast */ + $node->addProperty( + new PropertyNode((string)$key, 'public', static::export($value, $context->withAddedDepth())) + ); } $ref = new ReflectionObject($var); @@ -633,36 +772,28 @@ protected static function _object(object $var, int $depth, int $indent): string foreach ($filters as $filter => $visibility) { $reflectionProperties = $ref->getProperties($filter); foreach ($reflectionProperties as $reflectionProperty) { - $value = null; $reflectionProperty->setAccessible(true); if ( method_exists($reflectionProperty, 'isInitialized') && !$reflectionProperty->isInitialized($var) ) { - $value = '[uninitialized]'; + $value = new SpecialNode('[uninitialized]'); + } else { + $value = static::export($reflectionProperty->getValue($var), $context->withAddedDepth()); } - - if ($value === null) { - $property = $reflectionProperty->getValue($var); - $value = static::_export($property, $depth - 1, $indent); - } - - $key = $reflectionProperty->name; - $props[] = sprintf( - '[%s] %s => %s', - $visibility, - $key, - array_key_exists($key, $outputMask) ? $outputMask[$key] : $value + $node->addProperty( + new PropertyNode( + $reflectionProperty->getName(), + $visibility, + $value + ) ); } } - - $out .= $break . implode($break, $props) . $end; } - $out .= '}'; - return $out; + return $node; } /** @@ -713,7 +844,7 @@ public static function setOutputFormat(string $format): void * Gets the following templates: `id`, `context` * - 'links' - An array of HTML links that are used for creating links to other resources. * Typically this is used to create javascript links to open other sections. - * Link keys, are: `code`, `context`, `help`. See the js output format for an + * Link keys, are: `code`, `context`, `help`. See the JS output format for an * example. * - 'traceLine' - Used for creating lines in the stacktrace. Gets the following * template variables: `reference`, `path`, `line` @@ -817,7 +948,6 @@ public function outputError(array $data): void } if (!empty($tpl['escapeContext'])) { - $context = h($context); $data['description'] = h($data['description']); } @@ -834,7 +964,7 @@ public function outputError(array $data): void $links = implode(' ', $links); if (isset($tpl['callback']) && is_callable($tpl['callback'])) { - call_user_func($tpl['callback'], $data, compact('links', 'info')); + $tpl['callback']($data, compact('links', 'info')); return; } @@ -850,32 +980,21 @@ public function outputError(array $data): void */ public static function getType($var): string { - if (is_object($var)) { - return get_class($var); - } - if ($var === null) { + $type = getTypeName($var); + + if ($type === 'NULL') { return 'null'; } - if (is_string($var)) { - return 'string'; - } - if (is_array($var)) { - return 'array'; - } - if (is_int($var)) { - return 'integer'; - } - if (is_bool($var)) { - return 'boolean'; - } - if (is_float($var)) { + + if ($type === 'double') { return 'float'; } - if (is_resource($var)) { - return 'resource'; + + if ($type === 'unknown type') { + return 'unknown'; } - return 'unknown'; + return $type; } /** @@ -885,59 +1004,31 @@ public static function getType($var): string * @param array $location If contains keys "file" and "line" their values will * be used to show location info. * @param bool|null $showHtml If set to true, the method prints the debug - * data in a browser-friendly way. + * data encoded as HTML. If false, plain text formatting will be used. + * If null, the format will be chosen based on the configured exportFormatter, or + * environment conditions. * @return void */ public static function printVar($var, array $location = [], ?bool $showHtml = null): void { $location += ['file' => null, 'line' => null]; - $file = $location['file']; - $line = $location['line']; - $lineInfo = ''; - if ($file) { - $search = []; - if (defined('ROOT')) { - $search = [ROOT]; - } - if (defined('CAKE_CORE_INCLUDE_PATH')) { - array_unshift($search, CAKE_CORE_INCLUDE_PATH); - } - $file = str_replace($search, '', $file); - } - $html = << -%s -
-%s
-
- -HTML; - $text = <<getConfig('exportFormatter'); + $debugger->setConfig('exportFormatter', $showHtml ? HtmlFormatter::class : TextFormatter::class); } - $var = Debugger::exportVar($var, 25); - if ($showHtml) { - $template = $html; - $var = h($var); - if ($file && $line) { - $lineInfo = sprintf('%s (line %s)', $file, $line); - } + $contents = static::exportVar($var, 25); + $formatter = $debugger->getExportFormatter(); + + if ($restore) { + $debugger->setConfig('exportFormatter', $restore); } - printf($template, $lineInfo, $var); + echo $formatter->formatWrapper($contents, $location); } /** @@ -968,12 +1059,13 @@ public static function formatHtmlMessage(string $message): string */ public static function checkSecurityKeys(): void { - if (Security::getSalt() === '__SALT__') { - trigger_error(sprintf( - 'Please change the value of %s in %s to a salt value specific to your application.', - '\'Security.salt\'', - 'ROOT/config/app.php' - ), E_USER_NOTICE); + $salt = Security::getSalt(); + if ($salt === '__SALT__' || strlen($salt) < 32) { + trigger_error( + 'Please change the value of `Security.salt` in `ROOT/config/app_local.php` ' . + 'to a random value of at least 32 characters.', + E_USER_NOTICE + ); } } } diff --git a/app/vendor/cakephp/cakephp/src/Error/ErrorLogger.php b/app/vendor/cakephp/cakephp/src/Error/ErrorLogger.php index 06380ab03..21285bd5d 100644 --- a/app/vendor/cakephp/cakephp/src/Error/ErrorLogger.php +++ b/app/vendor/cakephp/cakephp/src/Error/ErrorLogger.php @@ -17,7 +17,7 @@ namespace Cake\Error; use Cake\Core\Configure; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Core\InstanceConfigTrait; use Cake\Log\Log; use Psr\Http\Message\ServerRequestInterface; @@ -26,7 +26,7 @@ /** * Log errors and unhandled exceptions to `Cake\Log\Log` */ -class ErrorLogger +class ErrorLogger implements ErrorLoggerInterface { use InstanceConfigTrait; @@ -55,11 +55,22 @@ public function __construct(array $config = []) } /** - * Generate the error log message. - * - * @param \Throwable $exception The exception to log a message for. - * @param \Psr\Http\Message\ServerRequestInterface|null $request The current request if available. - * @return bool + * @inheritDoc + */ + public function logMessage($level, string $message, array $context = []): bool + { + if (!empty($context['request'])) { + $message .= $this->getRequestContext($context['request']); + } + if (!empty($context['trace'])) { + $message .= "\nTrace:\n" . $context['trace'] . "\n"; + } + + return Log::write($level, $message); + } + + /** + * @inheritDoc */ public function log(Throwable $exception, ?ServerRequestInterface $request = null): bool { @@ -99,7 +110,7 @@ protected function getMessage(Throwable $exception, bool $isPrevious = false): s ); $debug = Configure::read('debug'); - if ($debug && $exception instanceof Exception) { + if ($debug && $exception instanceof CakeException) { $attributes = $exception->getAttributes(); if ($attributes) { $message .= "\nException Attributes: " . var_export($exception->getAttributes(), true); diff --git a/app/vendor/cakephp/cakephp/src/Error/ErrorLoggerInterface.php b/app/vendor/cakephp/cakephp/src/Error/ErrorLoggerInterface.php new file mode 100644 index 000000000..b8ee57c23 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Error/ErrorLoggerInterface.php @@ -0,0 +1,51 @@ +, int> + */ + protected $exceptionHttpCodes = [ + // Controller exceptions + MissingActionException::class => 404, + // Datasource exceptions + PageOutOfBoundsException::class => 404, + RecordNotFoundException::class => 404, + // Http exceptions + MissingControllerException::class => 404, + // Routing exceptions + MissingRouteException::class => 404, + ]; + /** * Creates the controller to perform rendering on the error response. - * If the error is a Cake\Core\Exception\Exception it will be converted to either a 400 or a 500 - * code error depending on the code used to construct the error. * * @param \Throwable $exception Exception. * @param \Cake\Http\ServerRequest $request The request if this is set it will be used @@ -136,7 +161,7 @@ protected function _getController(): Controller $params = $request->getAttribute('params'); $params['controller'] = 'Error'; - $factory = new ControllerFactory(); + $factory = new ControllerFactory(new Container()); $class = $factory->getControllerClass($request->withAttribute('params', $params)); if (!$class) { @@ -192,7 +217,7 @@ protected function clearOutput(): void public function render(): ResponseInterface { $exception = $this->error; - $code = $this->_code($exception); + $code = $this->getHttpCode($exception); $method = $this->_method($exception); $template = $this->_template($exception, $method, $code); $this->clearOutput(); @@ -206,10 +231,16 @@ public function render(): ResponseInterface $response = $this->controller->getResponse(); if ($exception instanceof CakeException) { + /** @psalm-suppress DeprecatedMethod */ foreach ((array)$exception->responseHeader() as $key => $value) { $response = $response->withHeader($key, $value); } } + if ($exception instanceof HttpException) { + foreach ($exception->getHeaders() as $name => $value) { + $response = $response->withHeader($name, $value); + } + } $response = $response->withStatus($code); $viewVars = [ @@ -257,7 +288,7 @@ public function render(): ResponseInterface */ protected function _customMethod(string $method, Throwable $exception): Response { - $result = call_user_func([$this, $method], $exception); + $result = $this->{$method}($exception); $this->_shutdown(); if (is_string($result)) { $result = $this->controller->getResponse()->withStringBody($result); @@ -280,7 +311,8 @@ protected function _method(Throwable $exception): string $baseClass = substr($baseClass, 0, -9); } - $method = Inflector::variable($baseClass) ?: 'error500'; + // $baseClass would be an empty string if the exception class is \Exception. + $method = $baseClass === '' ? 'error500' : Inflector::variable($baseClass); return $this->method = $method; } @@ -320,41 +352,30 @@ protected function _message(Throwable $exception, int $code): string */ protected function _template(Throwable $exception, string $method, int $code): string { - $isHttpException = $exception instanceof HttpException; - - if (!Configure::read('debug') && !$isHttpException || $isHttpException) { - $template = 'error500'; - if ($code < 500) { - $template = 'error400'; - } - - return $this->template = $template; + if ($exception instanceof HttpException || !Configure::read('debug')) { + return $this->template = $code < 500 ? 'error400' : 'error500'; } - $template = $method ?: 'error500'; - if ($exception instanceof PDOException) { - $template = 'pdo_error'; + return $this->template = 'pdo_error'; } - return $this->template = $template; + return $this->template = $method; } /** - * Get HTTP status code. + * Gets the appropriate http status code for exception. * * @param \Throwable $exception Exception. - * @return int A valid HTTP error status code. + * @return int A valid HTTP status code. */ - protected function _code(Throwable $exception): int + protected function getHttpCode(Throwable $exception): int { - $code = 500; - $errorCode = (int)$exception->getCode(); - if ($errorCode >= 400 && $errorCode < 600) { - $code = $errorCode; + if ($exception instanceof HttpException) { + return $exception->getCode(); } - return $code; + return $this->exceptionHttpCodes[get_class($exception)] ?? 500; } /** diff --git a/app/vendor/cakephp/cakephp/src/Error/FatalErrorException.php b/app/vendor/cakephp/cakephp/src/Error/FatalErrorException.php index 396317086..42f751850 100644 --- a/app/vendor/cakephp/cakephp/src/Error/FatalErrorException.php +++ b/app/vendor/cakephp/cakephp/src/Error/FatalErrorException.php @@ -14,13 +14,13 @@ */ namespace Cake\Error; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Throwable; /** * Represents a fatal error */ -class FatalErrorException extends Exception +class FatalErrorException extends CakeException { /** * Constructor diff --git a/app/vendor/cakephp/cakephp/src/Error/Middleware/ErrorHandlerMiddleware.php b/app/vendor/cakephp/cakephp/src/Error/Middleware/ErrorHandlerMiddleware.php index c24867214..8f2ea7967 100644 --- a/app/vendor/cakephp/cakephp/src/Error/Middleware/ErrorHandlerMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/Error/Middleware/ErrorHandlerMiddleware.php @@ -17,11 +17,14 @@ namespace Cake\Error\Middleware; use Cake\Core\App; +use Cake\Core\Configure; use Cake\Core\InstanceConfigTrait; use Cake\Error\ErrorHandler; use Cake\Error\ExceptionRenderer; +use Cake\Http\Exception\RedirectException; use Cake\Http\Response; use InvalidArgumentException; +use Laminas\Diactoros\Response\RedirectResponse; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; @@ -90,6 +93,10 @@ public function __construct($errorHandler = []) $errorHandler = func_get_arg(1); } + if (PHP_VERSION_ID >= 70400 && Configure::read('debug')) { + ini_set('zend.exception_ignore_args', '0'); + } + if (is_array($errorHandler)) { $this->setConfig($errorHandler); @@ -117,6 +124,8 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface { try { return $handler->handle($request); + } catch (RedirectException $exception) { + return $this->handleRedirect($exception); } catch (Throwable $exception) { return $this->handleException($exception, $request); } @@ -135,8 +144,8 @@ public function handleException(Throwable $exception, ServerRequestInterface $re $renderer = $errorHandler->getRenderer($exception, $request); try { - $response = $renderer->render(); $errorHandler->logException($exception, $request); + $response = $renderer->render(); } catch (Throwable $internalException) { $errorHandler->logException($internalException, $request); $response = $this->handleInternalError(); @@ -145,6 +154,21 @@ public function handleException(Throwable $exception, ServerRequestInterface $re return $response; } + /** + * Convert a redirect exception into a response. + * + * @param \Cake\Http\Exception\RedirectException $exception The exception to handle + * @return \Psr\Http\Message\ResponseInterface Response created from the redirect. + */ + public function handleRedirect(RedirectException $exception): ResponseInterface + { + return new RedirectResponse( + $exception->getMessage(), + $exception->getCode(), + $exception->getHeaders() + ); + } + /** * Handle internal errors. * diff --git a/app/vendor/cakephp/cakephp/src/Event/Decorator/SubjectFilterDecorator.php b/app/vendor/cakephp/cakephp/src/Event/Decorator/SubjectFilterDecorator.php index a591e483e..b802ed1ab 100644 --- a/app/vendor/cakephp/cakephp/src/Event/Decorator/SubjectFilterDecorator.php +++ b/app/vendor/cakephp/cakephp/src/Event/Decorator/SubjectFilterDecorator.php @@ -16,7 +16,7 @@ */ namespace Cake\Event\Decorator; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Event\EventInterface; use RuntimeException; @@ -61,7 +61,7 @@ public function canTrigger(EventInterface $event): bool try { $subject = $event->getSubject(); - } catch (Exception $e) { + } catch (CakeException $e) { return false; } diff --git a/app/vendor/cakephp/cakephp/src/Event/Event.php b/app/vendor/cakephp/cakephp/src/Event/Event.php index 36c34a99e..75084ca31 100644 --- a/app/vendor/cakephp/cakephp/src/Event/Event.php +++ b/app/vendor/cakephp/cakephp/src/Event/Event.php @@ -16,7 +16,7 @@ */ namespace Cake\Event; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Class Event @@ -103,14 +103,14 @@ public function getName(): string * If the event has no subject an exception will be raised. * * @return object - * @throws \Cake\Core\Exception\Exception + * @throws \Cake\Core\Exception\CakeException * @psalm-return TSubject * @psalm-suppress LessSpecificImplementedReturnType */ public function getSubject() { if ($this->_subject === null) { - throw new Exception('No subject set for this event'); + throw new CakeException('No subject set for this event'); } return $this->_subject; @@ -172,6 +172,7 @@ public function getData(?string $key = null) return $this->_data[$key] ?? null; } + /** @psalm-suppress RedundantCastGivenDocblockType */ return (array)$this->_data; } diff --git a/app/vendor/cakephp/cakephp/src/Event/EventDispatcherTrait.php b/app/vendor/cakephp/cakephp/src/Event/EventDispatcherTrait.php index 4c314cac6..e9b4dd963 100644 --- a/app/vendor/cakephp/cakephp/src/Event/EventDispatcherTrait.php +++ b/app/vendor/cakephp/cakephp/src/Event/EventDispatcherTrait.php @@ -25,7 +25,7 @@ trait EventDispatcherTrait * Instance of the Cake\Event\EventManager this object is using * to dispatch inner events. * - * @var \Cake\Event\EventManagerInterface + * @var \Cake\Event\EventManagerInterface|null */ protected $_eventManager; diff --git a/app/vendor/cakephp/cakephp/src/Event/EventManager.php b/app/vendor/cakephp/cakephp/src/Event/EventManager.php index f7dd1d14e..e65ab1378 100644 --- a/app/vendor/cakephp/cakephp/src/Event/EventManager.php +++ b/app/vendor/cakephp/cakephp/src/Event/EventManager.php @@ -16,7 +16,7 @@ */ namespace Cake\Event; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * The event manager is responsible for keeping track of event listeners, passing the correct @@ -36,7 +36,7 @@ class EventManager implements EventManagerInterface /** * The globally available instance, used for dispatching events attached from any scope * - * @var \Cake\Event\EventManager + * @var \Cake\Event\EventManager|null */ protected static $_generalManager; @@ -130,7 +130,7 @@ public function on($eventKey, $options = [], ?callable $callable = null) */ protected function _attachSubscriber(EventListenerInterface $subscriber): void { - foreach ((array)$subscriber->implementedEvents() as $eventKey => $function) { + foreach ($subscriber->implementedEvents() as $eventKey => $function) { $options = []; $method = $function; if (is_array($function) && isset($function['callable'])) { @@ -184,7 +184,7 @@ public function off($eventKey, $callable = null) if (!is_string($eventKey)) { if (!is_callable($eventKey)) { - throw new Exception( + throw new CakeException( 'First argument of EventManager::off() must be ' . ' string or EventListenerInterface instance or callable.' ); @@ -234,7 +234,7 @@ public function off($eventKey, $callable = null) */ protected function _detachSubscriber(EventListenerInterface $subscriber, ?string $eventKey = null): void { - $events = (array)$subscriber->implementedEvents(); + $events = $subscriber->implementedEvents(); if (!empty($eventKey) && empty($events[$eventKey])) { return; } @@ -406,7 +406,7 @@ public function addEventToList(EventInterface $event) */ public function trackEvents(bool $enabled) { - $this->_trackEvents = (bool)$enabled; + $this->_trackEvents = $enabled; return $this; } @@ -472,7 +472,7 @@ public function __debugInfo(): array try { $subject = $event->getSubject(); $properties['_dispatchedEvents'][] = $event->getName() . ' with subject ' . get_class($subject); - } catch (Exception $e) { + } catch (CakeException $e) { $properties['_dispatchedEvents'][] = $event->getName() . ' with no subject'; } } diff --git a/app/vendor/cakephp/cakephp/src/Event/LICENSE.txt b/app/vendor/cakephp/cakephp/src/Event/LICENSE.txt index 0b3b94303..b938c9e8e 100644 --- a/app/vendor/cakephp/cakephp/src/Event/LICENSE.txt +++ b/app/vendor/cakephp/cakephp/src/Event/LICENSE.txt @@ -1,7 +1,7 @@ The MIT License (MIT) CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) -Copyright (c) 2005-2019, Cake Software Foundation, Inc. (https://cakefoundation.org) +Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/vendor/cakephp/cakephp/src/Filesystem/Filesystem.php b/app/vendor/cakephp/cakephp/src/Filesystem/Filesystem.php index 90ef244e6..d1ecebc3b 100644 --- a/app/vendor/cakephp/cakephp/src/Filesystem/Filesystem.php +++ b/app/vendor/cakephp/cakephp/src/Filesystem/Filesystem.php @@ -16,7 +16,7 @@ */ namespace Cake\Filesystem; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use CallbackFilterIterator; use FilesystemIterator; use Iterator; @@ -128,7 +128,7 @@ protected function filterIterator(Iterator $iterator, $filter): Iterator * @param string $filename File path. * @param string $content Content to dump. * @return void - * @throws \Cake\Core\Exception\Exception When dumping fails. + * @throws \Cake\Core\Exception\CakeException When dumping fails. */ public function dumpFile(string $filename, string $content): void { @@ -148,7 +148,7 @@ public function dumpFile(string $filename, string $content): void } if ($success === false) { - throw new Exception(sprintf('Failed dumping content to file `%s`', $dir)); + throw new CakeException(sprintf('Failed dumping content to file `%s`', $dir)); } if (!$exists) { @@ -162,7 +162,7 @@ public function dumpFile(string $filename, string $content): void * @param string $dir Directory path. * @param int $mode Octal mode passed to mkdir(). Defaults to 0755. * @return void - * @throws \Cake\Core\Exception\Exception When directory creation fails. + * @throws \Cake\Core\Exception\CakeException When directory creation fails. */ public function mkdir(string $dir, int $mode = 0755): void { @@ -174,7 +174,7 @@ public function mkdir(string $dir, int $mode = 0755): void // phpcs:ignore if (@mkdir($dir, $mode, true) === false) { umask($old); - throw new Exception(sprintf('Failed to create directory "%s"', $dir)); + throw new CakeException(sprintf('Failed to create directory "%s"', $dir)); } umask($old); @@ -185,7 +185,7 @@ public function mkdir(string $dir, int $mode = 0755): void * * @param string $path Directory path. * @return bool - * @throws \Cake\Core\Exception\Exception If path is not a directory. + * @throws \Cake\Core\Exception\CakeException If path is not a directory. */ public function deleteDir(string $path): bool { @@ -194,7 +194,7 @@ public function deleteDir(string $path): bool } if (!is_dir($path)) { - throw new Exception(sprintf('"%s" is not a directory', $path)); + throw new CakeException(sprintf('"%s" is not a directory', $path)); } $iterator = new RecursiveIteratorIterator( @@ -204,16 +204,24 @@ public function deleteDir(string $path): bool $result = true; foreach ($iterator as $fileInfo) { - if ($fileInfo->getType() === self::TYPE_DIR) { + $isWindowsLink = DIRECTORY_SEPARATOR === '\\' && $fileInfo->getType() === 'link'; + if ($fileInfo->getType() === self::TYPE_DIR || $isWindowsLink) { // phpcs:ignore $result = $result && @rmdir($fileInfo->getPathname()); + unset($fileInfo); continue; } // phpcs:ignore $result = $result && @unlink($fileInfo->getPathname()); + // possible inner iterators need to be unset too in order for locks on parents to be released + unset($fileInfo); } + // unsetting iterators helps releasing possible locks in certain environments, + // which could otherwise make `rmdir()` fail + unset($iterator); + // phpcs:ignore $result = $result && @rmdir($path); diff --git a/app/vendor/cakephp/cakephp/src/Filesystem/Folder.php b/app/vendor/cakephp/cakephp/src/Filesystem/Folder.php index a8cdac495..83da3d71c 100644 --- a/app/vendor/cakephp/cakephp/src/Filesystem/Folder.php +++ b/app/vendor/cakephp/cakephp/src/Filesystem/Folder.php @@ -558,6 +558,8 @@ public function tree(?string $path = null, $exceptions = false, ?string $type = ); $iterator = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::SELF_FIRST); } catch (Exception $e) { + unset($directory, $iterator); + if ($type === null) { return [[], []]; } @@ -573,12 +575,14 @@ public function tree(?string $path = null, $exceptions = false, ?string $type = if ($skipHidden) { $subPathName = $fsIterator->getSubPathname(); if ($subPathName[0] === '.' || strpos($subPathName, DIRECTORY_SEPARATOR . '.') !== false) { + unset($fsIterator); continue; } } /** @var \FilesystemIterator $item */ $item = $fsIterator->current(); if (!empty($exceptions) && isset($exceptions[$item->getFilename()])) { + unset($fsIterator, $item); continue; } @@ -587,7 +591,15 @@ public function tree(?string $path = null, $exceptions = false, ?string $type = } elseif ($item->isDir() && !$item->isDot()) { $directories[] = $itemPath; } + + // inner iterators need to be unset too in order for locks on parents to be released + unset($fsIterator, $item); } + + // unsetting iterators helps releasing possible locks in certain environments, + // which could otherwise make `rmdir()` fail + unset($directory, $iterator); + if ($type === null) { return [$directories, $files]; } @@ -707,6 +719,8 @@ public function delete(?string $path = null): bool $directory = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::CURRENT_AS_SELF); $iterator = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::CHILD_FIRST); } catch (Exception $e) { + unset($directory, $iterator); + return false; } @@ -728,11 +742,20 @@ public function delete(?string $path = null): bool } else { $this->_errors[] = sprintf('%s NOT removed', $filePath); + unset($directory, $iterator, $item); + return false; } } + + // inner iterators need to be unset too in order for locks on parents to be released + unset($item); } + // unsetting iterators helps releasing possible locks in certain environments, + // which could otherwise make `rmdir()` fail + unset($directory, $iterator); + $path = rtrim($path, DIRECTORY_SEPARATOR); // phpcs:disable if (@rmdir($path)) { diff --git a/app/vendor/cakephp/cakephp/src/Filesystem/LICENSE.txt b/app/vendor/cakephp/cakephp/src/Filesystem/LICENSE.txt index 0b3b94303..b938c9e8e 100644 --- a/app/vendor/cakephp/cakephp/src/Filesystem/LICENSE.txt +++ b/app/vendor/cakephp/cakephp/src/Filesystem/LICENSE.txt @@ -1,7 +1,7 @@ The MIT License (MIT) CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) -Copyright (c) 2005-2019, Cake Software Foundation, Inc. (https://cakefoundation.org) +Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/vendor/cakephp/cakephp/src/Filesystem/README.md b/app/vendor/cakephp/cakephp/src/Filesystem/README.md index df1862ebb..ce2fa5c15 100644 --- a/app/vendor/cakephp/cakephp/src/Filesystem/README.md +++ b/app/vendor/cakephp/cakephp/src/Filesystem/README.md @@ -1,7 +1,7 @@ [![Total Downloads](https://img.shields.io/packagist/dt/cakephp/filesystem.svg?style=flat-square)](https://packagist.org/packages/cakephp/filesystem) [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE.txt) -# This package is deprecated. +# This package has been deprecated. ## CakePHP Filesystem Library diff --git a/app/vendor/cakephp/cakephp/src/Form/Form.php b/app/vendor/cakephp/cakephp/src/Form/Form.php index 530651b5e..41563e253 100644 --- a/app/vendor/cakephp/cakephp/src/Form/Form.php +++ b/app/vendor/cakephp/cakephp/src/Form/Form.php @@ -75,7 +75,7 @@ class Form implements EventListenerInterface, EventDispatcherInterface, Validato /** * The schema used by this form. * - * @var \Cake\Form\Schema + * @var \Cake\Form\Schema|null */ protected $_schema; @@ -136,6 +136,39 @@ public function implementedEvents(): array return []; } + /** + * Set the schema for this form. + * + * @since 4.1.0 + * @param \Cake\Form\Schema $schema The schema to set + * @return $this + */ + public function setSchema(Schema $schema) + { + $this->_schema = $schema; + + return $this; + } + + /** + * Get the schema for this form. + * + * This method will call `_buildSchema()` when the schema + * is first built. This hook method lets you configure the + * schema or load a pre-defined one. + * + * @since 4.1.0 + * @return \Cake\Form\Schema the schema instance. + */ + public function getSchema(): Schema + { + if ($this->_schema === null) { + $this->_schema = $this->_buildSchema(new $this->_schemaClass()); + } + + return $this->_schema; + } + /** * Get/Set the schema for this form. * @@ -143,19 +176,18 @@ public function implementedEvents(): array * is first built. This hook method lets you configure the * schema or load a pre-defined one. * + * @deprecated 4.1.0 Use {@link setSchema()}/{@link getSchema()} instead. * @param \Cake\Form\Schema|null $schema The schema to set, or null. * @return \Cake\Form\Schema the schema instance. */ public function schema(?Schema $schema = null): Schema { - if ($schema === null && empty($this->_schema)) { - $schema = $this->_buildSchema(new $this->_schemaClass()); - } - if ($schema) { - $this->_schema = $schema; + deprecationWarning('Form::schema() is deprecated. Use setSchema() and getSchema() instead.'); + if ($schema !== null) { + $this->setSchema($schema); } - return $this->_schema; + return $this->getSchema(); } /** @@ -235,6 +267,8 @@ public function setErrors(array $errors) */ public function execute(array $data): bool { + $this->_data = $data; + if (!$this->validate($data)) { return false; } @@ -271,6 +305,29 @@ public function getData(?string $field = null) return Hash::get($this->_data, $field); } + /** + * Saves a variable or an associative array of variables for use inside form data. + * + * @param string|array $name The key to write, can be a dot notation value. + * Alternatively can be an array containing key(s) and value(s). + * @param mixed $value Value to set for var + * @return $this + */ + public function set($name, $value = null) + { + $write = $name; + if (!is_array($name)) { + $write = [$name => $value]; + } + + /** @psalm-suppress PossiblyInvalidIterator */ + foreach ($write as $key => $val) { + $this->_data = Hash::insert($this->_data, $key, $val); + } + + return $this; + } + /** * Set form data. * @@ -292,7 +349,7 @@ public function setData(array $data) public function __debugInfo(): array { $special = [ - '_schema' => $this->schema()->__debugInfo(), + '_schema' => $this->getSchema()->__debugInfo(), '_errors' => $this->getErrors(), '_validator' => $this->getValidator()->__debugInfo(), ]; diff --git a/app/vendor/cakephp/cakephp/src/Form/FormProtector.php b/app/vendor/cakephp/cakephp/src/Form/FormProtector.php index e981f096d..e0776ce88 100644 --- a/app/vendor/cakephp/cakephp/src/Form/FormProtector.php +++ b/app/vendor/cakephp/cakephp/src/Form/FormProtector.php @@ -533,7 +533,7 @@ protected function matchExistingFields( $messages = []; foreach ($dataFields as $key => $value) { if (is_int($key)) { - $foundKey = array_search($value, (array)$expectedFields, true); + $foundKey = array_search($value, $expectedFields, true); if ($foundKey === false) { $messages[] = sprintf($intKeyMessage, $value); } else { @@ -564,7 +564,7 @@ protected function debugExpectedFields(array $expectedFields = [], string $missi } $expectedFieldNames = []; - foreach ((array)$expectedFields as $key => $expectedField) { + foreach ($expectedFields as $key => $expectedField) { if (is_int($key)) { $expectedFieldNames[] = $expectedField; } else { diff --git a/app/vendor/cakephp/cakephp/src/Form/LICENSE.txt b/app/vendor/cakephp/cakephp/src/Form/LICENSE.txt index 0b3b94303..b938c9e8e 100644 --- a/app/vendor/cakephp/cakephp/src/Form/LICENSE.txt +++ b/app/vendor/cakephp/cakephp/src/Form/LICENSE.txt @@ -1,7 +1,7 @@ The MIT License (MIT) CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) -Copyright (c) 2005-2019, Cake Software Foundation, Inc. (https://cakefoundation.org) +Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/vendor/cakephp/cakephp/src/Http/BaseApplication.php b/app/vendor/cakephp/cakephp/src/Http/BaseApplication.php index 02109f9b6..f6c9867a0 100644 --- a/app/vendor/cakephp/cakephp/src/Http/BaseApplication.php +++ b/app/vendor/cakephp/cakephp/src/Http/BaseApplication.php @@ -14,11 +14,16 @@ * @since 3.3.0 * @license https://opensource.org/licenses/mit-license.php MIT License */ + namespace Cake\Http; use Cake\Console\CommandCollection; use Cake\Controller\ControllerFactory; use Cake\Core\ConsoleApplicationInterface; +use Cake\Core\Container; +use Cake\Core\ContainerApplicationInterface; +use Cake\Core\ContainerInterface; +use Cake\Core\Exception\MissingPluginException; use Cake\Core\HttpApplicationInterface; use Cake\Core\Plugin; use Cake\Core\PluginApplicationInterface; @@ -45,6 +50,7 @@ */ abstract class BaseApplication implements ConsoleApplicationInterface, + ContainerApplicationInterface, HttpApplicationInterface, PluginApplicationInterface, RoutingApplicationInterface @@ -70,6 +76,13 @@ abstract class BaseApplication implements */ protected $controllerFactory; + /** + * Container + * + * @var \Cake\Core\ContainerInterface|null + */ + protected $container; + /** * Constructor * @@ -121,6 +134,26 @@ public function addPlugin($name, array $config = []) return $this; } + /** + * Add an optional plugin + * + * If it isn't available, ignore it. + * + * @param string|\Cake\Core\PluginInterface $name The plugin name or plugin object. + * @param array $config The configuration data for the plugin if using a string for $name + * @return $this + */ + public function addOptionalPlugin($name, array $config = []) + { + try { + $this->addPlugin($name, $config); + } catch (MissingPluginException $e) { + // Do not halt if the plugin is missing + } + + return $this; + } + /** * Get the plugin collection in use. * @@ -203,6 +236,57 @@ public function pluginConsole(CommandCollection $commands): CommandCollection return $commands; } + /** + * Get the dependency injection container for the application. + * + * The first time the container is fetched it will be constructed + * and stored for future calls. + * + * @return \Cake\Core\ContainerInterface + */ + public function getContainer(): ContainerInterface + { + if ($this->container === null) { + $this->container = $this->buildContainer(); + } + + return $this->container; + } + + /** + * Build the service container + * + * Override this method if you need to use a custom container or + * want to change how the container is built. + * + * @return \Cake\Core\ContainerInterface + */ + protected function buildContainer(): ContainerInterface + { + $container = new Container(); + $this->services($container); + foreach ($this->plugins->with('services') as $plugin) { + $plugin->services($container); + } + + $event = $this->dispatchEvent('Application.buildContainer', ['container' => $container]); + if ($event->getResult() instanceof ContainerInterface) { + return $event->getResult(); + } + + return $container; + } + + /** + * Register application container services. + * + * @param \Cake\Core\ContainerInterface $container The Container to update. + * @return void + */ + public function services(ContainerInterface $container): void + { + } + /** * Invoke the application. * @@ -217,7 +301,7 @@ public function handle( ServerRequestInterface $request ): ResponseInterface { if ($this->controllerFactory === null) { - $this->controllerFactory = new ControllerFactory(); + $this->controllerFactory = new ControllerFactory($this->getContainer()); } if (Router::getRequest() !== $request) { diff --git a/app/vendor/cakephp/cakephp/src/Http/CallbackStream.php b/app/vendor/cakephp/cakephp/src/Http/CallbackStream.php index 4f930e276..4ceed68e4 100644 --- a/app/vendor/cakephp/cakephp/src/Http/CallbackStream.php +++ b/app/vendor/cakephp/cakephp/src/Http/CallbackStream.php @@ -40,7 +40,8 @@ public function getContents(): string { $callback = $this->detach(); $result = ''; - if (is_callable($callback)) { + /** @psalm-suppress TypeDoesNotContainType */ + if ($callback !== null) { $result = $callback(); } if (!is_string($result)) { diff --git a/app/vendor/cakephp/cakephp/src/Http/Client.php b/app/vendor/cakephp/cakephp/src/Http/Client.php index 3a1e2d83d..277911405 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Client.php +++ b/app/vendor/cakephp/cakephp/src/Http/Client.php @@ -16,7 +16,7 @@ namespace Cake\Http; use Cake\Core\App; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Core\InstanceConfigTrait; use Cake\Http\Client\Adapter\Curl; use Cake\Http\Client\Adapter\Stream; @@ -114,6 +114,7 @@ class Client implements ClientInterface 'host' => null, 'port' => null, 'scheme' => 'http', + 'basePath' => '', 'timeout' => 30, 'ssl_verify_peer' => true, 'ssl_verify_peer_name' => true, @@ -150,6 +151,7 @@ class Client implements ClientInterface * - host - The hostname to do requests on. * - port - The port to use. * - scheme - The default scheme/protocol to use. Defaults to http. + * - basePath - A path to append to the domain to use. (/api/v1/) * - timeout - The timeout in seconds. Defaults to 30 * - ssl_verify_peer - Whether or not SSL certificates should be validated. * Defaults to true. @@ -200,6 +202,38 @@ public function __construct(array $config = []) } } + /** + * Client instance returned is scoped to the domain, port, and scheme parsed from the passed URL string. The passed + * string must have a scheme and a domain. Optionally, if a port is included in the string, the port will be scoped + * too. If a path is included in the URL, the client instance will build urls with it prepended. + * Other parts of the url string are ignored. + * + * @param string $url A string URL e.g. https://example.com + * @return static + * @throws \InvalidArgumentException + */ + public static function createFromUrl(string $url) + { + $parts = parse_url($url); + + if ($parts === false) { + throw new InvalidArgumentException('String ' . $url . ' did not parse'); + } + + $config = array_intersect_key($parts, ['scheme' => '', 'port' => '', 'host' => '', 'path' => '']); + + if (empty($config['scheme']) || empty($config['host'])) { + throw new InvalidArgumentException('The URL was parsed but did not contain a scheme or host'); + } + + if (isset($config['path'])) { + $config['basePath'] = $config['path']; + unset($config['path']); + } + + return new static($config); + } + /** * Get the cookies stored in the Client. * @@ -493,6 +527,7 @@ public function buildUrl(string $url, $query = [], array $options = []): string 'host' => null, 'port' => null, 'scheme' => 'http', + 'basePath' => '', 'protocolRelative' => false, ]; $options += $defaults; @@ -512,6 +547,9 @@ public function buildUrl(string $url, $query = [], array $options = []): string if ($options['port'] && (int)$options['port'] !== $defaultPorts[$options['scheme']]) { $out .= ':' . $options['port']; } + if (!empty($options['basePath'])) { + $out .= '/' . trim($options['basePath'], '/'); + } $out .= '/' . ltrim($url, '/'); return $out; @@ -558,7 +596,7 @@ protected function _createRequest(string $method, string $url, $data, $options): * * @param string $type short type alias or full mimetype. * @return string[] Headers to set on the request. - * @throws \Cake\Core\Exception\Exception When an unknown type alias is used. + * @throws \Cake\Core\Exception\CakeException When an unknown type alias is used. * @psalm-return array{Accept: string, Content-Type: string} */ protected function _typeHeaders(string $type): array @@ -574,7 +612,7 @@ protected function _typeHeaders(string $type): array 'xml' => 'application/xml', ]; if (!isset($typeMap[$type])) { - throw new Exception("Unknown type alias '$type'."); + throw new CakeException("Unknown type alias '$type'."); } return [ @@ -630,7 +668,7 @@ protected function _addProxy(Request $request, array $options): Request * @param array $auth The authentication options to use. * @param array $options The overall request options to use. * @return object Authentication strategy instance. - * @throws \Cake\Core\Exception\Exception when an invalid strategy is chosen. + * @throws \Cake\Core\Exception\CakeException when an invalid strategy is chosen. */ protected function _createAuth(array $auth, array $options) { @@ -640,7 +678,7 @@ protected function _createAuth(array $auth, array $options) $name = ucfirst($auth['type']); $class = App::className($name, 'Http/Client/Auth'); if (!$class) { - throw new Exception( + throw new CakeException( sprintf('Invalid authentication type %s', $name) ); } diff --git a/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Curl.php b/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Curl.php index 68dee3dda..7fc5df08b 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Curl.php +++ b/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Curl.php @@ -103,6 +103,10 @@ public function buildOptions(RequestInterface $request, array $options): array $out[CURLOPT_POST] = true; break; + case Request::METHOD_HEAD: + $out[CURLOPT_NOBODY] = true; + break; + default: $out[CURLOPT_POST] = true; $out[CURLOPT_CUSTOMREQUEST] = $request->getMethod(); @@ -187,12 +191,14 @@ protected function getProtocolVersion(RequestInterface $request): int /** * Convert the raw curl response into an Http\Client\Response * - * @param resource $handle Curl handle + * @param resource|\CurlHandle $handle Curl handle * @param string $responseData string The response data from curl_exec * @return \Cake\Http\Client\Response[] + * @psalm-suppress UndefinedDocblockClass */ protected function createResponse($handle, $responseData): array { + /** @psalm-suppress PossiblyInvalidArgument */ $headerSize = curl_getinfo($handle, CURLINFO_HEADER_SIZE); $headers = trim(substr($responseData, 0, $headerSize)); $body = substr($responseData, $headerSize); @@ -204,11 +210,13 @@ protected function createResponse($handle, $responseData): array /** * Execute the curl handle. * - * @param resource $ch Curl Resource handle + * @param resource|\CurlHandle $ch Curl Resource handle * @return string|bool + * @psalm-suppress UndefinedDocblockClass */ protected function exec($ch) { + /** @psalm-suppress PossiblyInvalidArgument */ return curl_exec($ch); } } diff --git a/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Stream.php b/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Stream.php index 6f5b31a9c..761b23a24 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Stream.php +++ b/app/vendor/cakephp/cakephp/src/Http/Client/Adapter/Stream.php @@ -104,6 +104,7 @@ public function createResponses(array $headers, string $content): array foreach ($indexes as $i => $start) { /** @psalm-suppress InvalidOperand */ $end = isset($indexes[$i + 1]) ? $indexes[$i + 1] - $start : null; + /** @psalm-suppress PossiblyInvalidArgument */ $headerSlice = array_slice($headers, $start, $end); $body = $i === $last ? $content : ''; $responses[] = $this->_buildResponse($headerSlice, $body); @@ -217,6 +218,7 @@ protected function _buildSslContext(RequestInterface $request, array $options): 'ssl_allow_self_signed', 'ssl_cafile', 'ssl_local_cert', + 'ssl_local_pk', 'ssl_passphrase', ]; if (empty($options['ssl_cafile'])) { @@ -254,6 +256,7 @@ protected function _send(RequestInterface $request): array $content = ''; $timedOut = false; + /** @psalm-suppress PossiblyNullArgument */ while (!feof($this->_stream)) { if ($deadline !== false) { stream_set_timeout($this->_stream, max($deadline - time(), 1)); diff --git a/app/vendor/cakephp/cakephp/src/Http/Client/Auth/Oauth.php b/app/vendor/cakephp/cakephp/src/Http/Client/Auth/Oauth.php index 46b1ab733..f066ee025 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Client/Auth/Oauth.php +++ b/app/vendor/cakephp/cakephp/src/Http/Client/Auth/Oauth.php @@ -15,7 +15,7 @@ */ namespace Cake\Http\Client\Auth; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Http\Client\Request; use Cake\Utility\Security; use Psr\Http\Message\UriInterface; @@ -39,7 +39,7 @@ class Oauth * @param \Cake\Http\Client\Request $request The request object. * @param array $credentials Authentication credentials. * @return \Cake\Http\Client\Request The updated request. - * @throws \Cake\Core\Exception\Exception On invalid signature types. + * @throws \Cake\Core\Exception\CakeException On invalid signature types. */ public function authentication(Request $request, array $credentials): Request { @@ -85,7 +85,7 @@ public function authentication(Request $request, array $credentials): Request break; default: - throw new Exception(sprintf('Unknown Oauth signature method %s', $credentials['method'])); + throw new CakeException(sprintf('Unknown Oauth signature method %s', $credentials['method'])); } return $request->withHeader('Authorization', $value); @@ -141,10 +141,14 @@ protected function _hmacSha1(Request $request, array $credentials): string 'oauth_timestamp' => $timestamp, 'oauth_signature_method' => 'HMAC-SHA1', 'oauth_token' => $credentials['token'], - 'oauth_consumer_key' => $credentials['consumerKey'], + 'oauth_consumer_key' => $this->_encode($credentials['consumerKey']), ]; $baseString = $this->baseString($request, $values); + // Consumer key should only be encoded for base string calculation as + // auth header generation already encodes independently + $values['oauth_consumer_key'] = $credentials['consumerKey']; + if (isset($credentials['realm'])) { $values['oauth_realm'] = $credentials['realm']; } @@ -218,7 +222,9 @@ protected function _rsaSha1(Request $request, array $credentials): string $privateKey = openssl_pkey_get_private($credentials['privateKey'], $credentials['privateKeyPassphrase']); $signature = ''; openssl_sign($baseString, $signature, $privateKey); - openssl_free_key($privateKey); + if (PHP_MAJOR_VERSION < 8) { + openssl_free_key($privateKey); + } $values['oauth_signature'] = base64_encode($signature); @@ -285,9 +291,10 @@ protected function _normalizedParams(Request $request, array $oauthValues): stri parse_str((string)$query, $queryArgs); $post = []; - $body = (string)$request->getBody(); - parse_str($body, $post); - + $contentType = $request->getHeaderLine('Content-Type'); + if ($contentType === '' || $contentType === 'application/x-www-form-urlencoded') { + parse_str((string)$request->getBody(), $post); + } $args = array_merge($queryArgs, $oauthValues, $post); $pairs = $this->_normalizeData($args); $data = []; diff --git a/app/vendor/cakephp/cakephp/src/Http/Client/Response.php b/app/vendor/cakephp/cakephp/src/Http/Client/Response.php index ee41e4f63..e89ff2693 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Client/Response.php +++ b/app/vendor/cakephp/cakephp/src/Http/Client/Response.php @@ -64,9 +64,9 @@ * as SimpleXML nodes: * * ``` - * // Get as xml + * // Get as XML * $content = $response->getXml() - * // Get as json + * // Get as JSON * $content = $response->getJson() * ``` * @@ -247,7 +247,7 @@ public function isRedirect(): bool */ public function getStatusCode(): int { - return (int)$this->code; + return $this->code; } /** diff --git a/app/vendor/cakephp/cakephp/src/Http/Cookie/Cookie.php b/app/vendor/cakephp/cakephp/src/Http/Cookie/Cookie.php index 4ed707c00..1eaae0a61 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Cookie/Cookie.php +++ b/app/vendor/cakephp/cakephp/src/Http/Cookie/Cookie.php @@ -236,7 +236,7 @@ public static function create(string $name, $value, array $options = []) * Converts non null expiry value into DateTimeInterface instance. * * @param mixed $expires Expiry value. - * @return \DateTimeInterface|null + * @return \DateTime|\DatetimeImmutable|null */ protected static function dateTimeInstance($expires): ?DateTimeInterface { @@ -431,6 +431,8 @@ public function getValue() */ public function getStringValue() { + deprecationWarning('Cookie::getStringValue() is deprecated. Use getScalarValue() instead.'); + return $this->getScalarValue(); } @@ -793,7 +795,7 @@ public function toArray(): array * Implode method to keep keys are multidimensional arrays * * @param array $array Map of key and values - * @return string A json encoded string. + * @return string A JSON encoded string. */ protected function _flatten(array $array): string { diff --git a/app/vendor/cakephp/cakephp/src/Http/CorsBuilder.php b/app/vendor/cakephp/cakephp/src/Http/CorsBuilder.php index dd32cdad2..6d155141f 100644 --- a/app/vendor/cakephp/cakephp/src/Http/CorsBuilder.php +++ b/app/vendor/cakephp/cakephp/src/Http/CorsBuilder.php @@ -174,7 +174,7 @@ public function allowCredentials() } /** - * Whitelist headers that can be sent in CORS requests. + * Allowed headers that can be sent in CORS requests. * * @param string[] $headers The list of headers to accept in CORS requests. * @return $this diff --git a/app/vendor/cakephp/cakephp/src/Http/Exception/HttpException.php b/app/vendor/cakephp/cakephp/src/Http/Exception/HttpException.php index a03c195f5..2f7744cee 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Exception/HttpException.php +++ b/app/vendor/cakephp/cakephp/src/Http/Exception/HttpException.php @@ -14,16 +14,58 @@ */ namespace Cake\Http\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Parent class for all of the HTTP related exceptions in CakePHP. * All HTTP status/error related exceptions should extend this class so * catch blocks can be specifically typed. * - * You may also use this as a meaningful bridge to Cake\Core\Exception\Exception, e.g.: + * You may also use this as a meaningful bridge to Cake\Core\Exception\CakeException, e.g.: * throw new \Cake\Network\Exception\HttpException('HTTP Version Not Supported', 505); */ -class HttpException extends Exception +class HttpException extends CakeException { + /** + * @inheritDoc + */ + protected $_defaultCode = 500; + + /** + * @var array + */ + protected $headers = []; + + /** + * Set a single HTTP response header. + * + * @param string $header Header name + * @param string|string[]|null $value Header value + * @return void + */ + public function setHeader(string $header, $value = null): void + { + $this->headers[$header] = $value; + } + + /** + * Sets HTTP response headers. + * + * @param array $headers Array of header name and value pairs. + * @return void + */ + public function setHeaders(array $headers): void + { + $this->headers = $headers; + } + + /** + * Returns array of response headers. + * + * @return array + */ + public function getHeaders(): array + { + return $this->headers; + } } diff --git a/app/vendor/cakephp/cakephp/src/Http/Exception/MissingControllerException.php b/app/vendor/cakephp/cakephp/src/Http/Exception/MissingControllerException.php index 4c0a4486d..4cbe6c15f 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Exception/MissingControllerException.php +++ b/app/vendor/cakephp/cakephp/src/Http/Exception/MissingControllerException.php @@ -14,13 +14,13 @@ */ namespace Cake\Http\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Missing Controller exception - used when a controller * cannot be found. */ -class MissingControllerException extends Exception +class MissingControllerException extends CakeException { /** * @inheritDoc diff --git a/app/vendor/cakephp/cakephp/src/Http/Exception/RedirectException.php b/app/vendor/cakephp/cakephp/src/Http/Exception/RedirectException.php new file mode 100644 index 000000000..5d6b1065f --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Http/Exception/RedirectException.php @@ -0,0 +1,84 @@ + $value) { + $this->setHeader($key, (array)$value); + } + } + + /** + * Add headers to be included in the response generated from this exception + * + * @param array $headers An array of `header => value` to append to the exception. + * If a header already exists, the new values will be appended to the existing ones. + * @return $this + * @deprecated 4.2.0 Use `setHeaders()` instead. + */ + public function addHeaders(array $headers) + { + deprecationWarning('RedirectException::addHeaders() is deprecated, use setHeaders() instead.'); + + foreach ($headers as $key => $value) { + $this->headers[$key][] = $value; + } + + return $this; + } + + /** + * Remove a header from the exception. + * + * @param string $key The header to remove. + * @return $this + * @deprecated 4.2.0 Use `setHeaders()` instead. + */ + public function removeHeader(string $key) + { + deprecationWarning('RedirectException::removeHeader() is deprecated, use setHeaders() instead.'); + + unset($this->headers[$key]); + + return $this; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Http/FlashMessage.php b/app/vendor/cakephp/cakephp/src/Http/FlashMessage.php new file mode 100644 index 000000000..2b5270a7d --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Http/FlashMessage.php @@ -0,0 +1,226 @@ + 'flash', + 'element' => 'default', + 'plugin' => null, + 'params' => [], + 'clear' => false, + 'duplicate' => true, + ]; + + /** + * @var \Cake\Http\Session + */ + protected $session; + + /** + * Constructor + * + * @param \Cake\Http\Session $session Session instance. + * @param array $config Config array. + * @see FlashMessage::set() For list of valid config keys. + */ + public function __construct(Session $session, array $config = []) + { + $this->session = $session; + $this->setConfig($config); + } + + /** + * Store flash messages that can be output in the view. + * + * If you make consecutive calls to this method, the messages will stack + * (if they are set with the same flash key) + * + * ### Options: + * + * - `key` The key to set under the session's Flash key. + * - `element` The element used to render the flash message. You can use + * `'SomePlugin.name'` style value for flash elements from a plugin. + * - `plugin` Plugin name to use element from. + * - `params` An array of variables to be made available to the element. + * - `clear` A bool stating if the current stack should be cleared to start a new one. + * - `escape` Set to false to allow templates to print out HTML content. + * + * @param string $message Message to be flashed. + * @param array $options An array of options + * @return void + * @see FlashMessage::$_defaultConfig For default values for the options. + */ + public function set($message, array $options = []): void + { + $options += (array)$this->getConfig(); + + if (isset($options['escape']) && !isset($options['params']['escape'])) { + $options['params']['escape'] = $options['escape']; + } + + [$plugin, $element] = pluginSplit($options['element']); + if ($options['plugin']) { + $plugin = $options['plugin']; + } + + if ($plugin) { + $options['element'] = $plugin . '.flash/' . $element; + } else { + $options['element'] = 'flash/' . $element; + } + + $messages = []; + if (!$options['clear']) { + $messages = (array)$this->session->read('Flash.' . $options['key']); + } + + if (!$options['duplicate']) { + foreach ($messages as $existingMessage) { + if ($existingMessage['message'] === $message) { + return; + } + } + } + + $messages[] = [ + 'message' => $message, + 'key' => $options['key'], + 'element' => $options['element'], + 'params' => $options['params'], + ]; + + $this->session->write('Flash.' . $options['key'], $messages); + } + + /** + * Set an exception's message as flash message. + * + * The following options will be set by default if unset: + * ``` + * 'element' => 'error', + * `params' => ['code' => $exception->getCode()] + * ``` + * + * @param \Throwable $exception Exception instance. + * @param array $options An array of options. + * @return void + * @see FlashMessage::set() For list of valid options + */ + public function setExceptionMessage(Throwable $exception, array $options = []): void + { + if (!isset($options['element'])) { + $options['element'] = 'error'; + } + if (!isset($options['params']['code'])) { + $options['params']['code'] = $exception->getCode(); + } + + $message = $exception->getMessage(); + $this->set($message, $options); + } + + /** + * Get the messages for given key and remove from session. + * + * @param string $key The key for get messages for. + * @return array|null + */ + public function consume(string $key): ?array + { + return $this->session->consume("Flash.{$key}"); + } + + /** + * Set a success message. + * + * The `'element'` option will be set to `'success'`. + * + * @param string $message Message to flash. + * @param array $options An array of options. + * @return void + * @see FlashMessage::set() For list of valid options + */ + public function success(string $message, array $options = []): void + { + $options['element'] = 'success'; + $this->set($message, $options); + } + + /** + * Set an success message. + * + * The `'element'` option will be set to `'error'`. + * + * @param string $message Message to flash. + * @param array $options An array of options. + * @return void + * @see FlashMessage::set() For list of valid options + */ + public function error(string $message, array $options = []): void + { + $options['element'] = 'error'; + $this->set($message, $options); + } + + /** + * Set a warning message. + * + * The `'element'` option will be set to `'warning'`. + * + * @param string $message Message to flash. + * @param array $options An array of options. + * @return void + * @see FlashMessage::set() For list of valid options + */ + public function warning(string $message, array $options = []): void + { + $options['element'] = 'warning'; + $this->set($message, $options); + } + + /** + * Set an info message. + * + * The `'element'` option will be set to `'info'`. + * + * @param string $message Message to flash. + * @param array $options An array of options. + * @return void + * @see FlashMessage::set() For list of valid options + */ + public function info(string $message, array $options = []): void + { + $options['element'] = 'info'; + $this->set($message, $options); + } +} diff --git a/app/vendor/cakephp/cakephp/src/Http/LICENSE.txt b/app/vendor/cakephp/cakephp/src/Http/LICENSE.txt index 0b3b94303..b938c9e8e 100644 --- a/app/vendor/cakephp/cakephp/src/Http/LICENSE.txt +++ b/app/vendor/cakephp/cakephp/src/Http/LICENSE.txt @@ -1,7 +1,7 @@ The MIT License (MIT) CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) -Copyright (c) 2005-2019, Cake Software Foundation, Inc. (https://cakefoundation.org) +Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/vendor/cakephp/cakephp/src/Http/Middleware/BodyParserMiddleware.php b/app/vendor/cakephp/cakephp/src/Http/Middleware/BodyParserMiddleware.php index ac4e260e2..2044a7474 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Middleware/BodyParserMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/Http/Middleware/BodyParserMiddleware.php @@ -28,9 +28,7 @@ /** * Parse encoded request body data. * - * Enables JSON and XML request payloads to be parsed into the request's - * Provides CSRF protection & validation. - * + * Enables JSON and XML request payloads to be parsed into the request's body. * You can also add your own request body parsers using the `addParser()` method. */ class BodyParserMiddleware implements MiddlewareInterface @@ -54,7 +52,7 @@ class BodyParserMiddleware implements MiddlewareInterface * * ### Options * - * - `json` Set to false to disable json body parsing. + * - `json` Set to false to disable JSON body parsing. * - `xml` Set to true to enable XML parsing. Defaults to false, as XML * handling requires more care than JSON does. * - `methods` The HTTP methods to parse on. Defaults to PUT, POST, PATCH DELETE. @@ -178,11 +176,19 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface * Decode JSON into an array. * * @param string $body The request body to decode - * @return mixed + * @return array|null */ protected function decodeJson(string $body) { - return json_decode($body, true); + if ($body === '') { + return []; + } + $decoded = json_decode($body, true); + if (json_last_error() === JSON_ERROR_NONE) { + return (array)$decoded; + } + + return null; } /** diff --git a/app/vendor/cakephp/cakephp/src/Http/Middleware/CspMiddleware.php b/app/vendor/cakephp/cakephp/src/Http/Middleware/CspMiddleware.php index 8ce975d08..f880bc02c 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Middleware/CspMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/Http/Middleware/CspMiddleware.php @@ -66,7 +66,6 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface { $response = $handler->handle($request); - // phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.InvalidFormat /** @var \Psr\Http\Message\ResponseInterface */ return $this->csp->injectCSPHeader($response); } diff --git a/app/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php b/app/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php index 3c7c292eb..d96655777 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php @@ -23,10 +23,12 @@ use Cake\Http\Response; use Cake\Utility\Hash; use Cake\Utility\Security; +use InvalidArgumentException; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use RuntimeException; /** * Provides CSRF protection & validation. @@ -53,7 +55,10 @@ class CsrfProtectionMiddleware implements MiddlewareInterface * - `expiry` A strotime compatible value of how long the CSRF token should last. * Defaults to browser session. * - `secure` Whether or not the cookie will be set with the Secure flag. Defaults to false. - * - `httpOnly` Whether or not the cookie will be set with the HttpOnly flag. Defaults to false. + * - `httponly` Whether or not the cookie will be set with the HttpOnly flag. Defaults to false. + * - `samesite` "SameSite" attribute for cookies. Defaults to `null`. + * Valid values: `CookieInterface::SAMESITE_LAX`, `CookieInterface::SAMESITE_STRICT`, + * `CookieInterface::SAMESITE_NONE` or `null`. * - `field` The form field to check. Changing this will also require configuring * FormHelper. * @@ -63,7 +68,8 @@ class CsrfProtectionMiddleware implements MiddlewareInterface 'cookieName' => 'csrfToken', 'expiry' => 0, 'secure' => false, - 'httpOnly' => false, + 'httponly' => false, + 'samesite' => null, 'field' => '_csrfToken', ]; @@ -74,13 +80,26 @@ class CsrfProtectionMiddleware implements MiddlewareInterface * * @var callable|null */ - protected $whitelistCallback; + protected $skipCheckCallback; /** * @var int */ public const TOKEN_VALUE_LENGTH = 16; + /** + * Tokens have an hmac generated so we can ensure + * that tokens were generated by our application. + * + * Should be TOKEN_VALUE_LENGTH + strlen(hmac) + * + * We are currently using sha1 for the hmac which + * creates 40 bytes. + * + * @var int + */ + public const TOKEN_WITH_CHECKSUM_LENGTH = 56; + /** * Constructor * @@ -88,6 +107,11 @@ class CsrfProtectionMiddleware implements MiddlewareInterface */ public function __construct(array $config = []) { + if (array_key_exists('httpOnly', $config)) { + $config['httponly'] = $config['httpOnly']; + deprecationWarning('Option `httpOnly` is deprecated. Use lowercased `httponly` instead.'); + } + $this->_config = $config + $this->_config; } @@ -106,24 +130,36 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface if ( $hasData - && $this->whitelistCallback !== null - && call_user_func($this->whitelistCallback, $request) === true + && $this->skipCheckCallback !== null + && call_user_func($this->skipCheckCallback, $request) === true ) { $request = $this->_unsetTokenField($request); return $handler->handle($request); } + if ($request->getAttribute('csrfToken')) { + throw new RuntimeException( + 'A CSRF token is already set in the request.' . + "\n" . + 'Ensure you do not have the CSRF middleware applied more than once. ' . + 'Check both your `Application::middleware()` method and `config/routes.php`.' + ); + } $cookies = $request->getCookieParams(); $cookieData = Hash::get($cookies, $this->_config['cookieName']); if (is_string($cookieData) && strlen($cookieData) > 0) { - $request = $request->withAttribute('csrfToken', $cookieData); + try { + $request = $request->withAttribute('csrfToken', $this->saltToken($cookieData)); + } catch (InvalidArgumentException $e) { + $cookieData = null; + } } if ($method === 'GET' && $cookieData === null) { $token = $this->createToken(); - $request = $request->withAttribute('csrfToken', $token); + $request = $request->withAttribute('csrfToken', $this->saltToken($token)); /** @var mixed $response */ $response = $handler->handle($request); @@ -144,12 +180,30 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface * The callback will receive request instance as argument and must return * `true` if you want to skip token check for the current request. * + * @deprecated 4.1.0 Use skipCheckCallback instead. * @param callable $callback A callable. * @return $this */ public function whitelistCallback(callable $callback) { - $this->whitelistCallback = $callback; + deprecationWarning('`whitelistCallback()` is deprecated. Use `skipCheckCallback()` instead.'); + $this->skipCheckCallback = $callback; + + return $this; + } + + /** + * Set callback for allowing to skip token check for particular request. + * + * The callback will receive request instance as argument and must return + * `true` if you want to skip token check for the current request. + * + * @param callable $callback A callable. + * @return $this + */ + public function skipCheckCallback(callable $callback) + { + $this->skipCheckCallback = $callback; return $this; } @@ -179,9 +233,27 @@ protected function _unsetTokenField(ServerRequestInterface $request): ServerRequ */ protected function _createToken(): string { + deprecationWarning('_createToken() is deprecated. Use createToken() instead.'); + return $this->createToken(); } + /** + * Test if the token predates salted tokens. + * + * These tokens are hexadecimal values and equal + * to the token with checksum length. While they are vulnerable + * to BREACH they should rotate over time and support will be dropped + * in 5.x. + * + * @param string $token The token to test. + * @return bool + */ + protected function isHexadecimalToken(string $token): bool + { + return preg_match('/^[a-f0-9]{' . static::TOKEN_WITH_CHECKSUM_LENGTH . '}$/', $token) === 1; + } + /** * Create a new token to be used for CSRF protection * @@ -189,9 +261,70 @@ protected function _createToken(): string */ public function createToken(): string { - $value = Security::randomString(static::TOKEN_VALUE_LENGTH); + $value = Security::randomBytes(static::TOKEN_VALUE_LENGTH); - return $value . hash_hmac('sha1', $value, Security::getSalt()); + return base64_encode($value . hash_hmac('sha1', $value, Security::getSalt())); + } + + /** + * Apply entropy to a CSRF token + * + * To avoid BREACH apply a random salt value to a token + * When the token is compared to the session the token needs + * to be unsalted. + * + * @param string $token The token to salt. + * @return string The salted token with the salt appended. + */ + public function saltToken(string $token): string + { + if ($this->isHexadecimalToken($token)) { + return $token; + } + $decoded = base64_decode($token, true); + if ($decoded === false) { + throw new InvalidArgumentException('Invalid token data.'); + } + + $length = strlen($decoded); + $salt = Security::randomBytes($length); + $salted = ''; + for ($i = 0; $i < $length; $i++) { + // XOR the token and salt together so that we can reverse it later. + $salted .= chr(ord($decoded[$i]) ^ ord($salt[$i])); + } + + return base64_encode($salted . $salt); + } + + /** + * Remove the salt from a CSRF token. + * + * If the token is not TOKEN_VALUE_LENGTH * 2 it is an old + * unsalted value that is supported for backwards compatibility. + * + * @param string $token The token that could be salty. + * @return string An unsalted token. + */ + public function unsaltToken(string $token): string + { + if ($this->isHexadecimalToken($token)) { + return $token; + } + $decoded = base64_decode($token, true); + if ($decoded === false || strlen($decoded) !== static::TOKEN_WITH_CHECKSUM_LENGTH * 2) { + return $token; + } + $salted = substr($decoded, 0, static::TOKEN_WITH_CHECKSUM_LENGTH); + $salt = substr($decoded, static::TOKEN_WITH_CHECKSUM_LENGTH); + + $unsalted = ''; + for ($i = 0; $i < static::TOKEN_WITH_CHECKSUM_LENGTH; $i++) { + // Reverse the XOR to desalt. + $unsalted .= chr(ord($salted[$i]) ^ ord($salt[$i])); + } + + return base64_encode($unsalted); } /** @@ -202,12 +335,19 @@ public function createToken(): string */ protected function _verifyToken(string $token): bool { - if (strlen($token) <= self::TOKEN_VALUE_LENGTH) { + // If we have a hexadecimal value we're in a compatibility mode from before + // tokens were salted on each request. + if ($this->isHexadecimalToken($token)) { + $decoded = $token; + } else { + $decoded = base64_decode($token, true); + } + if (strlen($decoded) <= static::TOKEN_VALUE_LENGTH) { return false; } - $key = substr($token, 0, static::TOKEN_VALUE_LENGTH); - $hmac = substr($token, static::TOKEN_VALUE_LENGTH); + $key = substr($decoded, 0, static::TOKEN_VALUE_LENGTH); + $hmac = substr($decoded, static::TOKEN_VALUE_LENGTH); $expectedHmac = hash_hmac('sha1', $key, Security::getSalt()); @@ -254,7 +394,7 @@ protected function _validateToken(ServerRequestInterface $request): void $exception = new InvalidCsrfTokenException(__d('cake', 'Missing or invalid CSRF cookie.')); $expiredCookie = $this->_createCookie('', $request)->withExpired(); - $exception->responseHeader('Set-Cookie', $expiredCookie->toHeaderValue()); + $exception->setHeader('Set-Cookie', $expiredCookie->toHeaderValue()); throw $exception; } @@ -262,12 +402,14 @@ protected function _validateToken(ServerRequestInterface $request): void $body = $request->getParsedBody(); if (is_array($body) || $body instanceof ArrayAccess) { $post = (string)Hash::get($body, $this->_config['field']); + $post = $this->unsaltToken($post); if (hash_equals($post, $cookie)) { return; } } $header = $request->getHeaderLine('X-CSRF-Token'); + $header = $this->unsaltToken($header); if (hash_equals($header, $cookie)) { return; } @@ -294,7 +436,8 @@ protected function _createCookie(string $value, ServerRequestInterface $request) 'expires' => $this->_config['expiry'] ?: null, 'path' => $request->getAttribute('webroot'), 'secure' => $this->_config['secure'], - 'httponly' => $this->_config['httpOnly'], + 'httponly' => $this->_config['httponly'], + 'samesite' => $this->_config['samesite'], ] ); diff --git a/app/vendor/cakephp/cakephp/src/Http/Middleware/EncryptedCookieMiddleware.php b/app/vendor/cakephp/cakephp/src/Http/Middleware/EncryptedCookieMiddleware.php index 132b24f21..4786cb589 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Middleware/EncryptedCookieMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/Http/Middleware/EncryptedCookieMiddleware.php @@ -25,7 +25,7 @@ use Psr\Http\Server\RequestHandlerInterface; /** - * Middlware for encrypting & decrypting cookies. + * Middleware for encrypting & decrypting cookies. * * This middleware layer will encrypt/decrypt the named cookies with the given key * and cipher type. To support multiple keys/cipher types use this middleware multiple diff --git a/app/vendor/cakephp/cakephp/src/Http/Middleware/SecurityHeadersMiddleware.php b/app/vendor/cakephp/cakephp/src/Http/Middleware/SecurityHeadersMiddleware.php index 01c4bb2e4..b458522fa 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Middleware/SecurityHeadersMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/Http/Middleware/SecurityHeadersMiddleware.php @@ -192,7 +192,7 @@ public function setXFrameOptions(string $option = self::SAMEORIGIN, ?string $url */ public function setXssProtection(string $mode = self::XSS_BLOCK) { - $mode = (string)$mode; + $mode = $mode; if ($mode === self::XSS_BLOCK) { $mode = self::XSS_ENABLED_BLOCK; diff --git a/app/vendor/cakephp/cakephp/src/Http/Middleware/SessionCsrfProtectionMiddleware.php b/app/vendor/cakephp/cakephp/src/Http/Middleware/SessionCsrfProtectionMiddleware.php new file mode 100644 index 000000000..4b3beae79 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Http/Middleware/SessionCsrfProtectionMiddleware.php @@ -0,0 +1,270 @@ +Form->create(...)` is used in a view. + * + * If you use this middleware *do not* also use CsrfProtectionMiddleware. + * + * @see https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#sychronizer-token-pattern + */ +class SessionCsrfProtectionMiddleware implements MiddlewareInterface +{ + /** + * Config for the CSRF handling. + * + * - `key` The session key to use. Defaults to `csrfToken` + * - `field` The form field to check. Changing this will also require configuring + * FormHelper. + * + * @var array + */ + protected $_config = [ + 'key' => 'csrfToken', + 'field' => '_csrfToken', + ]; + + /** + * Callback for deciding whether or not to skip the token check for particular request. + * + * CSRF protection token check will be skipped if the callback returns `true`. + * + * @var callable|null + */ + protected $skipCheckCallback; + + /** + * @var int + */ + public const TOKEN_VALUE_LENGTH = 32; + + /** + * Constructor + * + * @param array $config Config options. See $_config for valid keys. + */ + public function __construct(array $config = []) + { + $this->_config = $config + $this->_config; + } + + /** + * Checks and sets the CSRF token depending on the HTTP verb. + * + * @param \Psr\Http\Message\ServerRequestInterface $request The request. + * @param \Psr\Http\Server\RequestHandlerInterface $handler The request handler. + * @return \Psr\Http\Message\ResponseInterface A response. + */ + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + $method = $request->getMethod(); + $hasData = in_array($method, ['PUT', 'POST', 'DELETE', 'PATCH'], true) + || $request->getParsedBody(); + + if ( + $hasData + && $this->skipCheckCallback !== null + && call_user_func($this->skipCheckCallback, $request) === true + ) { + $request = $this->unsetTokenField($request); + + return $handler->handle($request); + } + + $session = $request->getAttribute('session'); + if (!$session || !($session instanceof Session)) { + throw new RuntimeException('You must have a `session` attribute to use session based CSRF tokens'); + } + + $token = $session->read($this->_config['key']); + if ($token === null) { + $token = $this->createToken(); + $session->write($this->_config['key'], $token); + } + $request = $request->withAttribute('csrfToken', $this->saltToken($token)); + + if ($method === 'GET') { + return $handler->handle($request); + } + + if ($hasData) { + $this->validateToken($request, $session); + $request = $this->unsetTokenField($request); + } + + return $handler->handle($request); + } + + /** + * Set callback for allowing to skip token check for particular request. + * + * The callback will receive request instance as argument and must return + * `true` if you want to skip token check for the current request. + * + * @param callable $callback A callable. + * @return $this + */ + public function skipCheckCallback(callable $callback) + { + $this->skipCheckCallback = $callback; + + return $this; + } + + /** + * Apply entropy to a CSRF token + * + * To avoid BREACH apply a random salt value to a token + * When the token is compared to the session the token needs + * to be unsalted. + * + * @param string $token The token to salt. + * @return string The salted token with the salt appended. + */ + public function saltToken(string $token): string + { + $decoded = base64_decode($token); + $length = strlen($decoded); + $salt = Security::randomBytes($length); + $salted = ''; + for ($i = 0; $i < $length; $i++) { + // XOR the token and salt together so that we can reverse it later. + $salted .= chr(ord($decoded[$i]) ^ ord($salt[$i])); + } + + return base64_encode($salted . $salt); + } + + /** + * Remove the salt from a CSRF token. + * + * If the token is not TOKEN_VALUE_LENGTH * 2 it is an old + * unsalted value that is supported for backwards compatibility. + * + * @param string $token The token that could be salty. + * @return string An unsalted token. + */ + protected function unsaltToken(string $token): string + { + $decoded = base64_decode($token, true); + if ($decoded === false || strlen($decoded) !== static::TOKEN_VALUE_LENGTH * 2) { + return $token; + } + $salted = substr($decoded, 0, static::TOKEN_VALUE_LENGTH); + $salt = substr($decoded, static::TOKEN_VALUE_LENGTH); + + $unsalted = ''; + for ($i = 0; $i < static::TOKEN_VALUE_LENGTH; $i++) { + // Reverse the XOR to desalt. + $unsalted .= chr(ord($salted[$i]) ^ ord($salt[$i])); + } + + return base64_encode($unsalted); + } + + /** + * Remove CSRF protection token from request data. + * + * This ensures that the token does not cause failures during + * form tampering protection. + * + * @param \Psr\Http\Message\ServerRequestInterface $request The request object. + * @return \Psr\Http\Message\ServerRequestInterface + */ + protected function unsetTokenField(ServerRequestInterface $request): ServerRequestInterface + { + $body = $request->getParsedBody(); + if (is_array($body)) { + unset($body[$this->_config['field']]); + $request = $request->withParsedBody($body); + } + + return $request; + } + + /** + * Create a new token to be used for CSRF protection + * + * This token is a simple unique random value as the compare + * value is stored in the session where it cannot be tampered with. + * + * @return string + */ + public function createToken(): string + { + return base64_encode(Security::randomBytes(static::TOKEN_VALUE_LENGTH)); + } + + /** + * Validate the request data against the cookie token. + * + * @param \Psr\Http\Message\ServerRequestInterface $request The request to validate against. + * @param \Cake\Http\Session $session The session instance. + * @return void + * @throws \Cake\Http\Exception\InvalidCsrfTokenException When the CSRF token is invalid or missing. + */ + protected function validateToken(ServerRequestInterface $request, Session $session): void + { + $token = $session->read($this->_config['key']); + if (!$token || !is_string($token)) { + throw new InvalidCsrfTokenException(__d('cake', 'Missing or incorrect CSRF session key')); + } + + $body = $request->getParsedBody(); + if (is_array($body) || $body instanceof ArrayAccess) { + $post = (string)Hash::get($body, $this->_config['field']); + $post = $this->unsaltToken($post); + if (hash_equals($post, $token)) { + return; + } + } + + $header = $request->getHeaderLine('X-CSRF-Token'); + $header = $this->unsaltToken($header); + if (hash_equals($header, $token)) { + return; + } + + throw new InvalidCsrfTokenException(__d( + 'cake', + 'CSRF token from either the request body or request headers did not match or is missing.' + )); + } +} diff --git a/app/vendor/cakephp/cakephp/src/Http/MiddlewareQueue.php b/app/vendor/cakephp/cakephp/src/Http/MiddlewareQueue.php index ccdd4a87f..ee5704183 100644 --- a/app/vendor/cakephp/cakephp/src/Http/MiddlewareQueue.php +++ b/app/vendor/cakephp/cakephp/src/Http/MiddlewareQueue.php @@ -31,6 +31,8 @@ /** * Provides methods for creating and manipulating a "queue" of middlewares. * This queue is used to process a request and generate response via \Cake\Http\Runner. + * + * @template-implements \SeekableIterator */ class MiddlewareQueue implements Countable, SeekableIterator { diff --git a/app/vendor/cakephp/cakephp/src/Http/README.md b/app/vendor/cakephp/cakephp/src/Http/README.md index e67f57a18..f305fdd5c 100644 --- a/app/vendor/cakephp/cakephp/src/Http/README.md +++ b/app/vendor/cakephp/cakephp/src/Http/README.md @@ -9,7 +9,7 @@ handle incoming server requests and send outgoing HTTP requests. ## Using the Http Client -Sending requests is straight forward. Doing a GET request looks like +Sending requests is straight forward. Doing a GET request looks like: ```php use Cake\Http\Client; @@ -34,13 +34,16 @@ To learn more read the [Http Client documentation](https://book.cakephp.org/4/en The Http Server allows an `HttpApplicationInterface` to process requests and emit responses. To get started first implement the -`Cake\Http\HttpApplicationInterface` A minimal example would could look like: +`Cake\Http\HttpApplicationInterface` A minimal example could look like: ```php namespace App; use Cake\Core\HttpApplicationInterface; use Cake\Http\MiddlewareQueue; +use Cake\Http\Response; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; class Application implements HttpApplicationInterface { @@ -66,6 +69,17 @@ class Application implements HttpApplicationInterface // Add middleware for your application. return $middlewareQueue; } + + /** + * Handle incoming server request and return a response. + * + * @param \Psr\Http\Message\ServerRequestInterface $request The request + * @return \Psr\Http\Message\ResponseInterface + */ + public function handle(ServerRequestInterface $request): ResponseInterface + { + return new Response(['body'=>'Hello World!']); + } } ``` diff --git a/app/vendor/cakephp/cakephp/src/Http/Response.php b/app/vendor/cakephp/cakephp/src/Http/Response.php index 3276cd9cc..385bdee9d 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Response.php +++ b/app/vendor/cakephp/cakephp/src/Http/Response.php @@ -35,7 +35,7 @@ * * There are external packages such as `fig/http-message-util` that provide HTTP * status code constants. These can be used with any method that accepts or - * returns a status code integer. Keep in mind that these consants might + * returns a status code integer. Keep in mind that these constants might * include status codes that are now allowed which will throw an * `\InvalidArgumentException`. */ @@ -167,6 +167,8 @@ class Response implements ResponseInterface 'gz' => 'application/x-gzip', 'bz2' => 'application/x-bzip', '7z' => 'application/x-7z-compressed', + 'haljson' => ['application/hal+json', 'application/vnd.hal+json'], + 'halxml' => ['application/hal+xml', 'application/vnd.hal+xml'], 'hdf' => 'application/x-hdf', 'hqx' => 'application/mac-binhex40', 'ico' => 'image/x-icon', @@ -175,6 +177,7 @@ class Response implements ResponseInterface 'js' => 'application/javascript', 'jsonapi' => 'application/vnd.api+json', 'latex' => 'application/x-latex', + 'jsonld' => 'application/ld+json', 'lha' => 'application/octet-stream', 'lsp' => 'application/x-lisp', 'lzh' => 'application/octet-stream', @@ -496,7 +499,7 @@ protected function _setContentType(string $type): void return; } - $whitelist = [ + $allowed = [ 'application/javascript', 'application/xml', 'application/rss+xml', ]; @@ -505,7 +508,7 @@ protected function _setContentType(string $type): void $this->_charset && ( strpos($type, 'text/') === 0 || - in_array($type, $whitelist, true) + in_array($type, $allowed, true) ) ) { $charset = true; @@ -596,7 +599,7 @@ public function getStatusCode(): int * * There are external packages such as `fig/http-message-util` that provide HTTP * status code constants. These can be used with any method that accepts or - * returns a status code integer. However, keep in mind that these consants + * returns a status code integer. However, keep in mind that these constants * might include status codes that are now allowed which will throw an * `\InvalidArgumentException`. * @@ -806,7 +809,7 @@ public function withCharset(string $charset) public function withDisabledCache() { return $this->withHeader('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT') - ->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT') + ->withHeader('Last-Modified', gmdate(DATE_RFC7231)) ->withHeader('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'); } @@ -828,7 +831,7 @@ public function withCache($since, $time = '+1 day') } } - return $this->withHeader('Date', gmdate('D, j M Y G:i:s ', time()) . 'GMT') + return $this->withHeader('Date', gmdate(DATE_RFC7231, time())) ->withModified($since) ->withExpires($time) ->withSharable(true) @@ -956,7 +959,7 @@ public function withExpires($time) { $date = $this->_getUTCDate($time); - return $this->withHeader('Expires', $date->format('D, j M Y H:i:s') . ' GMT'); + return $this->withHeader('Expires', $date->format(DATE_RFC7231)); } /** @@ -979,7 +982,7 @@ public function withModified($time) { $date = $this->_getUTCDate($time); - return $this->withHeader('Last-Modified', $date->format('D, j M Y H:i:s') . ' GMT'); + return $this->withHeader('Last-Modified', $date->format(DATE_RFC7231)); } /** @@ -1206,7 +1209,7 @@ public function withAddedLink(string $url, array $options = []) */ public function checkNotModified(ServerRequest $request): bool { - $etags = preg_split('/\s*,\s*/', (string)$request->getHeaderLine('If-None-Match'), 0, PREG_SPLIT_NO_EMPTY); + $etags = preg_split('/\s*,\s*/', $request->getHeaderLine('If-None-Match'), 0, PREG_SPLIT_NO_EMPTY); $responseTag = $this->getHeaderLine('Etag'); $etagMatches = null; if ($responseTag) { @@ -1240,7 +1243,7 @@ public function __toString(): string { $this->stream->rewind(); - return (string)$this->stream->getContents(); + return $this->stream->getContents(); } /** @@ -1373,7 +1376,7 @@ public function withCookieCollection(CookieCollection $cookieCollection) * cors($request, '*'); * ``` * - * ### Whitelist of URIs + * ### Allowed list of URIs * ``` * cors($request, ['http://www.cakephp.org', '*.google.com', 'https://myproject.github.io']); * ``` diff --git a/app/vendor/cakephp/cakephp/src/Http/ResponseEmitter.php b/app/vendor/cakephp/cakephp/src/Http/ResponseEmitter.php index a0779bb8a..a55a082cf 100644 --- a/app/vendor/cakephp/cakephp/src/Http/ResponseEmitter.php +++ b/app/vendor/cakephp/cakephp/src/Http/ResponseEmitter.php @@ -232,6 +232,7 @@ protected function setCookie($cookie): bool } if (PHP_VERSION_ID >= 70300) { + /** @psalm-suppress InvalidArgument */ return setcookie($cookie->getName(), $cookie->getScalarValue(), $cookie->getOptions()); } diff --git a/app/vendor/cakephp/cakephp/src/Http/Server.php b/app/vendor/cakephp/cakephp/src/Http/Server.php index 416808662..913fbe053 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Server.php +++ b/app/vendor/cakephp/cakephp/src/Http/Server.php @@ -99,16 +99,14 @@ public function run( /** * Application bootstrap wrapper. * - * Calls `bootstrap()` and `events()` if application implements `EventApplicationInterface`. - * After the application is bootstrapped and events are attached, plugins are bootstrapped - * and have their events attached. + * Calls the application's `bootstrap()` hook. After the application the + * plugins are bootstrapped. * * @return void */ protected function bootstrap(): void { $this->app->bootstrap(); - if ($this->app instanceof PluginApplicationInterface) { $this->app->pluginBootstrap(); } diff --git a/app/vendor/cakephp/cakephp/src/Http/ServerRequest.php b/app/vendor/cakephp/cakephp/src/Http/ServerRequest.php index 098a8297e..2be352824 100644 --- a/app/vendor/cakephp/cakephp/src/Http/ServerRequest.php +++ b/app/vendor/cakephp/cakephp/src/Http/ServerRequest.php @@ -18,6 +18,7 @@ use BadMethodCallException; use Cake\Core\Configure; +use Cake\Core\Exception\CakeException; use Cake\Http\Cookie\CookieCollection; use Cake\Http\Exception\MethodNotAllowedException; use Cake\Utility\Hash; @@ -159,6 +160,13 @@ class ServerRequest implements ServerRequestInterface */ protected $session; + /** + * Instance of a FlashMessage object relative to this request + * + * @var \Cake\Http\FlashMessage + */ + protected $flash; + /** * Store the additional attributes attached to the request. * @@ -171,7 +179,7 @@ class ServerRequest implements ServerRequestInterface * * @var array */ - protected $emulatedAttributes = ['session', 'webroot', 'base', 'params', 'here']; + protected $emulatedAttributes = ['session', 'flash', 'webroot', 'base', 'params', 'here']; /** * Array of Psr\Http\Message\UploadedFileInterface objects. @@ -194,13 +202,6 @@ class ServerRequest implements ServerRequestInterface */ protected $requestTarget; - /** - * Whether to merge file uploads as objects (`true`) or arrays (`false`). - * - * @var bool - */ - protected $mergeFilesAsObjects = true; - /** * Create a new request object. * @@ -210,7 +211,7 @@ class ServerRequest implements ServerRequestInterface * * - `post` POST data or non query string data * - `query` Additional data from the query string. - * - `files` Uploaded file data formatted like $_FILES. + * - `files` Uploaded files in a normalized structure, with each leaf an instance of UploadedFileInterface. * - `cookies` Cookies for this request. * - `environment` $_SERVER and $_ENV data. * - `url` The URL without the base path for the request. @@ -220,7 +221,6 @@ class ServerRequest implements ServerRequestInterface * - `input` The data that would come from php://input this is useful for simulating * requests with put, patch or delete data. * - `session` An instance of a Session object - * - `mergeFilesAsObjects` Whether to merge file uploads as objects (`true`) or arrays (`false`). * * @param array $config An array of request data to create a request with. */ @@ -238,7 +238,6 @@ public function __construct(array $config = []) 'base' => '', 'webroot' => '', 'input' => null, - 'mergeFilesAsObjects' => true, ]; $this->_setConfig($config); @@ -252,38 +251,31 @@ public function __construct(array $config = []) */ protected function _setConfig(array $config): void { - if (strlen($config['url']) > 1 && $config['url'][0] === '/') { - $config['url'] = substr($config['url'], 1); - } - if (empty($config['session'])) { $config['session'] = new Session([ 'cookiePath' => $config['base'], ]); } - $this->_environment = $config['environment']; + if (empty($config['environment']['REQUEST_METHOD'])) { + $config['environment']['REQUEST_METHOD'] = 'GET'; + } + $this->cookies = $config['cookies']; - if (isset($config['uri']) && $config['uri'] instanceof UriInterface) { + if (isset($config['uri'])) { + if (!$config['uri'] instanceof UriInterface) { + throw new CakeException('The `uri` key must be an instance of ' . UriInterface::class); + } $uri = $config['uri']; } else { + if ($config['url'] !== '') { + $config = $this->processUrlOption($config); + } $uri = ServerRequestFactory::createUri($config['environment']); } - // Extract a query string from config[url] if present. - // This is required for backwards compatibility and keeping - // UriInterface implementations happy. - $querystr = ''; - if (strpos($config['url'], '?') !== false) { - [$config['url'], $querystr] = explode('?', $config['url']); - } - if (strlen($config['url'])) { - $uri = $uri->withPath('/' . $config['url']); - } - if (strlen($querystr)) { - $uri = $uri->withQuery($querystr); - } + $this->_environment = $config['environment']; $this->uri = $uri; $this->base = $config['base']; @@ -298,176 +290,38 @@ protected function _setConfig(array $config): void } $this->stream = $stream; - $this->mergeFilesAsObjects = $config['mergeFilesAsObjects']; - - $config['post'] = $this->_processPost($config['post']); - $this->data = $this->_processFiles($config['post'], $config['files']); - $this->query = $this->_processGet($config['query'], $querystr); + $this->data = $config['post']; + $this->uploadedFiles = $config['files']; + $this->query = $config['query']; $this->params = $config['params']; $this->session = $config['session']; + $this->flash = new FlashMessage($this->session); } /** - * Sets the REQUEST_METHOD environment variable based on the simulated _method - * HTTP override value. The 'ORIGINAL_REQUEST_METHOD' is also preserved, if you - * want the read the non-simulated HTTP method the client used. + * Set environment vars based on `url` option to facilitate UriInterface instance generation. * - * @param mixed $data Array of post data. - * @return mixed - */ - protected function _processPost($data) - { - $method = $this->getEnv('REQUEST_METHOD'); - $override = false; - - if ( - in_array($method, ['PUT', 'DELETE', 'PATCH'], true) && - strpos((string)$this->contentType(), 'application/x-www-form-urlencoded') === 0 - ) { - $data = $this->input(); - parse_str($data, $data); - } - if ($this->hasHeader('X-Http-Method-Override')) { - $data['_method'] = $this->getHeaderLine('X-Http-Method-Override'); - $override = true; - } - $this->_environment['ORIGINAL_REQUEST_METHOD'] = $method; - if (isset($data['_method'])) { - $this->_environment['REQUEST_METHOD'] = $data['_method']; - unset($data['_method']); - $override = true; - } - - if ($override && !in_array($this->_environment['REQUEST_METHOD'], ['PUT', 'POST', 'DELETE', 'PATCH'], true)) { - $data = []; - } - - return $data; - } - - /** - * Process the GET parameters and move things into the object. - * - * @param array $query The array to which the parsed keys/values are being added. - * @param string $queryString A query string from the URL if provided - * @return array An array containing the parsed query string as keys/values. - */ - protected function _processGet(array $query, string $queryString = ''): array - { - $unsetUrl = str_replace(['.', ' '], '_', urldecode($this->uri->getPath())); - unset($query[$unsetUrl], $query[$this->base . $unsetUrl]); - if (strlen($queryString)) { - parse_str($queryString, $queryArgs); - $query += $queryArgs; - } - - return $query; - } - - /** - * Process uploaded files and move things onto the post data. + * `query` option is also updated based on URL's querystring. * - * @param mixed $post Post data to merge files onto. - * @param mixed $files Uploaded files to merge in. - * @return array merged post + file data. + * @param array $config Config array. + * @return array Update config. */ - protected function _processFiles($post, $files) + protected function processUrlOption(array $config): array { - if (!is_array($post) || !is_array($files) || empty($files)) { - return $post; + if ($config['url'][0] !== '/') { + $config['url'] = '/' . $config['url']; } - $fileData = []; - foreach ($files as $key => $value) { - if ($value instanceof UploadedFileInterface) { - $fileData[$key] = $value; - continue; - } - - if (is_array($value) && isset($value['tmp_name'])) { - $fileData[$key] = $this->_createUploadedFile($value); - continue; - } - - throw new InvalidArgumentException(sprintf( - 'Invalid value in FILES "%s"', - json_encode($value) - )); - } - $this->uploadedFiles = $fileData; - - if ($this->mergeFilesAsObjects) { - return Hash::merge($post, $fileData); - } - - // Make a flat map that can be inserted into $post for BC. - $fileMap = Hash::flatten($fileData); - foreach ($fileMap as $key => $file) { - $error = $file->getError(); - $tmpName = ''; - if ($error === UPLOAD_ERR_OK) { - $tmpName = $file->getStream()->getMetadata('uri'); - } - $post = Hash::insert($post, (string)$key, [ - 'tmp_name' => $tmpName, - 'error' => $error, - 'name' => $file->getClientFilename(), - 'type' => $file->getClientMediaType(), - 'size' => $file->getSize(), - ]); - } - - return $post; - } + if (strpos($config['url'], '?') !== false) { + [$config['url'], $config['environment']['QUERY_STRING']] = explode('?', $config['url']); - /** - * Create an UploadedFile instance from a $_FILES array. - * - * If the value represents an array of values, this method will - * recursively process the data. - * - * @param array $value $_FILES struct - * @return array|\Psr\Http\Message\UploadedFileInterface - */ - protected function _createUploadedFile(array $value) - { - if (is_array($value['tmp_name'])) { - return $this->_normalizeNestedFiles($value); + parse_str($config['environment']['QUERY_STRING'], $queryArgs); + $config['query'] += $queryArgs; } - return new UploadedFile( - $value['tmp_name'], - $value['size'], - $value['error'], - $value['name'], - $value['type'] - ); - } + $config['environment']['REQUEST_URI'] = $config['url']; - /** - * Normalize an array of file specifications. - * - * Loops through all nested files and returns a normalized array of - * UploadedFileInterface instances. - * - * @param array $files The file data to normalize & convert. - * @return array An array of UploadedFileInterface objects. - */ - protected function _normalizeNestedFiles(array $files = []): array - { - $normalizedFiles = []; - foreach (array_keys($files['tmp_name']) as $key) { - $spec = [ - 'tmp_name' => $files['tmp_name'][$key], - 'size' => $files['size'][$key], - 'error' => $files['error'][$key], - 'name' => $files['name'][$key], - 'type' => $files['type'][$key], - ]; - $normalizedFiles[$key] = $this->_createUploadedFile($spec); - } - - return $normalizedFiles; + return $config; } /** @@ -495,6 +349,16 @@ public function getSession(): Session return $this->session; } + /** + * Returns the instance of the FlashMessage object for this request + * + * @return \Cake\Http\FlashMessage + */ + public function getFlash(): FlashMessage + { + return $this->flash; + } + /** * Get the IP the client is using, or says they are using. * @@ -613,7 +477,7 @@ public function __call(string $name, array $params) * * @param string|string[] $type The type of request you want to check. If an array * this method will return true if the request matches any type. - * @param string ...$args List of arguments + * @param mixed ...$args List of arguments * @return bool Whether or not the request is the type you are checking. */ public function is($type, ...$args): bool @@ -713,7 +577,7 @@ protected function _headerDetector(array $detect): bool $header = $this->getEnv('http_' . $header); if ($header !== null) { if (!is_string($value) && !is_bool($value) && is_callable($value)) { - return call_user_func($value, $header); + return $value($header); } return $header === $value; @@ -1421,6 +1285,9 @@ public function getData(?string $name = null, $default = null) * * Any additional parameters are applied to the callback in the order they are given. * + * @deprecated 4.1.0 Use `(string)$request->getBody()` to get the raw PHP input + * as string; use `BodyParserMiddleware` to parse the request body so that it's + * available as array/object through `$request->getParsedBody()`. * @param callable|null $callback A decoding callback that will convert the string data to another * representation. Leave empty to access the raw input data. You can also * supply additional parameters for the decoding callback using var args, see above. @@ -1429,12 +1296,17 @@ public function getData(?string $name = null, $default = null) */ public function input(?callable $callback = null, ...$args) { + deprecationWarning( + 'Use `(string)$request->getBody()` to get the raw PHP input as string; ' + . 'use `BodyParserMiddleware` to parse the request body so that it\'s available as array/object ' + . 'through $request->getParsedBody()' + ); $this->stream->rewind(); $input = $this->stream->getContents(); if ($callback) { array_unshift($args, $input); - return call_user_func_array($callback, $args); + return $callback(...$args); } return $input; @@ -1653,7 +1525,7 @@ public function allowMethod($methods): bool } $allowed = strtoupper(implode(', ', $methods)); $e = new MethodNotAllowedException(); - $e->responseHeader('Allow', $allowed); + $e->setHeader('Allow', $allowed); throw $e; } @@ -1841,15 +1713,15 @@ public function getUploadedFiles(): array /** * Update the request replacing the files, and creating a new instance. * - * @param array $files An array of uploaded file objects. + * @param array $uploadedFiles An array of uploaded file objects. * @return static * @throws \InvalidArgumentException when $files contains an invalid object. */ - public function withUploadedFiles(array $files) + public function withUploadedFiles(array $uploadedFiles) { - $this->validateUploadedFiles($files, ''); + $this->validateUploadedFiles($uploadedFiles, ''); $new = clone $this; - $new->uploadedFiles = $files; + $new->uploadedFiles = $uploadedFiles; return $new; } @@ -1952,14 +1824,14 @@ public function withUri(UriInterface $uri, $preserveHost = false) * * @link https://tools.ietf.org/html/rfc7230#section-2.7 (for the various * request-target forms allowed in request messages) - * @param string $target The request target. + * @param string $requestTarget The request target. * @return static * @psalm-suppress MoreSpecificImplementedParamType */ - public function withRequestTarget($target) + public function withRequestTarget($requestTarget) { $new = clone $this; - $new->requestTarget = $target; + $new->requestTarget = $requestTarget; return $new; } diff --git a/app/vendor/cakephp/cakephp/src/Http/ServerRequestFactory.php b/app/vendor/cakephp/cakephp/src/Http/ServerRequestFactory.php index d1e9a8967..9a39a4cf9 100644 --- a/app/vendor/cakephp/cakephp/src/Http/ServerRequestFactory.php +++ b/app/vendor/cakephp/cakephp/src/Http/ServerRequestFactory.php @@ -24,6 +24,7 @@ use function Laminas\Diactoros\marshalHeadersFromSapi; use function Laminas\Diactoros\marshalUriFromSapi; use function Laminas\Diactoros\normalizeServer; +use function Laminas\Diactoros\normalizeUploadedFiles; /** * Factory for making ServerRequest instances. @@ -46,7 +47,7 @@ abstract class ServerRequestFactory implements ServerRequestFactoryInterface * @see fromServer() * @param array $server $_SERVER superglobal * @param array $query $_GET superglobal - * @param array $body $_POST superglobal + * @param array $parsedBody $_POST superglobal * @param array $cookies $_COOKIE superglobal * @param array $files $_FILES superglobal * @return \Cake\Http\ServerRequest @@ -55,36 +56,125 @@ abstract class ServerRequestFactory implements ServerRequestFactoryInterface public static function fromGlobals( ?array $server = null, ?array $query = null, - ?array $body = null, + ?array $parsedBody = null, ?array $cookies = null, ?array $files = null ): ServerRequest { $server = normalizeServer($server ?: $_SERVER); $uri = static::createUri($server); + /** @psalm-suppress NoInterfaceProperties */ $sessionConfig = (array)Configure::read('Session') + [ 'defaults' => 'php', 'cookiePath' => $uri->webroot, ]; $session = Session::create($sessionConfig); + /** @psalm-suppress NoInterfaceProperties */ $request = new ServerRequest([ 'environment' => $server, 'uri' => $uri, - 'files' => $files ?: $_FILES, 'cookies' => $cookies ?: $_COOKIE, 'query' => $query ?: $_GET, - 'post' => $body ?: $_POST, 'webroot' => $uri->webroot, 'base' => $uri->base, 'session' => $session, - 'mergeFilesAsObjects' => Configure::read('App.uploadedFilesAsObjects', true), 'input' => $server['CAKEPHP_INPUT'] ?? null, ]); + $request = static::marshalBodyAndRequestMethod($parsedBody ?? $_POST, $request); + $request = static::marshalFiles($files ?? $_FILES, $request); + return $request; } + /** + * Sets the REQUEST_METHOD environment variable based on the simulated _method + * HTTP override value. The 'ORIGINAL_REQUEST_METHOD' is also preserved, if you + * want the read the non-simulated HTTP method the client used. + * + * Request body of content type "application/x-www-form-urlencoded" is parsed + * into array for PUT/PATCH/DELETE requests. + * + * @param array $parsedBody Parsed body. + * @param \Cake\Http\ServerRequest $request Request instance. + * @return \Cake\Http\ServerRequest + */ + protected static function marshalBodyAndRequestMethod(array $parsedBody, ServerRequest $request): ServerRequest + { + $method = $request->getMethod(); + $override = false; + + if ( + in_array($method, ['PUT', 'DELETE', 'PATCH'], true) && + strpos((string)$request->contentType(), 'application/x-www-form-urlencoded') === 0 + ) { + $data = (string)$request->getBody(); + parse_str($data, $parsedBody); + } + if ($request->hasHeader('X-Http-Method-Override')) { + $parsedBody['_method'] = $request->getHeaderLine('X-Http-Method-Override'); + $override = true; + } + + $request = $request->withEnv('ORIGINAL_REQUEST_METHOD', $method); + if (isset($parsedBody['_method'])) { + $request = $request->withEnv('REQUEST_METHOD', $parsedBody['_method']); + unset($parsedBody['_method']); + $override = true; + } + + if ( + $override && + !in_array($request->getMethod(), ['PUT', 'POST', 'DELETE', 'PATCH'], true) + ) { + $parsedBody = []; + } + + return $request->withParsedBody($parsedBody); + } + + /** + * Process uploaded files and move things onto the parsed body. + * + * @param array $files Files array for normalization and merging in parsed body. + * @param \Cake\Http\ServerRequest $request Request instance. + * @return \Cake\Http\ServerRequest + */ + protected static function marshalFiles(array $files, ServerRequest $request): ServerRequest + { + $files = normalizeUploadedFiles($files); + $request = $request->withUploadedFiles($files); + + $parsedBody = $request->getParsedBody(); + if (!is_array($parsedBody)) { + return $request; + } + + if (Configure::read('App.uploadedFilesAsObjects', true)) { + $parsedBody = Hash::merge($parsedBody, $files); + } else { + // Make a flat map that can be inserted into body for BC. + $fileMap = Hash::flatten($files); + foreach ($fileMap as $key => $file) { + $error = $file->getError(); + $tmpName = ''; + if ($error === UPLOAD_ERR_OK) { + $tmpName = $file->getStream()->getMetadata('uri'); + } + $parsedBody = Hash::insert($parsedBody, (string)$key, [ + 'tmp_name' => $tmpName, + 'error' => $error, + 'name' => $file->getClientFilename(), + 'type' => $file->getClientMediaType(), + 'size' => $file->getSize(), + ]); + } + } + + return $request->withParsedBody($parsedBody); + } + /** * Create a new server request. * diff --git a/app/vendor/cakephp/cakephp/src/Http/Session.php b/app/vendor/cakephp/cakephp/src/Http/Session.php index 77cb73845..99792b9b2 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Session.php +++ b/app/vendor/cakephp/cakephp/src/Http/Session.php @@ -25,7 +25,7 @@ /** * This class is a wrapper for the native PHP session functions. It provides * several defaults for the most common session configuration - * via external handlers and helps with using session in cli without any warnings. + * via external handlers and helps with using session in CLI without any warnings. * * Sessions can be created from the defaults using `Session::create()` or you can get * an instance of a new session by just instantiating this class and passing the complete @@ -430,31 +430,48 @@ public function check(?string $name = null): bool * Returns given session variable, or all of them, if no parameters given. * * @param string|null $name The name of the session variable (or a path as sent to Hash.extract) - * @return string|array|null The value of the session variable, null if session not available, - * session not started, or provided name not found in the session. + * @param mixed $default The return value when the path does not exist + * @return mixed|null The value of the session variable, or default value if a session + * is not available, can't be started, or provided $name is not found in the session. */ - public function read(?string $name = null) + public function read(?string $name = null, $default = null) { if ($this->_hasSession() && !$this->started()) { $this->start(); } if (!isset($_SESSION)) { - return null; + return $default; } if ($name === null) { return $_SESSION ?: []; } - return Hash::get($_SESSION, $name); + return Hash::get($_SESSION, $name, $default); + } + + /** + * Returns given session variable, or throws Exception if not found. + * + * @param string $name The name of the session variable (or a path as sent to Hash.extract) + * @throws \RuntimeException + * @return mixed|null + */ + public function readOrFail(string $name) + { + if (!$this->check($name)) { + throw new RuntimeException(sprintf('Expected session key "%s" not found.', $name)); + } + + return $this->read($name); } /** * Reads and deletes a variable from session. * * @param string $name The key to read and remove (or a path as sent to Hash.extract). - * @return mixed The value of the session variable, null if session not available, + * @return mixed|null The value of the session variable, null if session not available, * session not started, or provided name not found in the session. */ public function consume(string $name) @@ -492,6 +509,7 @@ public function write($name, $value = null): void $data = Hash::insert($data, $key, $val); } + /** @psalm-suppress PossiblyNullArgument */ $this->_overwrite($_SESSION, $data); } diff --git a/app/vendor/cakephp/cakephp/src/Http/Session/CacheSession.php b/app/vendor/cakephp/cakephp/src/Http/Session/CacheSession.php index 1b4366cdb..55790ce1c 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Session/CacheSession.php +++ b/app/vendor/cakephp/cakephp/src/Http/Session/CacheSession.php @@ -104,7 +104,7 @@ public function write($id, $data): bool return false; } - return (bool)Cache::write($id, $data, $this->_options['config']); + return Cache::write($id, $data, $this->_options['config']); } /** diff --git a/app/vendor/cakephp/cakephp/src/Http/Session/DatabaseSession.php b/app/vendor/cakephp/cakephp/src/Http/Session/DatabaseSession.php index 42b3e9fc4..5dc76644a 100644 --- a/app/vendor/cakephp/cakephp/src/Http/Session/DatabaseSession.php +++ b/app/vendor/cakephp/cakephp/src/Http/Session/DatabaseSession.php @@ -18,7 +18,6 @@ */ namespace Cake\Http\Session; -use Cake\ORM\Entity; use Cake\ORM\Locator\LocatorAwareTrait; use SessionHandlerInterface; @@ -58,7 +57,7 @@ public function __construct(array $config = []) $tableLocator = $this->getTableLocator(); if (empty($config['model'])) { - $config = $tableLocator->exists('Sessions') ? [] : ['table' => 'sessions']; + $config = $tableLocator->exists('Sessions') ? [] : ['table' => 'sessions', 'allowFallbackClass' => true]; $this->_table = $tableLocator->get('Sessions', $config); } else { $this->_table = $tableLocator->get($config['model']); @@ -150,14 +149,16 @@ public function write($id, $data): bool if (!$id) { return false; } - $expires = time() + $this->_timeout; - $record = compact('data', 'expires'); + /** @var string $pkField */ $pkField = $this->_table->getPrimaryKey(); - $record[$pkField] = $id; - $result = $this->_table->save(new Entity($record)); + $session = $this->_table->newEntity([ + $pkField => $id, + 'data' => $data, + 'expires' => time() + $this->_timeout, + ], ['accessibleFields' => [$pkField => true]]); - return (bool)$result; + return (bool)$this->_table->save($session); } /** @@ -170,10 +171,7 @@ public function destroy($id): bool { /** @var string $pkField */ $pkField = $this->_table->getPrimaryKey(); - $this->_table->delete(new Entity( - [$pkField => $id], - ['markNew' => false] - )); + $this->_table->deleteAll([$pkField => $id]); return true; } diff --git a/app/vendor/cakephp/cakephp/src/I18n/ChainMessagesLoader.php b/app/vendor/cakephp/cakephp/src/I18n/ChainMessagesLoader.php index 80693adc5..27ccd6cf2 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/ChainMessagesLoader.php +++ b/app/vendor/cakephp/cakephp/src/I18n/ChainMessagesLoader.php @@ -16,7 +16,6 @@ */ namespace Cake\I18n; -use Aura\Intl\Package; use RuntimeException; /** @@ -47,7 +46,7 @@ public function __construct(array $loaders) * Executes this object returning the translations package as configured in * the chain. * - * @return \Aura\Intl\Package + * @return \Cake\I18n\Package * @throws \RuntimeException if any of the loaders in the chain is not a valid callable */ public function __invoke(): Package diff --git a/app/vendor/cakephp/cakephp/src/I18n/Date.php b/app/vendor/cakephp/cakephp/src/I18n/Date.php index 9ae44c786..450018d00 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/Date.php +++ b/app/vendor/cakephp/cakephp/src/I18n/Date.php @@ -40,10 +40,10 @@ class Date extends MutableDate implements I18nDateTimeInterface * will be used for formatting the date part of the object and the second position * will be used to format the time part. * - * @var string|array|int + * @var string|int|int[] * @see \Cake\I18n\DateFormatTrait::i18nFormat() */ - protected static $_toStringFormat = [IntlDateFormatter::SHORT, -1]; + protected static $_toStringFormat = [IntlDateFormatter::SHORT, IntlDateFormatter::NONE]; /** * The format to use when converting this object to JSON. @@ -56,7 +56,7 @@ class Date extends MutableDate implements I18nDateTimeInterface * will be used for formatting the date part of the object and the second position * will be used to format the time part. * - * @var string|array|int + * @var string|int|int[]|\Closure * @see \Cake\I18n\Time::i18nFormat() */ protected static $_jsonEncodeFormat = 'yyyy-MM-dd'; @@ -65,10 +65,10 @@ class Date extends MutableDate implements I18nDateTimeInterface * The format to use when formatting a time using `Cake\I18n\Date::timeAgoInWords()` * and the difference is more than `Cake\I18n\Date::$wordEnd` * - * @var string|array|int + * @var string|int|int[] * @see \Cake\I18n\DateFormatTrait::parseDate() */ - public static $wordFormat = [IntlDateFormatter::SHORT, -1]; + public static $wordFormat = [IntlDateFormatter::SHORT, IntlDateFormatter::NONE]; /** * The format to use when formatting a time using `Cake\I18n\Date::nice()` @@ -81,10 +81,10 @@ class Date extends MutableDate implements I18nDateTimeInterface * will be used for formatting the date part of the object and the second position * will be used to format the time part. * - * @var string|array|int + * @var string|int|int[] * @see \Cake\I18n\DateFormatTrait::nice() */ - public static $niceFormat = [IntlDateFormatter::MEDIUM, -1]; + public static $niceFormat = [IntlDateFormatter::MEDIUM, IntlDateFormatter::NONE]; /** * The format to use when formatting a time using `Date::timeAgoInWords()` @@ -122,10 +122,10 @@ class Date extends MutableDate implements I18nDateTimeInterface * * Date instances lack time components, however due to limitations in PHP's * internal Datetime object the time will always be set to 00:00:00, and the - * timezone will always be UTC. Normalizing the timezone allows for + * timezone will always be the server local time. Normalizing the timezone allows for * subtraction/addition to have deterministic results. * - * @param string|int|\DateTimeInterface|null $time Fixed or relative time + * @param string|int|\DateTime|\DateTimeImmutable|null $time Fixed or relative time * @param \DateTimeZone|string|null $tz The timezone in which the date is taken. * Ignored if `$time` is a DateTimeInterface instance. */ diff --git a/app/vendor/cakephp/cakephp/src/I18n/DateFormatTrait.php b/app/vendor/cakephp/cakephp/src/I18n/DateFormatTrait.php index f50cc1a63..e08a17a04 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/DateFormatTrait.php +++ b/app/vendor/cakephp/cakephp/src/I18n/DateFormatTrait.php @@ -17,10 +17,10 @@ namespace Cake\I18n; use Cake\Chronos\DifferenceFormatterInterface; +use Closure; use DateTime; use DateTimeZone; use IntlDateFormatter; -use InvalidArgumentException; use RuntimeException; /** @@ -35,10 +35,19 @@ trait DateFormatTrait * * Use static::setDefaultLocale() and static::getDefaultLocale() instead. * - * @var string + * @var string|null */ protected static $defaultLocale; + /** + * Whether lenient parsing is enabled for IntlDateFormatter. + * + * Defaults to true which is the default for IntlDateFormatter. + * + * @var bool + */ + protected static $lenientParsing = true; + /** * In-memory cache of date formatters * @@ -59,7 +68,9 @@ public static function getDefaultLocale(): ?string /** * Sets the default locale. * - * @param string|null $locale The default locale string to be used or null. + * Set to null to use IntlDateFormatter default. + * + * @param string|null $locale The default locale string to be used. * @return void */ public static function setDefaultLocale(?string $locale = null): void @@ -67,6 +78,36 @@ public static function setDefaultLocale(?string $locale = null): void static::$defaultLocale = $locale; } + /** + * Gets whether locale format parsing is set to lenient. + * + * @return bool + */ + public static function lenientParsingEnabled(): bool + { + return static::$lenientParsing; + } + + /** + * Enables lenient parsing for locale formats. + * + * @return void + */ + public static function enableLenientParsing(): void + { + static::$lenientParsing = true; + } + + /** + * Enables lenient parsing for locale formats. + * + * @return void + */ + public static function disableLenientParsing(): void + { + static::$lenientParsing = false; + } + /** * Returns a nicely formatted date string for this object. * @@ -90,7 +131,7 @@ public function nice($timezone = null, $locale = null): string * It is possible to specify the desired format for the string to be displayed. * You can either pass `IntlDateFormatter` constants as the first argument of this * function, or pass a full ICU date formatting string as specified in the following - * resource: http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details. + * resource: https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax. * * Additional to `IntlDateFormatter` constants and date formatting string you can use * Time::UNIX_TIMESTAMP_FORMAT to get a unix timestamp @@ -129,7 +170,7 @@ public function nice($timezone = null, $locale = null): string * You can control the default locale used through `Time::setDefaultLocale()`. * If empty, the default will be taken from the `intl.default_locale` ini config. * - * @param string|int|array|null $format Format string. + * @param string|int|int[]|null $format Format string. * @param string|\DateTimeZone|null $timezone Timezone string or DateTimeZone object * in which the date will be displayed. The timezone stored for this object will not * be changed. @@ -161,18 +202,18 @@ public function i18nFormat($format = null, $timezone = null, $locale = null) * Implements what IntlDateFormatter::formatObject() is in PHP 5.5+ * * @param \DateTime|\DateTimeImmutable $date Date. - * @param string|int|array $format Format. + * @param string|int|int[] $format Format. * @param string|null $locale The locale name in which the date should be displayed. * @return string */ protected function _formatObject($date, $format, ?string $locale): string { - $pattern = $timeFormat = null; + $pattern = ''; if (is_array($format)) { [$dateFormat, $timeFormat] = $format; - } elseif (is_numeric($format)) { - $dateFormat = $format; + } elseif (is_int($format)) { + $dateFormat = $timeFormat = $format; } else { $dateFormat = $timeFormat = IntlDateFormatter::FULL; $pattern = $format; @@ -182,8 +223,12 @@ protected function _formatObject($date, $format, ?string $locale): string $locale = I18n::getLocale(); } - // phpcs:ignore Generic.Files.LineLength - if (preg_match('/@calendar=(japanese|buddhist|chinese|persian|indian|islamic|hebrew|coptic|ethiopic)/', $locale)) { + if ( + preg_match( + '/@calendar=(japanese|buddhist|chinese|persian|indian|islamic|hebrew|coptic|ethiopic)/', + $locale + ) + ) { $calendar = IntlDateFormatter::TRADITIONAL; } else { $calendar = IntlDateFormatter::GREGORIAN; @@ -200,13 +245,13 @@ protected function _formatObject($date, $format, ?string $locale): string } $formatter = datefmt_create( $locale, - (int)$dateFormat, - (int)$timeFormat, + $dateFormat, + $timeFormat, $timezone, $calendar, - (string)$pattern + $pattern ); - if ($formatter === false) { + if (empty($formatter)) { throw new RuntimeException( 'Your version of icu does not support creating a date formatter for ' . "`$key`. You should try to upgrade libicu and the intl extension." @@ -248,7 +293,7 @@ public static function resetToStringFormat(): void * will be used for formatting the date part of the object and the second position * will be used to format the time part. * - * @param string|array|int $format Format. + * @param string|int|int[] $format Format. * @return void */ public static function setToStringFormat($format): void @@ -257,19 +302,7 @@ public static function setToStringFormat($format): void } /** - * Sets the default format used when converting this object to json - * - * The format should be either the formatting constants from IntlDateFormatter as - * described in (https://secure.php.net/manual/en/class.intldateformatter.php) or a pattern - * as specified in (http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details) - * - * It is possible to provide an array of 2 constants. In this case, the first position - * will be used for formatting the date part of the object and the second position - * will be used to format the time part. - * - * @see \Cake\I18n\Time::i18nFormat() - * @param string|array|int $format Format. - * @return void + * @inheritDoc */ public static function setJsonEncodeFormat($format): void { @@ -295,42 +328,39 @@ public static function setJsonEncodeFormat($format): void * ``` * $time = Time::parseDateTime('10/13/2013 12:54am'); * $time = Time::parseDateTime('13 Oct, 2013 13:54', 'dd MMM, y H:mm'); - * $time = Time::parseDateTime('10/10/2015', [IntlDateFormatter::SHORT, -1]); + * $time = Time::parseDateTime('10/10/2015', [IntlDateFormatter::SHORT, IntlDateFormatter::NONE]); * ``` * * @param string $time The time string to parse. - * @param string|int[]|null $format Any format accepted by IntlDateFormatter. + * @param string|int|int[]|null $format Any format accepted by IntlDateFormatter. * @param \DateTimeZone|string|null $tz The timezone for the instance * @return static|null - * @throws \InvalidArgumentException If $format is a single int instead of array of constants */ public static function parseDateTime(string $time, $format = null, $tz = null) { - $dateFormat = $format ?: static::$_toStringFormat; - $timeFormat = $pattern = null; + $format = $format ?? static::$_toStringFormat; + $pattern = ''; - if (is_array($dateFormat)) { - [$newDateFormat, $timeFormat] = $dateFormat; - $dateFormat = $newDateFormat; + if (is_array($format)) { + [$dateFormat, $timeFormat] = $format; + } elseif (is_int($format)) { + $dateFormat = $timeFormat = $format; } else { - $pattern = $dateFormat; - $dateFormat = null; - - if (is_int($pattern)) { - throw new InvalidArgumentException( - 'If $format is an IntlDateFormatter constant, must be an array.' - ); - } + $dateFormat = $timeFormat = IntlDateFormatter::FULL; + $pattern = $format; } + $locale = static::$defaultLocale ?? I18n::getLocale(); $formatter = datefmt_create( - (string)static::$defaultLocale, - $dateFormat ?? 0, - $timeFormat ?? 0, + $locale, + $dateFormat, + $timeFormat, $tz, null, - $pattern ?? '' + $pattern ); + $formatter->setLenient(static::$lenientParsing); + $time = $formatter->parse($time); if ($time !== false) { $dateTime = new DateTime('@' . $time); @@ -371,7 +401,7 @@ public static function parseDateTime(string $time, $format = null, $tz = null) public static function parseDate(string $date, $format = null) { if (is_int($format)) { - $format = [$format, -1]; + $format = [$format, IntlDateFormatter::NONE]; } $format = $format ?: static::$wordFormat; @@ -401,20 +431,24 @@ public static function parseDate(string $date, $format = null) public static function parseTime(string $time, $format = null) { if (is_int($format)) { - $format = [-1, $format]; + $format = [IntlDateFormatter::NONE, $format]; } - $format = $format ?: [-1, IntlDateFormatter::SHORT]; + $format = $format ?: [IntlDateFormatter::NONE, IntlDateFormatter::SHORT]; return static::parseDateTime($time, $format); } /** - * Returns a string that should be serialized when converting this object to json + * Returns a string that should be serialized when converting this object to JSON * * @return string|int */ public function jsonSerialize() { + if (static::$_jsonEncodeFormat instanceof Closure) { + return call_user_func(static::$_jsonEncodeFormat, $this); + } + return $this->i18nFormat(static::$_jsonEncodeFormat); } diff --git a/app/vendor/cakephp/cakephp/src/I18n/Exception/I18nException.php b/app/vendor/cakephp/cakephp/src/I18n/Exception/I18nException.php new file mode 100644 index 000000000..3b3819e3e --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/I18n/Exception/I18nException.php @@ -0,0 +1,27 @@ +_formatMessage($locale, $message, $vars); - } - - /** - * Does the actual formatting using the MessageFormatter class - * - * @param string $locale The locale in which the message is presented. - * @param string $message The message to be translated - * @param array $vars The list of values to interpolate in the message - * @return string The formatted message - * @throws \Aura\Intl\Exception\CannotInstantiateFormatter if any error occurred - * while parsing the message - * @throws \Aura\Intl\Exception\CannotFormat If any error related to the passed - * variables is found - */ - protected function _formatMessage(string $locale, string $message, array $vars): string + public function format(string $locale, string $message, array $tokenValues): string { if ($message === '') { return $message; } - // Using procedural style as it showed twice as fast as - // its counterpart in PHP 5.5 - $result = MessageFormatter::formatMessage($locale, $message, $vars); + $formatter = new MessageFormatter($locale, $message); + $result = $formatter->format($tokenValues); if ($result === false) { - // The user might be interested in what went wrong, so replay the - // previous action using the object oriented style to figure out - $formatter = new MessageFormatter($locale, $message); - $formatter->format($vars); - throw new CannotFormat($formatter->getErrorMessage(), $formatter->getErrorCode()); + throw new I18nException($formatter->getErrorMessage(), $formatter->getErrorCode()); } return $result; diff --git a/app/vendor/cakephp/cakephp/src/I18n/Formatter/SprintfFormatter.php b/app/vendor/cakephp/cakephp/src/I18n/Formatter/SprintfFormatter.php index 2609ce023..84c5d7bdd 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/Formatter/SprintfFormatter.php +++ b/app/vendor/cakephp/cakephp/src/I18n/Formatter/SprintfFormatter.php @@ -16,7 +16,7 @@ */ namespace Cake\I18n\Formatter; -use Aura\Intl\FormatterInterface; +use Cake\I18n\FormatterInterface; /** * A formatter that will interpolate variables using sprintf and @@ -30,13 +30,11 @@ class SprintfFormatter implements FormatterInterface * * @param string $locale The locale in which the message is presented. * @param string $message The message to be translated - * @param array $vars The list of values to interpolate in the message + * @param array $tokenValues The list of values to interpolate in the message * @return string The formatted message */ - public function format($locale, $message, array $vars): string + public function format(string $locale, string $message, array $tokenValues): string { - unset($vars['_singular']); - - return vsprintf($message, $vars); + return vsprintf($message, $tokenValues); } } diff --git a/app/vendor/cakephp/cakephp/src/I18n/FormatterInterface.php b/app/vendor/cakephp/cakephp/src/I18n/FormatterInterface.php new file mode 100644 index 000000000..0f8311670 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/I18n/FormatterInterface.php @@ -0,0 +1,35 @@ + $spec) { + $this->set($name, $spec); + } + } + + /** + * Sets a formatter into the registry by name. + * + * @param string $name The formatter name. + * @param string $className A FQCN for a formatter. + * @return void + */ + public function set(string $name, string $className): void + { + $this->registry[$name] = $className; + $this->converted[$name] = false; + } + + /** + * Gets a formatter from the registry by name. + * + * @param string $name The formatter to retrieve. + * @return \Cake\I18n\FormatterInterface A formatter object. + * @throws \Cake\I18n\Exception\I18nException + */ + public function get(string $name): FormatterInterface + { + if (!isset($this->registry[$name])) { + throw new I18nException("Formatter named `{$name}` has not been registered"); + } + + if (!$this->converted[$name]) { + $this->registry[$name] = new $this->registry[$name](); + $this->converted[$name] = true; + } + + return $this->registry[$name]; + } +} diff --git a/app/vendor/cakephp/cakephp/src/I18n/FrozenDate.php b/app/vendor/cakephp/cakephp/src/I18n/FrozenDate.php index a87f0132c..a4602bfdd 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/FrozenDate.php +++ b/app/vendor/cakephp/cakephp/src/I18n/FrozenDate.php @@ -42,7 +42,7 @@ class FrozenDate extends ChronosDate implements I18nDateTimeInterface * will be used for formatting the date part of the object and the second position * will be used to format the time part. * - * @var string|array|int + * @var string|int|int[] * @see \Cake\I18n\DateFormatTrait::i18nFormat() */ protected static $_toStringFormat = [IntlDateFormatter::SHORT, -1]; @@ -58,7 +58,7 @@ class FrozenDate extends ChronosDate implements I18nDateTimeInterface * will be used for formatting the date part of the object and the second position * will be used to format the time part. * - * @var string|array|int + * @var string|int|int[]|\Closure * @see \Cake\I18n\Time::i18nFormat() */ protected static $_jsonEncodeFormat = 'yyyy-MM-dd'; @@ -67,10 +67,10 @@ class FrozenDate extends ChronosDate implements I18nDateTimeInterface * The format to use when formatting a time using `Cake\I18n\Date::timeAgoInWords()` * and the difference is more than `Cake\I18n\Date::$wordEnd` * - * @var string|array|int + * @var string|int|int[] * @see \Cake\I18n\DateFormatTrait::parseDate() */ - public static $wordFormat = [IntlDateFormatter::SHORT, -1]; + public static $wordFormat = [IntlDateFormatter::SHORT, IntlDateFormatter::NONE]; /** * The format to use when formatting a time using `Cake\I18n\Date::nice()` @@ -83,10 +83,10 @@ class FrozenDate extends ChronosDate implements I18nDateTimeInterface * will be used for formatting the date part of the object and the second position * will be used to format the time part. * - * @var string|array|int + * @var string|int|int[] * @see \Cake\I18n\DateFormatTrait::nice() */ - public static $niceFormat = [IntlDateFormatter::MEDIUM, -1]; + public static $niceFormat = [IntlDateFormatter::MEDIUM, IntlDateFormatter::NONE]; /** * The format to use when formatting a time using `Date::timeAgoInWords()` @@ -124,10 +124,10 @@ class FrozenDate extends ChronosDate implements I18nDateTimeInterface * * Date instances lack time components, however due to limitations in PHP's * internal Datetime object the time will always be set to 00:00:00, and the - * timezone will always be UTC. Normalizing the timezone allows for + * timezone will always be the server local time. Normalizing the timezone allows for * subtraction/addition to have deterministic results. * - * @param string|int|\DateTimeInterface|null $time Fixed or relative time + * @param string|int|\DateTime|\DateTimeImmutable|null $time Fixed or relative time * @param \DateTimeZone|string|null $tz The timezone in which the date is taken. * Ignored if `$time` is a DateTimeInterface instance. */ diff --git a/app/vendor/cakephp/cakephp/src/I18n/FrozenTime.php b/app/vendor/cakephp/cakephp/src/I18n/FrozenTime.php index bd342a5eb..2bf669c0e 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/FrozenTime.php +++ b/app/vendor/cakephp/cakephp/src/I18n/FrozenTime.php @@ -43,7 +43,7 @@ class FrozenTime extends Chronos implements I18nDateTimeInterface * will be used for formatting the date part of the object and the second position * will be used to format the time part. * - * @var string|array|int + * @var string|int|int[] * @see \Cake\I18n\FrozenTime::i18nFormat() */ protected static $_toStringFormat = [IntlDateFormatter::SHORT, IntlDateFormatter::SHORT]; @@ -59,7 +59,7 @@ class FrozenTime extends Chronos implements I18nDateTimeInterface * will be used for formatting the date part of the object and the second position * will be used to format the time part. * - * @var string|array|int + * @var string|int|int[]|\Closure * @see \Cake\I18n\Time::i18nFormat() */ protected static $_jsonEncodeFormat = "yyyy-MM-dd'T'HH':'mm':'ssxxx"; @@ -75,7 +75,7 @@ class FrozenTime extends Chronos implements I18nDateTimeInterface * will be used for formatting the date part of the object and the second position * will be used to format the time part. * - * @var string|array|int + * @var string|int|int[] * @see \Cake\I18n\FrozenTime::nice() */ public static $niceFormat = [IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT]; @@ -84,10 +84,10 @@ class FrozenTime extends Chronos implements I18nDateTimeInterface * The format to use when formatting a time using `Cake\I18n\FrozenTime::timeAgoInWords()` * and the difference is more than `Cake\I18n\FrozenTime::$wordEnd` * - * @var string|array|int + * @var string|int|int[] * @see \Cake\I18n\FrozenTime::timeAgoInWords() */ - public static $wordFormat = [IntlDateFormatter::SHORT, -1]; + public static $wordFormat = [IntlDateFormatter::SHORT, IntlDateFormatter::NONE]; /** * The format to use when formatting a time using `Time::timeAgoInWords()` diff --git a/app/vendor/cakephp/cakephp/src/I18n/I18n.php b/app/vendor/cakephp/cakephp/src/I18n/I18n.php index b1ae94ef9..2e5c539d7 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/I18n.php +++ b/app/vendor/cakephp/cakephp/src/I18n/I18n.php @@ -16,11 +16,8 @@ */ namespace Cake\I18n; -use Aura\Intl\FormatterLocator; -use Aura\Intl\PackageLocator; -use Aura\Intl\TranslatorInterface; use Cake\Cache\Cache; -use Cake\Core\Exception\Exception; +use Cake\I18n\Exception\I18nException; use Cake\I18n\Formatter\IcuFormatter; use Cake\I18n\Formatter\SprintfFormatter; use Locale; @@ -47,7 +44,7 @@ class I18n /** * The environment default locale * - * @var string + * @var string|null */ protected static $_defaultLocale; @@ -67,14 +64,9 @@ public static function translators(): TranslatorRegistry static::$_collection = new TranslatorRegistry( new PackageLocator(), new FormatterLocator([ - 'sprintf' => function () { - return new SprintfFormatter(); - }, - 'default' => function () { - return new IcuFormatter(); - }, + 'default' => IcuFormatter::class, + 'sprintf' => SprintfFormatter::class, ]), - new TranslatorFactory(), static::getLocale() ); @@ -95,7 +87,7 @@ public static function translators(): TranslatorRegistry * * ``` * I18n::setTranslator('default', function () { - * $package = new \Aura\Intl\Package(); + * $package = new \Cake\I18n\Package(); * $package->setMessages([ * 'Cake' => 'Gâteau' * ]); @@ -141,10 +133,10 @@ public static function setTranslator(string $name, callable $loader, ?string $lo * * @param string $name The domain of the translation messages. * @param string|null $locale The locale for the translator. - * @return \Aura\Intl\TranslatorInterface The configured translator. - * @throws \Aura\Intl\Exception + * @return \Cake\I18n\Translator The configured translator. + * @throws \Cake\I18n\Exception\I18nException */ - public static function getTranslator(string $name = 'default', ?string $locale = null): TranslatorInterface + public static function getTranslator(string $name = 'default', ?string $locale = null): Translator { $translators = static::translators(); @@ -155,7 +147,7 @@ public static function getTranslator(string $name = 'default', ?string $locale = $translator = $translators->get($name); if ($translator === null) { - throw new Exception(sprintf( + throw new I18nException(sprintf( 'Translator for domain "%s" could not be found.', $name )); @@ -180,7 +172,7 @@ public static function getTranslator(string $name = 'default', ?string $locale = * * Loader objects will receive two arguments: The domain name that needs to be * built, and the locale that is requested. These objects can assemble the messages - * from any source, but must return an `Aura\Intl\Package` object. + * from any source, but must return an `Cake\I18n\Package` object. * * ### Example: * @@ -196,7 +188,7 @@ public static function getTranslator(string $name = 'default', ?string $locale = * You can also assemble the package object yourself: * * ``` - * use Aura\Intl\Package; + * use Cake\I18n\Package; * I18n::config('my_domain', function ($name, $locale) { * $package = new Package('default'); * $messages = (...); // Fetch messages for locale from external service. diff --git a/app/vendor/cakephp/cakephp/src/I18n/I18nDateTimeInterface.php b/app/vendor/cakephp/cakephp/src/I18n/I18nDateTimeInterface.php index 02822624a..88b5060ce 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/I18nDateTimeInterface.php +++ b/app/vendor/cakephp/cakephp/src/I18n/I18nDateTimeInterface.php @@ -122,15 +122,27 @@ public static function resetToStringFormat(): void; /** * Sets the default format used when type converting instances of this type to string * - * @param string|array|int $format Format. + * @param string|int|int[] $format Format. * @return void */ public static function setToStringFormat($format): void; /** - * Sets the default format used when converting this object to json + * Sets the default format used when converting this object to JSON * - * @param string|array|int $format Format. + * The format should be either the formatting constants from IntlDateFormatter as + * described in (https://secure.php.net/manual/en/class.intldateformatter.php) or a pattern + * as specified in (http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details) + * + * It is possible to provide an array of 2 constants. In this case, the first position + * will be used for formatting the date part of the object and the second position + * will be used to format the time part. + * + * Alternatively, the format can provide a callback. In this case, the callback + * can receive this datetime object and return a formatted string. + * + * @see \Cake\I18n\Time::i18nFormat() + * @param string|array|int|\Closure $format Format. * @return void */ public static function setJsonEncodeFormat($format): void; diff --git a/app/vendor/cakephp/cakephp/src/I18n/LICENSE.txt b/app/vendor/cakephp/cakephp/src/I18n/LICENSE.txt index 0b3b94303..b938c9e8e 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/LICENSE.txt +++ b/app/vendor/cakephp/cakephp/src/I18n/LICENSE.txt @@ -1,7 +1,7 @@ The MIT License (MIT) CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) -Copyright (c) 2005-2019, Cake Software Foundation, Inc. (https://cakefoundation.org) +Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/vendor/cakephp/cakephp/src/I18n/MessagesFileLoader.php b/app/vendor/cakephp/cakephp/src/I18n/MessagesFileLoader.php index 662625b00..564077d6c 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/MessagesFileLoader.php +++ b/app/vendor/cakephp/cakephp/src/I18n/MessagesFileLoader.php @@ -16,7 +16,6 @@ */ namespace Cake\I18n; -use Aura\Intl\Package; use Cake\Core\App; use Cake\Core\Plugin; use Cake\Utility\Inflector; @@ -102,7 +101,7 @@ public function __construct(string $name, string $locale, string $extension = 'p * Loads the translation file and parses it. Returns an instance of a translations * package containing the messages loaded from the file. * - * @return \Aura\Intl\Package|false + * @return \Cake\I18n\Package|false * @throws \RuntimeException if no file parser class could be found for the specified * file extension. */ diff --git a/app/vendor/cakephp/cakephp/src/I18n/Number.php b/app/vendor/cakephp/cakephp/src/I18n/Number.php index 33d70c814..03f53f996 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/Number.php +++ b/app/vendor/cakephp/cakephp/src/I18n/Number.php @@ -74,7 +74,7 @@ class Number /** * Default currency format used by Number::currency() * - * @var string + * @var string|null */ protected static $_defaultCurrencyFormat; @@ -141,7 +141,7 @@ public static function toPercentage($value, int $precision = 2, array $options = { $options += ['multiply' => false, 'type' => NumberFormatter::PERCENT]; if (!$options['multiply']) { - $value /= 100; + $value = (float)$value / 100; } return static::precision($value, $precision, $options); @@ -259,11 +259,7 @@ public static function currency($value, ?string $currency = null, array $options $before = $options['before'] ?? ''; $after = $options['after'] ?? ''; - if ($currency) { - $value = $formatter->formatCurrency($value, $currency); - } else { - $formatter->format($value, NumberFormatter::TYPE_CURRENCY); - } + $value = $formatter->formatCurrency($value, $currency); return $before . $value . $after; } @@ -404,14 +400,17 @@ public static function formatter(array $options = []): NumberFormatter /** @var \NumberFormatter $formatter */ $formatter = static::$_formatters[$locale][$type]; - $options = array_intersect_key($options, [ - 'places' => null, - 'precision' => null, - 'pattern' => null, - 'useIntlCode' => null, - ]); - if (empty($options)) { - return $formatter; + // PHP 8.0.0 - 8.0.6 throws an exception when cloning NumberFormatter after a failed parse + if (version_compare(PHP_VERSION, '8.0.6', '>') || version_compare(PHP_VERSION, '8.0.0', '<')) { + $options = array_intersect_key($options, [ + 'places' => null, + 'precision' => null, + 'pattern' => null, + 'useIntlCode' => null, + ]); + if (empty($options)) { + return $formatter; + } } $formatter = clone $formatter; diff --git a/app/vendor/cakephp/cakephp/src/I18n/Package.php b/app/vendor/cakephp/cakephp/src/I18n/Package.php new file mode 100644 index 000000000..680e13bbd --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/I18n/Package.php @@ -0,0 +1,164 @@ + + */ + protected $messages = []; + + /** + * The name of a fallback package to use when a message key does not + * exist. + * + * @var string|null + */ + protected $fallback; + + /** + * The name of the formatter to use when formatting translated messages. + * + * @var string + */ + protected $formatter; + + /** + * Constructor. + * + * @param string $formatter The name of the formatter to use. + * @param string|null $fallback The name of the fallback package to use. + * @param array $messages The messages in this package. + */ + public function __construct( + string $formatter = 'default', + ?string $fallback = null, + array $messages = [] + ) { + $this->formatter = $formatter; + $this->fallback = $fallback; + $this->messages = $messages; + } + + /** + * Sets the messages for this package. + * + * @param array $messages The messages for this package. + * @return void + */ + public function setMessages(array $messages): void + { + $this->messages = $messages; + } + + /** + * Adds one message for this package. + * + * @param string $key the key of the message + * @param string|array $message the actual message + * @return void + */ + public function addMessage(string $key, $message): void + { + $this->messages[$key] = $message; + } + + /** + * Adds new messages for this package. + * + * @param array $messages The messages to add in this package. + * @return void + */ + public function addMessages(array $messages): void + { + $this->messages = array_merge($this->messages, $messages); + } + + /** + * Gets the messages for this package. + * + * @return array + */ + public function getMessages(): array + { + return $this->messages; + } + + /** + * Gets the message of the given key for this package. + * + * @param string $key the key of the message to return + * @return string|array|false The message translation, or false if not found. + */ + public function getMessage(string $key) + { + if (isset($this->messages[$key])) { + return $this->messages[$key]; + } + + return false; + } + + /** + * Sets the formatter name for this package. + * + * @param string $formatter The formatter name for this package. + * @return void + */ + public function setFormatter(string $formatter): void + { + $this->formatter = $formatter; + } + + /** + * Gets the formatter name for this package. + * + * @return string + */ + public function getFormatter(): string + { + return $this->formatter; + } + + /** + * Sets the fallback package name. + * + * @param string|null $fallback The fallback package name. + * @return void + */ + public function setFallback(?string $fallback): void + { + $this->fallback = $fallback; + } + + /** + * Gets the fallback package name. + * + * @return string|null + */ + public function getFallback(): ?string + { + return $this->fallback; + } +} diff --git a/app/vendor/cakephp/cakephp/src/I18n/PackageLocator.php b/app/vendor/cakephp/cakephp/src/I18n/PackageLocator.php new file mode 100644 index 000000000..6cc3f8bcf --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/I18n/PackageLocator.php @@ -0,0 +1,110 @@ + $locales) { + foreach ($locales as $locale => $spec) { + $this->set($name, $locale, $spec); + } + } + } + + /** + * Sets a Package loader. + * + * @param string $name The package name. + * @param string $locale The locale for the package. + * @param callable|\Cake\I18n\Package $spec A callable that returns a package or Package instance. + * @return void + */ + public function set(string $name, string $locale, $spec): void + { + $this->registry[$name][$locale] = $spec; + $this->converted[$name][$locale] = $spec instanceof Package; + } + + /** + * Gets a Package object. + * + * @param string $name The package name. + * @param string $locale The locale for the package. + * @return \Cake\I18n\Package + */ + public function get(string $name, string $locale): Package + { + if (!isset($this->registry[$name][$locale])) { + throw new I18nException("Package '$name' with locale '$locale' is not registered."); + } + + if (!$this->converted[$name][$locale]) { + $func = $this->registry[$name][$locale]; + $this->registry[$name][$locale] = $func(); + $this->converted[$name][$locale] = true; + } + + return $this->registry[$name][$locale]; + } + + /** + * Check if a Package object for given name and locale exists in registry. + * + * @param string $name The package name. + * @param string $locale The locale for the package. + * @return bool + */ + public function has(string $name, string $locale): bool + { + return isset($this->registry[$name][$locale]); + } +} diff --git a/app/vendor/cakephp/cakephp/src/I18n/Parser/MoFileParser.php b/app/vendor/cakephp/cakephp/src/I18n/Parser/MoFileParser.php index 7285ad224..69633363a 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/Parser/MoFileParser.php +++ b/app/vendor/cakephp/cakephp/src/I18n/Parser/MoFileParser.php @@ -121,11 +121,11 @@ public function parse($file): array if ($pluralId !== null || strpos($translated, "\000") !== false) { $translated = explode("\000", $translated); - $plurals = $pluralId !== null ? array_map('stripcslashes', $translated) : null; + $plurals = $pluralId !== null ? $translated : null; $translated = $translated[0]; } - $singular = stripcslashes($translated); + $singular = $translated; if ($context !== null) { $messages[$singularId]['_context'][$context] = $singular; if ($pluralId !== null) { diff --git a/app/vendor/cakephp/cakephp/src/I18n/PluralRules.php b/app/vendor/cakephp/cakephp/src/I18n/PluralRules.php index bad31ab85..b52514fe1 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/PluralRules.php +++ b/app/vendor/cakephp/cakephp/src/I18n/PluralRules.php @@ -16,7 +16,7 @@ */ namespace Cake\I18n; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Utility class used to determine the plural number to be used for a variable @@ -200,6 +200,6 @@ public static function calculate(string $locale, $n): int return $n % 10 !== 1 || $n % 100 === 11 ? 1 : 0; } - throw new Exception('Unable to find plural rule number.'); + throw new CakeException('Unable to find plural rule number.'); } } diff --git a/app/vendor/cakephp/cakephp/src/I18n/README.md b/app/vendor/cakephp/cakephp/src/I18n/README.md index 74df408b8..e7724b8fe 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/README.md +++ b/app/vendor/cakephp/cakephp/src/I18n/README.md @@ -50,7 +50,7 @@ Hi Charles, your balance on the Jan 13, 2014, 11:12 AM is $ 1,354.37 ```php use Cake\I18n\I18n; -use Aura\Intl\Package; +use Cake\I18n\Package; I18n::translator('animals', 'fr_FR', function () { $package = new Package( diff --git a/app/vendor/cakephp/cakephp/src/I18n/Time.php b/app/vendor/cakephp/cakephp/src/I18n/Time.php index 2705dbcdd..fdafc256e 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/Time.php +++ b/app/vendor/cakephp/cakephp/src/I18n/Time.php @@ -41,7 +41,7 @@ class Time extends MutableDateTime implements I18nDateTimeInterface * will be used for formatting the date part of the object and the second position * will be used to format the time part. * - * @var string|array|int + * @var string|int|int[] * @see \Cake\I18n\Time::i18nFormat() */ protected static $_toStringFormat = [IntlDateFormatter::SHORT, IntlDateFormatter::SHORT]; @@ -57,7 +57,7 @@ class Time extends MutableDateTime implements I18nDateTimeInterface * will be used for formatting the date part of the object and the second position * will be used to format the time part. * - * @var string|array|int + * @var string|int|int[]|\Closure * @see \Cake\I18n\Time::i18nFormat() */ protected static $_jsonEncodeFormat = "yyyy-MM-dd'T'HH':'mm':'ssxxx"; @@ -73,7 +73,7 @@ class Time extends MutableDateTime implements I18nDateTimeInterface * will be used for formatting the date part of the object and the second position * will be used to format the time part. * - * @var string|array|int + * @var string|int|int[] * @see \Cake\I18n\Time::nice() */ public static $niceFormat = [IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT]; @@ -82,10 +82,10 @@ class Time extends MutableDateTime implements I18nDateTimeInterface * The format to use when formatting a time using `Cake\I18n\Time::timeAgoInWords()` * and the difference is more than `Cake\I18n\Time::$wordEnd` * - * @var string|array|int + * @var string|int|int[] * @see \Cake\I18n\Time::timeAgoInWords() */ - public static $wordFormat = [IntlDateFormatter::SHORT, -1]; + public static $wordFormat = [IntlDateFormatter::SHORT, IntlDateFormatter::NONE]; /** * The format to use when formatting a time using `Time::timeAgoInWords()` diff --git a/app/vendor/cakephp/cakephp/src/I18n/Translator.php b/app/vendor/cakephp/cakephp/src/I18n/Translator.php index 50ecd85a5..58f771fbc 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/Translator.php +++ b/app/vendor/cakephp/cakephp/src/I18n/Translator.php @@ -7,27 +7,100 @@ * * Licensed under The MIT License * For full copyright and license information, please see the LICENSE.txt - * Redistributions of files must retain the above copyright notice + * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) * @link https://cakephp.org CakePHP(tm) Project + * @since 3.3.12 + * @license https://opensource.org/licenses/mit-license.php MIT License */ namespace Cake\I18n; -use Aura\Intl\Translator as BaseTranslator; - /** - * Provides missing message behavior for CakePHP internal message formats. + * Translator to translate the message. * * @internal */ -class Translator extends BaseTranslator +class Translator { /** * @var string */ public const PLURAL_PREFIX = 'p:'; + /** + * A fallback translator. + * + * @var \Cake\I18n\Translator|null + */ + protected $fallback; + + /** + * The formatter to use when translating messages. + * + * @var \Cake\I18n\FormatterInterface + */ + protected $formatter; + + /** + * The locale being used for translations. + * + * @var string + */ + protected $locale; + + /** + * The Package containing keys and translations. + * + * @var \Cake\I18n\Package + */ + protected $package; + + /** + * Constructor + * + * @param string $locale The locale being used. + * @param \Cake\I18n\Package $package The Package containing keys and translations. + * @param \Cake\I18n\FormatterInterface $formatter A message formatter. + * @param \Cake\I18n\Translator $fallback A fallback translator. + */ + public function __construct( + string $locale, + Package $package, + FormatterInterface $formatter, + ?Translator $fallback = null + ) { + $this->locale = $locale; + $this->package = $package; + $this->formatter = $formatter; + $this->fallback = $fallback; + } + + /** + * Gets the message translation by its key. + * + * @param string $key The message key. + * @return mixed The message translation string, or false if not found. + */ + protected function getMessage(string $key) + { + $message = $this->package->getMessage($key); + if ($message) { + return $message; + } + + if ($this->fallback) { + $message = $this->fallback->getMessage($key); + if ($message) { + $this->package->addMessage($key, $message); + + return $message; + } + } + + return false; + } + /** * Translates the message formatting any placeholders * @@ -36,7 +109,7 @@ class Translator extends BaseTranslator * message. * @return string The translated message with tokens replaced. */ - public function translate($key, array $tokensValues = []): string + public function translate(string $key, array $tokensValues = []): string { if (isset($tokensValues['_count'])) { $message = $this->getMessage(static::PLURAL_PREFIX . $key); @@ -56,7 +129,7 @@ public function translate($key, array $tokensValues = []): string } // Check for missing/invalid context - if (isset($message['_context'])) { + if (is_array($message) && isset($message['_context'])) { $message = $this->resolveContext($key, $message, $tokensValues); unset($tokensValues['_context']); } @@ -86,6 +159,8 @@ public function translate($key, array $tokensValues = []): string $message = $key; } + unset($tokensValues['_count'], $tokensValues['_singular']); + return $this->formatter->format($this->locale, $message, $tokensValues); } @@ -118,4 +193,14 @@ protected function resolveContext(string $key, array $message, array $vars) return $message['_context'][$context]; } + + /** + * Returns the translator package + * + * @return \Cake\I18n\Package + */ + public function getPackage(): Package + { + return $this->package; + } } diff --git a/app/vendor/cakephp/cakephp/src/I18n/TranslatorFactory.php b/app/vendor/cakephp/cakephp/src/I18n/TranslatorFactory.php deleted file mode 100644 index 29478392e..000000000 --- a/app/vendor/cakephp/cakephp/src/I18n/TranslatorFactory.php +++ /dev/null @@ -1,66 +0,0 @@ - - */ - protected $class = Translator::class; - - /** - * Returns a new Translator. - * - * @param string $locale The locale code for the translator. - * @param \Aura\Intl\Package $package The Package containing keys and translations. - * @param \Aura\Intl\FormatterInterface $formatter The formatter to use for interpolating token values. - * @param \Aura\Intl\TranslatorInterface $fallback A fallback translator to use, if any. - * @throws \Cake\Core\Exception\Exception If fallback class does not match Cake\I18n\Translator - * @return \Cake\I18n\Translator - */ - public function newInstance( - $locale, - Package $package, - FormatterInterface $formatter, - ?TranslatorInterface $fallback = null - ) { - $class = $this->class; - if ($fallback !== null && get_class($fallback) !== $class) { - throw new RuntimeException(sprintf( - 'Translator fallback class %s does not match Cake\I18n\Translator, try clearing your _cake_core_ cache', - get_class($fallback) - )); - } - - return new $class($locale, $package, $formatter, $fallback); - } -} diff --git a/app/vendor/cakephp/cakephp/src/I18n/TranslatorRegistry.php b/app/vendor/cakephp/cakephp/src/I18n/TranslatorRegistry.php index e00734528..181dde02b 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/TranslatorRegistry.php +++ b/app/vendor/cakephp/cakephp/src/I18n/TranslatorRegistry.php @@ -16,19 +16,48 @@ */ namespace Cake\I18n; -use Aura\Intl\Exception; -use Aura\Intl\FormatterLocator; -use Aura\Intl\PackageLocator; -use Aura\Intl\TranslatorInterface; -use Aura\Intl\TranslatorLocator; -use Closure; - /** * Constructs and stores instances of translators that can be * retrieved by name and locale. */ -class TranslatorRegistry extends TranslatorLocator +class TranslatorRegistry { + /** + * Fallback loader name. + * + * @var string + */ + public const FALLBACK_LOADER = '_fallback'; + + /** + * A registry to retain translator objects. + * + * @var array + * @psalm-var array> + */ + protected $registry = []; + + /** + * The current locale code. + * + * @var string + */ + protected $locale; + + /** + * A package locator. + * + * @var \Cake\I18n\PackageLocator + */ + protected $packages; + + /** + * A formatter locator. + * + * @var \Cake\I18n\FormatterLocator + */ + protected $formatters; + /** * A list of loader functions indexed by domain name. Loaders are * callables that are invoked as a default for building translation @@ -39,13 +68,6 @@ class TranslatorRegistry extends TranslatorLocator */ protected $_loaders = []; - /** - * Fallback loader name - * - * @var string - */ - protected $_fallbackLoader = '_fallback'; - /** * The name of the default formatter to use for newly created * translators from the fallback loader @@ -72,40 +94,74 @@ class TranslatorRegistry extends TranslatorLocator /** * Constructor. * - * @param \Aura\Intl\PackageLocator $packages The package locator. - * @param \Aura\Intl\FormatterLocator $formatters The formatter locator. - * @param \Cake\I18n\TranslatorFactory $factory A translator factory to - * create translator objects for the locale and package. + * @param \Cake\I18n\PackageLocator $packages The package locator. + * @param \Cake\I18n\FormatterLocator $formatters The formatter locator. * @param string $locale The default locale code to use. */ public function __construct( PackageLocator $packages, FormatterLocator $formatters, - TranslatorFactory $factory, string $locale ) { - parent::__construct($packages, $formatters, $factory, $locale); + $this->packages = $packages; + $this->formatters = $formatters; + $this->setLocale($locale); - $this->registerLoader($this->_fallbackLoader, function ($name, $locale) { - $chain = new ChainMessagesLoader([ + $this->registerLoader(static::FALLBACK_LOADER, function ($name, $locale) { + $loader = new ChainMessagesLoader([ new MessagesFileLoader($name, $locale, 'mo'), new MessagesFileLoader($name, $locale, 'po'), ]); - // \Aura\Intl\Package by default uses formatter configured with key "basic". - // and we want to make sure the cake domain always uses the default formatter $formatter = $name === 'cake' ? 'default' : $this->_defaultFormatter; - $chain = function () use ($formatter, $chain) { - $package = $chain(); - $package->setFormatter($formatter); - - return $package; - }; + $package = $loader(); + $package->setFormatter($formatter); - return $chain; + return $package; }); } + /** + * Sets the default locale code. + * + * @param string $locale The new locale code. + * @return void + */ + public function setLocale(string $locale): void + { + $this->locale = $locale; + } + + /** + * Returns the default locale code. + * + * @return string + */ + public function getLocale(): string + { + return $this->locale; + } + + /** + * Returns the translator packages + * + * @return \Cake\I18n\PackageLocator + */ + public function getPackages(): PackageLocator + { + return $this->packages; + } + + /** + * An object of type FormatterLocator + * + * @return \Cake\I18n\FormatterLocator + */ + public function getFormatters(): FormatterLocator + { + return $this->formatters; + } + /** * Sets the CacheEngine instance used to remember translators across * requests. @@ -121,20 +177,15 @@ public function setCacher($cacher): void /** * Gets a translator from the registry by package for a locale. * - * @param string|null $name The translator package to retrieve. + * @param string $name The translator package to retrieve. * @param string|null $locale The locale to use; if empty, uses the default * locale. - * @return \Aura\Intl\TranslatorInterface|null A translator object. - * @throws \Aura\Intl\Exception If no translator with that name could be found + * @return \Cake\I18n\Translator|null A translator object. + * @throws \Cake\I18n\Exception\I18nException If no translator with that name could be found * for the given locale. - * @psalm-suppress ImplementedReturnTypeMismatch */ - public function get($name, $locale = null) + public function get(string $name, ?string $locale = null): ?Translator { - if (!$name) { - return null; - } - if ($locale === null) { $locale = $this->getLocale(); } @@ -165,20 +216,43 @@ public function get($name, $locale = null) * @param string $name The translator package to retrieve. * @param string $locale The locale to use; if empty, uses the default * locale. - * @return \Aura\Intl\TranslatorInterface A translator object. + * @return \Cake\I18n\Translator A translator object. */ - protected function _getTranslator(string $name, string $locale): TranslatorInterface + protected function _getTranslator(string $name, string $locale): Translator { - try { - return parent::get($name, $locale); - } catch (Exception $e) { + if ($this->packages->has($name, $locale)) { + return $this->createInstance($name, $locale); } - if (!isset($this->_loaders[$name])) { - $this->registerLoader($name, $this->_partialLoader()); + if (isset($this->_loaders[$name])) { + $package = $this->_loaders[$name]($name, $locale); + } else { + $package = $this->_loaders[static::FALLBACK_LOADER]($name, $locale); + } + + $package = $this->setFallbackPackage($name, $package); + $this->packages->set($name, $locale, $package); + + return $this->createInstance($name, $locale); + } + + /** + * Create translator instance. + * + * @param string $name The translator package to retrieve. + * @param string $locale The locale to use; if empty, uses the default locale. + * @return \Cake\I18n\Translator A translator object. + */ + protected function createInstance(string $name, string $locale): Translator + { + $package = $this->packages->get($name, $locale); + $fallback = $package->getFallback(); + if ($fallback !== null) { + $fallback = $this->get($fallback, $locale); } + $formatter = $this->formatters->get($package->getFormatter()); - return $this->_getFromLoader($name, $locale); + return new Translator($locale, $package, $formatter, $fallback); } /** @@ -227,54 +301,26 @@ public function useFallback(bool $enable = true): void } /** - * Returns a new translator instance for the given name and locale - * based of conventions. - * - * @param string $name The translation package name. - * @param string $locale The locale to create the translator for. - * @return \Aura\Intl\TranslatorInterface|\Closure - */ - protected function _fallbackLoader(string $name, string $locale) - { - return $this->_loaders[$this->_fallbackLoader]($name, $locale); - } - - /** - * Returns a function that can be used as a loader for the registerLoaderMethod + * Set fallback domain for package. * - * @return \Closure + * @param string $name The name of the package. + * @param \Cake\I18n\Package $package Package instance + * @return \Cake\I18n\Package */ - protected function _partialLoader(): Closure + public function setFallbackPackage(string $name, Package $package): Package { - return function ($name, $locale) { - return $this->_fallbackLoader($name, $locale); - }; - } - - /** - * Registers a new package by passing the register loaded function for the - * package name. - * - * @param string $name The name of the translator package - * @param string $locale The locale that should be built the package for - * @return \Aura\Intl\TranslatorInterface A translator object. - */ - protected function _getFromLoader(string $name, string $locale): TranslatorInterface - { - $loader = $this->_loaders[$name]($name, $locale); - $package = $loader; - - if (!is_callable($loader)) { - $loader = function () use ($package) { - return $package; - }; + if ($package->getFallback()) { + return $package; } - $loader = $this->setLoaderFallback($name, $loader); + $fallbackDomain = null; + if ($this->_useFallback && $name !== 'default') { + $fallbackDomain = 'default'; + } - $this->packages->set($name, $locale, $loader); + $package->setFallback($fallbackDomain); - return parent::get($name, $locale); + return $package; } /** @@ -291,7 +337,7 @@ public function setLoaderFallback(string $name, callable $loader): callable return $loader; } $loader = function () use ($loader, $fallbackDomain) { - /** @var \Aura\Intl\Package $package */ + /** @var \Cake\I18n\Package $package */ $package = $loader(); if (!$package->getFallback()) { $package->setFallback($fallbackDomain); diff --git a/app/vendor/cakephp/cakephp/src/I18n/composer.json b/app/vendor/cakephp/cakephp/src/I18n/composer.json index 94976f05e..8b75144a9 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/composer.json +++ b/app/vendor/cakephp/cakephp/src/I18n/composer.json @@ -31,8 +31,7 @@ "php": ">=7.2.0", "ext-intl": "*", "cakephp/core": "^4.0", - "cakephp/chronos": "^2.0.0", - "aura/intl": "^3.0.0" + "cakephp/chronos": "^2.0.0" }, "suggest": { "cakephp/cache": "Require this if you want automatic caching of translators" diff --git a/app/vendor/cakephp/cakephp/src/I18n/functions.php b/app/vendor/cakephp/cakephp/src/I18n/functions.php index 5775595c9..45f17250f 100644 --- a/app/vendor/cakephp/cakephp/src/I18n/functions.php +++ b/app/vendor/cakephp/cakephp/src/I18n/functions.php @@ -17,6 +17,13 @@ use Cake\I18n\I18n; +// Backwards compatibility alias for custom translation messages loaders which return a Package instance. +// phpcs:disable +if (!class_exists('Aura\Intl\Package')) { + class_alias('Cake\I18n\Package', 'Aura\Intl\Package'); +} +// phpcs:enable + if (!function_exists('__')) { /** * Returns a translated string if one is found; Otherwise, the submitted message. diff --git a/app/vendor/cakephp/cakephp/src/Log/Engine/BaseLog.php b/app/vendor/cakephp/cakephp/src/Log/Engine/BaseLog.php index 28f0a9146..03edaa0ed 100644 --- a/app/vendor/cakephp/cakephp/src/Log/Engine/BaseLog.php +++ b/app/vendor/cakephp/cakephp/src/Log/Engine/BaseLog.php @@ -16,8 +16,12 @@ */ namespace Cake\Log\Engine; +use ArrayObject; use Cake\Core\InstanceConfigTrait; +use DateTimeImmutable; +use JsonSerializable; use Psr\Log\AbstractLogger; +use Serializable; /** * Base log engine class. @@ -34,6 +38,7 @@ abstract class BaseLog extends AbstractLogger protected $_defaultConfig = [ 'levels' => [], 'scopes' => [], + 'dateFormat' => 'Y-m-d H:i:s', ]; /** @@ -90,6 +95,83 @@ public function scopes() */ protected function _format(string $message, array $context = []): string { - return $message; + if (strpos($message, '{') === false && strpos($message, '}') === false) { + return $message; + } + + preg_match_all( + '/(?getArrayCopy(), JSON_UNESCAPED_UNICODE); + continue; + } + + if ($value instanceof Serializable) { + $replacements['{' . $key . '}'] = $value->serialize(); + continue; + } + + if (is_object($value)) { + if (method_exists($value, '__toString')) { + $replacements['{' . $key . '}'] = (string)$value; + continue; + } + + if (method_exists($value, 'toArray')) { + $replacements['{' . $key . '}'] = json_encode($value->toArray(), JSON_UNESCAPED_UNICODE); + continue; + } + + if (method_exists($value, '__debugInfo')) { + $replacements['{' . $key . '}'] = json_encode($value->__debugInfo(), JSON_UNESCAPED_UNICODE); + continue; + } + } + + $replacements['{' . $key . '}'] = sprintf('[unhandled value of type %s]', getTypeName($value)); + } + + /** @psalm-suppress InvalidArgument */ + return str_replace(array_keys($replacements), $replacements, $message); + } + + /** + * Returns date formatted according to given `dateFormat` option format. + * + * This function affects `FileLog` or` ConsoleLog` datetime information format. + * + * @return string + */ + protected function _getFormattedDate(): string + { + return (new DateTimeImmutable())->format($this->_config['dateFormat']); } } diff --git a/app/vendor/cakephp/cakephp/src/Log/Engine/ConsoleLog.php b/app/vendor/cakephp/cakephp/src/Log/Engine/ConsoleLog.php index fbc7899b1..76e02ad88 100644 --- a/app/vendor/cakephp/cakephp/src/Log/Engine/ConsoleLog.php +++ b/app/vendor/cakephp/cakephp/src/Log/Engine/ConsoleLog.php @@ -34,6 +34,7 @@ class ConsoleLog extends BaseLog 'levels' => null, 'scopes' => [], 'outputAs' => null, + 'dateFormat' => 'Y-m-d H:i:s', ]; /** @@ -52,6 +53,7 @@ class ConsoleLog extends BaseLog * - `scopes` string or array, scopes the engine is interested in * - `stream` the path to save logs on. * - `outputAs` integer or ConsoleOutput::[RAW|PLAIN|COLOR] + * - `dateFormat` PHP date() format. * * @param array $config Options for the FileLog, see above. * @throws \InvalidArgumentException @@ -86,7 +88,7 @@ public function __construct(array $config = []) public function log($level, $message, array $context = []) { $message = $this->_format($message, $context); - $output = date('Y-m-d H:i:s') . ' ' . ucfirst($level) . ': ' . $message; + $output = $this->_getFormattedDate() . ' ' . ucfirst($level) . ': ' . $message; $this->_output->write(sprintf('<%s>%s', $level, $output, $level)); } diff --git a/app/vendor/cakephp/cakephp/src/Log/Engine/FileLog.php b/app/vendor/cakephp/cakephp/src/Log/Engine/FileLog.php index e52fe7629..b844a8c8e 100644 --- a/app/vendor/cakephp/cakephp/src/Log/Engine/FileLog.php +++ b/app/vendor/cakephp/cakephp/src/Log/Engine/FileLog.php @@ -40,6 +40,7 @@ class FileLog extends BaseLog * If value is 0, old versions are removed rather then rotated. * - `mask` A mask is applied when log files are created. Left empty no chmod * is made. + * - `dateFormat` PHP date() format. * * @var array */ @@ -52,6 +53,7 @@ class FileLog extends BaseLog 'rotate' => 10, 'size' => 10485760, // 10MB 'mask' => null, + 'dateFormat' => 'Y-m-d H:i:s', ]; /** @@ -84,7 +86,7 @@ public function __construct(array $config = []) { parent::__construct($config); - $this->_path = $this->getConfig('path', sys_get_temp_dir()); + $this->_path = $this->getConfig('path', sys_get_temp_dir() . DIRECTORY_SEPARATOR); if (Configure::read('debug') && !is_dir($this->_path)) { mkdir($this->_path, 0775, true); } @@ -117,7 +119,7 @@ public function __construct(array $config = []) public function log($level, $message, array $context = []): void { $message = $this->_format($message, $context); - $output = date('Y-m-d H:i:s') . ' ' . ucfirst($level) . ': ' . $message . "\n"; + $output = $this->_getFormattedDate() . ' ' . ucfirst($level) . ': ' . $message . "\n"; $filename = $this->_getFilename($level); if ($this->_size) { $this->_rotateFile($filename); @@ -131,7 +133,7 @@ public function log($level, $message, array $context = []): void return; } - $exists = file_exists($pathname); + $exists = is_file($pathname); file_put_contents($pathname, $output, FILE_APPEND); static $selfError = false; @@ -182,7 +184,7 @@ protected function _rotateFile(string $filename): ?bool clearstatcache(true, $filePath); if ( - !file_exists($filePath) || + !is_file($filePath) || filesize($filePath) < $this->_size ) { return null; diff --git a/app/vendor/cakephp/cakephp/src/Log/LICENSE.txt b/app/vendor/cakephp/cakephp/src/Log/LICENSE.txt index 0b3b94303..b938c9e8e 100644 --- a/app/vendor/cakephp/cakephp/src/Log/LICENSE.txt +++ b/app/vendor/cakephp/cakephp/src/Log/LICENSE.txt @@ -1,7 +1,7 @@ The MIT License (MIT) CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) -Copyright (c) 2005-2019, Cake Software Foundation, Inc. (https://cakefoundation.org) +Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/vendor/cakephp/cakephp/src/Log/LogEngineRegistry.php b/app/vendor/cakephp/cakephp/src/Log/LogEngineRegistry.php index e90b4b634..92fd1ee2e 100644 --- a/app/vendor/cakephp/cakephp/src/Log/LogEngineRegistry.php +++ b/app/vendor/cakephp/cakephp/src/Log/LogEngineRegistry.php @@ -64,11 +64,11 @@ protected function _throwMissingClassError(string $class, ?string $plugin): void * * @param string|\Psr\Log\LoggerInterface $class The classname or object to make. * @param string $alias The alias of the object. - * @param array $settings An array of settings to use for the logger. + * @param array $config An array of settings to use for the logger. * @return \Psr\Log\LoggerInterface The constructed logger class. * @throws \RuntimeException when an object doesn't implement the correct interface. */ - protected function _create($class, string $alias, array $settings): LoggerInterface + protected function _create($class, string $alias, array $config): LoggerInterface { if (is_callable($class)) { $class = $class($alias); @@ -80,7 +80,7 @@ protected function _create($class, string $alias, array $settings): LoggerInterf if (!isset($instance)) { /** @psalm-suppress UndefinedClass */ - $instance = new $class($settings); + $instance = new $class($config); } if ($instance instanceof LoggerInterface) { diff --git a/app/vendor/cakephp/cakephp/src/Log/README.md b/app/vendor/cakephp/cakephp/src/Log/README.md index 91016928f..5056f84e5 100644 --- a/app/vendor/cakephp/cakephp/src/Log/README.md +++ b/app/vendor/cakephp/cakephp/src/Log/README.md @@ -8,7 +8,7 @@ multiple logging backends using a simple interface. With the `Log` class it is possible to send a single message to multiple logging backends at the same time or just a subset of them based on the log level or context. -By default you can use Files or Syslog as logging backends, but you can use any +By default, you can use Files or Syslog as logging backends, but you can use any object implementing `Psr\Log\LoggerInterface` as an engine for the `Log` class. ## Usage diff --git a/app/vendor/cakephp/cakephp/src/Mailer/AbstractTransport.php b/app/vendor/cakephp/cakephp/src/Mailer/AbstractTransport.php index 772e0c05c..89786f4d2 100644 --- a/app/vendor/cakephp/cakephp/src/Mailer/AbstractTransport.php +++ b/app/vendor/cakephp/cakephp/src/Mailer/AbstractTransport.php @@ -16,7 +16,7 @@ */ namespace Cake\Mailer; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Core\InstanceConfigTrait; /** @@ -36,7 +36,7 @@ abstract class AbstractTransport /** * Send mail * - * @param \Cake\Mailer\Message $message Email mesage. + * @param \Cake\Mailer\Message $message Email message. * @return array * @psalm-return array{headers: string, message: string} */ @@ -57,7 +57,7 @@ public function __construct(array $config = []) * * @param \Cake\Mailer\Message $message Message instance. * @return void - * @throws \Cake\Core\Exception\Exception If at least one of to, cc or bcc is not specified. + * @throws \Cake\Core\Exception\CakeException If at least one of to, cc or bcc is not specified. */ protected function checkRecipient(Message $message): void { @@ -66,7 +66,7 @@ protected function checkRecipient(Message $message): void && $message->getCc() === [] && $message->getBcc() === [] ) { - throw new Exception( + throw new CakeException( 'You must specify at least one recipient.' . ' Use one of `setTo`, `setCc` or `setBcc` to define a recipient.' ); diff --git a/app/vendor/cakephp/cakephp/src/Mailer/Email.php b/app/vendor/cakephp/cakephp/src/Mailer/Email.php index a22cb19e1..0362070e0 100644 --- a/app/vendor/cakephp/cakephp/src/Mailer/Email.php +++ b/app/vendor/cakephp/cakephp/src/Mailer/Email.php @@ -38,6 +38,7 @@ * Once made configuration profiles can be used to re-use across various email messages your * application sends. * + * @mixin \Cake\Mailer\Mailer * @deprecated 4.0.0 This class will be removed in CakePHP 5.0, use {@link \Cake\Mailer\Mailer} instead. */ class Email implements JsonSerializable, Serializable @@ -607,7 +608,7 @@ public function unserialize($data): void } /** - * Proxy all static method calls (for methods provided by StaticConfigTrat) to Mailer. + * Proxy all static method calls (for methods provided by StaticConfigTrait) to Mailer. * * @param string $name Method name. * @param array $arguments Method argument. @@ -615,6 +616,6 @@ public function unserialize($data): void */ public static function __callStatic($name, $arguments) { - return call_user_func_array([Mailer::class, $name], $arguments); + return [Mailer::class, $name](...$arguments); } } diff --git a/app/vendor/cakephp/cakephp/src/Mailer/Exception/MissingActionException.php b/app/vendor/cakephp/cakephp/src/Mailer/Exception/MissingActionException.php index 9c6d3f88f..eafd5be0a 100644 --- a/app/vendor/cakephp/cakephp/src/Mailer/Exception/MissingActionException.php +++ b/app/vendor/cakephp/cakephp/src/Mailer/Exception/MissingActionException.php @@ -14,20 +14,15 @@ */ namespace Cake\Mailer\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Missing Action exception - used when a mailer action cannot be found. */ -class MissingActionException extends Exception +class MissingActionException extends CakeException { /** * @inheritDoc */ protected $_messageTemplate = 'Mail %s::%s() could not be found, or is not accessible.'; - - /** - * @inheritDoc - */ - protected $_defaultCode = 404; } diff --git a/app/vendor/cakephp/cakephp/src/Mailer/Exception/MissingMailerException.php b/app/vendor/cakephp/cakephp/src/Mailer/Exception/MissingMailerException.php index 197982c00..4b3efcee3 100644 --- a/app/vendor/cakephp/cakephp/src/Mailer/Exception/MissingMailerException.php +++ b/app/vendor/cakephp/cakephp/src/Mailer/Exception/MissingMailerException.php @@ -16,12 +16,12 @@ */ namespace Cake\Mailer\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Used when a mailer cannot be found. */ -class MissingMailerException extends Exception +class MissingMailerException extends CakeException { /** * @inheritDoc diff --git a/app/vendor/cakephp/cakephp/src/Mailer/Mailer.php b/app/vendor/cakephp/cakephp/src/Mailer/Mailer.php index df9e5dbf1..efa8d069e 100644 --- a/app/vendor/cakephp/cakephp/src/Mailer/Mailer.php +++ b/app/vendor/cakephp/cakephp/src/Mailer/Mailer.php @@ -15,7 +15,7 @@ namespace Cake\Mailer; use BadMethodCallException; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Core\StaticConfigTrait; use Cake\Datasource\ModelAwareTrait; use Cake\Event\EventListenerInterface; @@ -95,6 +95,7 @@ * @method array getSender() Gets "sender" address. {@see \Cake\Mailer\Message::getSender()} * @method $this setReplyTo($email, $name = null) Sets "Reply-To" address. {@see \Cake\Mailer\Message::setReplyTo()} * @method array getReplyTo() Gets "Reply-To" address. {@see \Cake\Mailer\Message::getReplyTo()} + * @method $this addReplyTo($email, $name = null) Add "Reply-To" address. {@see \Cake\Mailer\Message::addReplyTo()} * @method $this setReadReceipt($email, $name = null) Sets Read Receipt (Disposition-Notification-To header). * {@see \Cake\Mailer\Message::setReadReceipt()} * @method array getReadReceipt() Gets Read Receipt (Disposition-Notification-To header). @@ -300,6 +301,8 @@ public function __call(string $method, array $args) */ public function set($key, $value = null) { + deprecationWarning('Mailer::set() is deprecated. Use setViewVars() instead.'); + return $this->setViewVars($key, $value); } @@ -478,7 +481,7 @@ public function setTransport($name) } elseif (is_object($name)) { $transport = $name; if (!$transport instanceof AbstractTransport) { - throw new Exception('Transport class must extend Cake\Mailer\AbstractTransport'); + throw new CakeException('Transport class must extend Cake\Mailer\AbstractTransport'); } } else { throw new InvalidArgumentException(sprintf( diff --git a/app/vendor/cakephp/cakephp/src/Mailer/Message.php b/app/vendor/cakephp/cakephp/src/Mailer/Message.php index 29e5fa28e..09248ab48 100644 --- a/app/vendor/cakephp/cakephp/src/Mailer/Message.php +++ b/app/vendor/cakephp/cakephp/src/Mailer/Message.php @@ -17,7 +17,7 @@ namespace Cake\Mailer; use Cake\Core\Configure; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Http\Client\FormDataPart; use Cake\Utility\Hash; use Cake\Utility\Security; @@ -101,7 +101,7 @@ class Message implements JsonSerializable, Serializable protected $sender = []; /** - * The email the recipient will reply to + * List of email(s) that the recipient will reply to * * @var array */ @@ -325,7 +325,7 @@ public function __construct(?array $config = null) /** * Sets "from" address. * - * @param string|array $email Null to get, String with email, + * @param string|array $email String with email, * Array with email as key, name as value or email as value (without name) * @param string|null $name Name * @return $this @@ -347,13 +347,14 @@ public function getFrom(): array } /** - * Sets "sender" address. + * Sets the "sender" address. See RFC link below for full explanation. * * @param string|array $email String with email, * Array with email as key, name as value or email as value (without name) * @param string|null $name Name * @return $this * @throws \InvalidArgumentException + * @link https://tools.ietf.org/html/rfc2822.html#section-3.6.2 */ public function setSender($email, ?string $name = null) { @@ -361,9 +362,10 @@ public function setSender($email, ?string $name = null) } /** - * Gets "sender" address. + * Gets the "sender" address. See RFC link below for full explanation. * * @return array + * @link https://tools.ietf.org/html/rfc2822.html#section-3.6.2 */ public function getSender(): array { @@ -381,7 +383,7 @@ public function getSender(): array */ public function setReplyTo($email, ?string $name = null) { - return $this->setEmailSingle('replyTo', $email, $name, 'Reply-To requires only 1 email address.'); + return $this->setEmail('replyTo', $email, $name); } /** @@ -394,6 +396,19 @@ public function getReplyTo(): array return $this->replyTo; } + /** + * Add "Reply-To" address. + * + * @param string|array $email String with email, + * Array with email as key, name as value or email as value (without name) + * @param string|null $name Name + * @return $this + */ + public function addReplyTo($email, ?string $name = null) + { + return $this->addEmail('replyTo', $email, $name); + } + /** * Sets Read Receipt (Disposition-Notification-To header). * @@ -473,7 +488,7 @@ public function getTo(): array /** * Add "To" address. * - * @param string|array $email Null to get, String with email, + * @param string|array $email String with email, * Array with email as key, name as value or email as value (without name) * @param string|null $name Name * @return $this @@ -509,7 +524,7 @@ public function getCc(): array /** * Add "cc" address. * - * @param string|array $email Null to get, String with email, + * @param string|array $email String with email, * Array with email as key, name as value or email as value (without name) * @param string|null $name Name * @return $this @@ -545,7 +560,7 @@ public function getBcc(): array /** * Add "bcc" address. * - * @param string|array $email Null to get, String with email, + * @param string|array $email String with email, * Array with email as key, name as value or email as value (without name) * @param string|null $name Name * @return $this @@ -706,7 +721,7 @@ protected function validateEmail(string $email, string $context): void if (filter_var($email, FILTER_VALIDATE_EMAIL)) { return; } - } elseif (preg_match($this->emailPattern, (string)$email)) { + } elseif (preg_match($this->emailPattern, $email)) { return; } @@ -878,10 +893,18 @@ public function getHeaders(array $include = []): array 'replyTo' => 'Reply-To', 'readReceipt' => 'Disposition-Notification-To', 'returnPath' => 'Return-Path', + 'to' => 'To', + 'cc' => 'Cc', + 'bcc' => 'Bcc', ]; + $headersMultipleEmails = ['to', 'cc', 'bcc', 'replyTo']; foreach ($relation as $var => $header) { if ($include[$var]) { - $headers[$header] = (string)current($this->formatAddress($this->{$var})); + if (in_array($var, $headersMultipleEmails)) { + $headers[$header] = implode(', ', $this->formatAddress($this->{$var})); + } else { + $headers[$header] = (string)current($this->formatAddress($this->{$var})); + } } } if ($include['sender']) { @@ -892,12 +915,6 @@ public function getHeaders(array $include = []): array } } - foreach (['to', 'cc', 'bcc'] as $var) { - if ($include[$var]) { - $headers[ucfirst($var)] = implode(', ', $this->formatAddress($this->{$var})); - } - } - $headers += $this->headers; if (!isset($headers['Date'])) { $headers['Date'] = date(DATE_RFC2822); @@ -1836,6 +1853,7 @@ public function jsonSerialize(): array 'to', 'from', 'sender', 'replyTo', 'cc', 'bcc', 'subject', 'returnPath', 'readReceipt', 'emailFormat', 'emailPattern', 'domain', 'attachments', 'messageId', 'headers', 'appCharset', 'charset', 'headerCharset', + 'textMessage', 'htmlMessage', ]; $array = []; @@ -1888,7 +1906,7 @@ public function serialize(): string } /** - * Unserializes the Email object. + * Unserializes the Message object. * * @param string $data Serialized string. * @return void @@ -1897,7 +1915,7 @@ public function unserialize($data) { $array = unserialize($data); if (!is_array($array)) { - throw new Exception('Unable to unserialize message.'); + throw new CakeException('Unable to unserialize message.'); } $this->createFromArray($array); diff --git a/app/vendor/cakephp/cakephp/src/Mailer/Renderer.php b/app/vendor/cakephp/cakephp/src/Mailer/Renderer.php index 566418d1b..118b41f70 100644 --- a/app/vendor/cakephp/cakephp/src/Mailer/Renderer.php +++ b/app/vendor/cakephp/cakephp/src/Mailer/Renderer.php @@ -68,7 +68,7 @@ public function render(string $content, array $types = []): array $view = $this->createView(); [$templatePlugin] = pluginSplit($view->getTemplate()); - [$layoutPlugin] = pluginSplit((string)$view->getLayout()); + [$layoutPlugin] = pluginSplit($view->getLayout()); if ($templatePlugin) { $view->setPlugin($templatePlugin); } elseif ($layoutPlugin) { diff --git a/app/vendor/cakephp/cakephp/src/Mailer/Transport/DebugTransport.php b/app/vendor/cakephp/cakephp/src/Mailer/Transport/DebugTransport.php index b73ce77bd..52671bd77 100644 --- a/app/vendor/cakephp/cakephp/src/Mailer/Transport/DebugTransport.php +++ b/app/vendor/cakephp/cakephp/src/Mailer/Transport/DebugTransport.php @@ -35,7 +35,7 @@ public function send(Message $message): array $headers = $message->getHeadersString( ['from', 'sender', 'replyTo', 'readReceipt', 'returnPath', 'to', 'cc', 'subject'] ); - $message = implode("\r\n", (array)$message->getBody()); + $message = implode("\r\n", $message->getBody()); return ['headers' => $headers, 'message' => $message]; } diff --git a/app/vendor/cakephp/cakephp/src/Mailer/Transport/MailTransport.php b/app/vendor/cakephp/cakephp/src/Mailer/Transport/MailTransport.php index 391985828..3442bd921 100644 --- a/app/vendor/cakephp/cakephp/src/Mailer/Transport/MailTransport.php +++ b/app/vendor/cakephp/cakephp/src/Mailer/Transport/MailTransport.php @@ -18,7 +18,7 @@ */ namespace Cake\Mailer\Transport; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Mailer\AbstractTransport; use Cake\Mailer\Message; @@ -41,7 +41,7 @@ public function send(Message $message): array $to = $message->getHeaders(['to'])['To']; $to = str_replace("\r\n", '', $to); - $eol = $this->getConfig('eol', PHP_EOL); + $eol = $this->getConfig('eol', version_compare(PHP_VERSION, '8.0', '>=') ? "\r\n" : "\n"); $headers = $message->getHeadersString( [ 'from', @@ -91,7 +91,7 @@ protected function _mail( if (!@mail($to, $subject, $message, $headers, $params)) { $error = error_get_last(); $msg = 'Could not send email: ' . ($error['message'] ?? 'unknown'); - throw new Exception($msg); + throw new CakeException($msg); } // phpcs:enable } diff --git a/app/vendor/cakephp/cakephp/src/Network/Exception/SocketException.php b/app/vendor/cakephp/cakephp/src/Network/Exception/SocketException.php index 92f70cba5..dadfb4997 100644 --- a/app/vendor/cakephp/cakephp/src/Network/Exception/SocketException.php +++ b/app/vendor/cakephp/cakephp/src/Network/Exception/SocketException.php @@ -14,16 +14,12 @@ */ namespace Cake\Network\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Exception class for Socket. This exception will be thrown from Socket, Email, HttpSocket * SmtpTransport, MailTransport and HttpResponse when it encounters an error. */ -class SocketException extends Exception +class SocketException extends CakeException { - /** - * @inheritDoc - */ - protected $_defaultCode = 0; } diff --git a/app/vendor/cakephp/cakephp/src/Network/Socket.php b/app/vendor/cakephp/cakephp/src/Network/Socket.php index 0d12ebfdd..f1214671d 100644 --- a/app/vendor/cakephp/cakephp/src/Network/Socket.php +++ b/app/vendor/cakephp/cakephp/src/Network/Socket.php @@ -16,7 +16,7 @@ */ namespace Cake\Network; -use Cake\Core\Exception\Exception as CakeException; +use Cake\Core\Exception\CakeException; use Cake\Core\InstanceConfigTrait; use Cake\Network\Exception\SocketException; use Cake\Validation\Validation; @@ -167,7 +167,7 @@ public function connect(): bool $remoteSocketTarget, $errNum, $errStr, - $this->_config['timeout'], + (int)$this->_config['timeout'], $connectAs, $context ); @@ -186,7 +186,7 @@ public function connect(): bool $this->connected = is_resource($this->connection); if ($this->connected) { /** @psalm-suppress PossiblyNullArgument */ - stream_set_timeout($this->connection, $this->_config['timeout']); + stream_set_timeout($this->connection, (int)$this->_config['timeout']); } return $this->connected; diff --git a/app/vendor/cakephp/cakephp/src/ORM/Association.php b/app/vendor/cakephp/cakephp/src/ORM/Association.php index 41bcf4861..218cade86 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Association.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Association.php @@ -106,7 +106,7 @@ abstract class Association /** * The field name in the owning side table that is used to match with the foreignKey * - * @var string|string[] + * @var string|string[]|null */ protected $_bindingKey; @@ -1000,7 +1000,7 @@ protected function _formatAssociationResults(Query $query, Query $surrogate, arr $property = $options['propertyPath']; $propertyPath = explode('.', $property); - $query->formatResults(function ($results) use ($formatters, $property, $propertyPath, $query) { + $query->formatResults(function ($results, $query) use ($formatters, $property, $propertyPath) { $extracted = []; foreach ($results as $result) { foreach ($propertyPath as $propertyPathItem) { @@ -1014,7 +1014,7 @@ protected function _formatAssociationResults(Query $query, Query $surrogate, arr } $extracted = new Collection($extracted); foreach ($formatters as $callable) { - $extracted = new ResultSetDecorator($callable($extracted)); + $extracted = new ResultSetDecorator($callable($extracted, $query)); } /** @var \Cake\Collection\CollectionInterface $results */ diff --git a/app/vendor/cakephp/cakephp/src/ORM/Association/BelongsToMany.php b/app/vendor/cakephp/cakephp/src/ORM/Association/BelongsToMany.php index 9423783c4..1ba41d9eb 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Association/BelongsToMany.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Association/BelongsToMany.php @@ -106,7 +106,7 @@ class BelongsToMany extends Association /** * The name of the field representing the foreign key to the target table * - * @var string|string[] + * @var string|string[]|null */ protected $_targetForeignKey; @@ -253,6 +253,7 @@ public function defaultRowValue(array $row, bool $joined): array * * @param string|\Cake\ORM\Table|null $table Name or instance for the join table * @return \Cake\ORM\Table + * @throws \InvalidArgumentException If the expected associations are incompatible with existing associations. */ public function junction($table = null): Table { @@ -269,7 +270,7 @@ public function junction($table = null): Table $config = []; if (!$tableLocator->exists($tableAlias)) { - $config = ['table' => $tableName]; + $config = ['table' => $tableName, 'allowFallbackClass' => true]; // Propagate the connection if we'll get an auto-model if (!App::className($tableAlias, 'Model/Table', 'Table')) { @@ -282,8 +283,16 @@ public function junction($table = null): Table if (is_string($table)) { $table = $tableLocator->get($table); } + $source = $this->getSource(); $target = $this->getTarget(); + if ($source->getAlias() === $target->getAlias()) { + throw new InvalidArgumentException(sprintf( + 'The `%s` association on `%s` cannot target the same table.', + $this->getName(), + $source->getAlias() + )); + } $this->_generateSourceAssociations($table, $source); $this->_generateTargetAssociations($table, $source, $target); @@ -312,10 +321,17 @@ protected function _generateTargetAssociations(Table $junction, Table $source, T { $junctionAlias = $junction->getAlias(); $sAlias = $source->getAlias(); + $tAlias = $target->getAlias(); + + $targetBindingKey = null; + if ($junction->hasAssociation($tAlias)) { + $targetBindingKey = $junction->getAssociation($tAlias)->getBindingKey(); + } if (!$target->hasAssociation($junctionAlias)) { $target->hasMany($junctionAlias, [ 'targetTable' => $junction, + 'bindingKey' => $targetBindingKey, 'foreignKey' => $this->getTargetForeignKey(), 'strategy' => $this->_strategy, ]); @@ -350,9 +366,17 @@ protected function _generateTargetAssociations(Table $junction, Table $source, T protected function _generateSourceAssociations(Table $junction, Table $source): void { $junctionAlias = $junction->getAlias(); + $sAlias = $source->getAlias(); + + $sourceBindingKey = null; + if ($junction->hasAssociation($sAlias)) { + $sourceBindingKey = $junction->getAssociation($sAlias)->getBindingKey(); + } + if (!$source->hasAssociation($junctionAlias)) { $source->hasMany($junctionAlias, [ 'targetTable' => $junction, + 'bindingKey' => $sourceBindingKey, 'foreignKey' => $this->getForeignKey(), 'strategy' => $this->_strategy, ]); @@ -374,6 +398,7 @@ protected function _generateSourceAssociations(Table $junction, Table $source): * @param \Cake\ORM\Table $source The source table. * @param \Cake\ORM\Table $target The target table. * @return void + * @throws \InvalidArgumentException If the expected associations are incompatible with existing associations. */ protected function _generateJunctionAssociations(Table $junction, Table $source, Table $target): void { @@ -385,7 +410,19 @@ protected function _generateJunctionAssociations(Table $junction, Table $source, 'foreignKey' => $this->getTargetForeignKey(), 'targetTable' => $target, ]); + } else { + $belongsTo = $junction->getAssociation($tAlias); + if ( + $this->getTargetForeignKey() !== $belongsTo->getForeignKey() || + $target !== $belongsTo->getTarget() + ) { + throw new InvalidArgumentException( + "The existing `{$tAlias}` association on `{$junction->getAlias()}` " . + "is incompatible with the `{$this->getName()}` association on `{$source->getAlias()}`" + ); + } } + if (!$junction->hasAssociation($sAlias)) { $junction->belongsTo($sAlias, [ 'foreignKey' => $this->getForeignKey(), @@ -561,7 +598,10 @@ public function cascadeDelete(EntityInterface $entity, array $options = []): boo $hasMany = $this->getSource()->getAssociation($table->getAlias()); if ($this->_cascadeCallbacks) { foreach ($hasMany->find('all')->where($conditions)->all()->toList() as $related) { - $table->delete($related, $options); + $success = $table->delete($related, $options); + if (!$success) { + return false; + } } return true; @@ -751,7 +791,7 @@ protected function _saveLinks(EntityInterface $sourceEntity, array $targetEntiti $belongsTo = $junction->getAssociation($target->getAlias()); $foreignKey = (array)$this->getForeignKey(); $assocForeignKey = (array)$belongsTo->getForeignKey(); - $targetPrimaryKey = (array)$target->getPrimaryKey(); + $targetBindingKey = (array)$belongsTo->getBindingKey(); $bindingKey = (array)$this->getBindingKey(); $jointProperty = $this->_junctionProperty; $junctionRegistryAlias = $junction->getRegistryAlias(); @@ -762,7 +802,7 @@ protected function _saveLinks(EntityInterface $sourceEntity, array $targetEntiti $joint = new $entityClass([], ['markNew' => true, 'source' => $junctionRegistryAlias]); } $sourceKeys = array_combine($foreignKey, $sourceEntity->extract($bindingKey)); - $targetKeys = array_combine($assocForeignKey, $e->extract($targetPrimaryKey)); + $targetKeys = array_combine($assocForeignKey, $e->extract($targetBindingKey)); $changedKeys = ( $sourceKeys !== $joint->extract($foreignKey) || @@ -897,6 +937,7 @@ function () use ($sourceEntity, $targetEntities, $options): void { return true; } + /** @var \SplObjectStorage<\Cake\Datasource\EntityInterface, null> $storage*/ $storage = new SplObjectStorage(); foreach ($targetEntities as $e) { $storage->attach($e); @@ -1051,8 +1092,9 @@ public function find($type = null, array $options = []): Query */ protected function _appendJunctionJoin(Query $query, ?array $conditions = null): Query { + $junctionTable = $this->junction(); if ($conditions === null) { - $belongsTo = $this->junction()->getAssociation($this->getTarget()->getAlias()); + $belongsTo = $junctionTable->getAssociation($this->getTarget()->getAlias()); $conditions = $belongsTo->_joinCondition([ 'foreignKey' => $this->getTargetForeignKey(), ]); @@ -1064,15 +1106,14 @@ protected function _appendJunctionJoin(Query $query, ?array $conditions = null): $joins = $query->clause('join'); $matching = [ $name => [ - 'table' => $this->junction()->getTable(), + 'table' => $junctionTable->getTable(), 'conditions' => $conditions, 'type' => Query::JOIN_TYPE_INNER, ], ]; - $assoc = $this->getTarget()->getAssociation($name); $query - ->addDefaultTypes($assoc->getTarget()) + ->addDefaultTypes($junctionTable) ->join($matching + $joins, [], true); return $query; @@ -1155,13 +1196,24 @@ function () use ($sourceEntity, $targetEntities, $primaryValue, $options) { // Find existing rows so that we can diff with new entities. // Only hydrate primary/foreign key columns to save time. - $existing = $this->find() + // Attach joins first to ensure where conditions have correct + // column types set. + $existing = $this->_appendJunctionJoin($this->find()) ->select($keys) ->where(array_combine($prefixedForeignKey, $primaryValue)); - $existing = $this->_appendJunctionJoin($existing); + + // Because we're aliasing key fields to look like they are not + // from joined table we need to overwrite the type map as the junction + // table can have a surrogate primary key that doesn't share a type + // with the target table. + $junctionTypes = array_intersect_key($junction->getSchema()->typeMap(), $keys); + $existing->getSelectTypeMap()->setTypes($junctionTypes); $jointEntities = $this->_collectJointEntities($sourceEntity, $targetEntities); $inserts = $this->_diffLinks($existing, $jointEntities, $targetEntities, $options); + if ($inserts === false) { + return false; + } if ($inserts && !$this->_saveTarget($sourceEntity, $inserts, $options)) { return false; @@ -1196,14 +1248,14 @@ function () use ($sourceEntity, $targetEntities, $primaryValue, $options) { * @param array $targetEntities entities in target table that are related to * the `$jointEntities` * @param array $options list of options accepted by `Table::delete()` - * @return array + * @return array|false Array of entities not deleted or false in case of deletion failure for atomic saves. */ protected function _diffLinks( Query $existing, array $jointEntities, array $targetEntities, array $options = [] - ): array { + ) { $junction = $this->junction(); $target = $this->getTarget(); $belongsTo = $junction->getAssociation($target->getAlias()); @@ -1249,9 +1301,9 @@ protected function _diffLinks( } } - if ($deletes) { - foreach ($deletes as $entity) { - $junction->delete($entity, $options); + foreach ($deletes as $entity) { + if (!$junction->delete($entity, $options) && !empty($options['atomic'])) { + return false; } } @@ -1400,25 +1452,25 @@ protected function _junctionTableName(?string $name = null): string /** * Parse extra options passed in the constructor. * - * @param array $opts original list of options passed in constructor + * @param array $options original list of options passed in constructor * @return void */ - protected function _options(array $opts): void + protected function _options(array $options): void { - if (!empty($opts['targetForeignKey'])) { - $this->setTargetForeignKey($opts['targetForeignKey']); + if (!empty($options['targetForeignKey'])) { + $this->setTargetForeignKey($options['targetForeignKey']); } - if (!empty($opts['joinTable'])) { - $this->_junctionTableName($opts['joinTable']); + if (!empty($options['joinTable'])) { + $this->_junctionTableName($options['joinTable']); } - if (!empty($opts['through'])) { - $this->setThrough($opts['through']); + if (!empty($options['through'])) { + $this->setThrough($options['through']); } - if (!empty($opts['saveStrategy'])) { - $this->setSaveStrategy($opts['saveStrategy']); + if (!empty($options['saveStrategy'])) { + $this->setSaveStrategy($options['saveStrategy']); } - if (isset($opts['sort'])) { - $this->setSort($opts['sort']); + if (isset($options['sort'])) { + $this->setSort($options['sort']); } } } diff --git a/app/vendor/cakephp/cakephp/src/ORM/Association/DependentDeleteHelper.php b/app/vendor/cakephp/cakephp/src/ORM/Association/DependentDeleteHelper.php index 9b6c5161a..9311eb9e5 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Association/DependentDeleteHelper.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Association/DependentDeleteHelper.php @@ -45,16 +45,25 @@ public function cascadeDelete(Association $association, EntityInterface $entity, /** @psalm-suppress InvalidArgument */ $foreignKey = array_map([$association, 'aliasField'], (array)$association->getForeignKey()); $bindingKey = (array)$association->getBindingKey(); - $conditions = array_combine($foreignKey, $entity->extract($bindingKey)); + $bindingValue = $entity->extract($bindingKey); + if (in_array(null, $bindingValue, true)) { + return true; + } + $conditions = array_combine($foreignKey, $bindingValue); if ($association->getCascadeCallbacks()) { foreach ($association->find()->where($conditions)->all()->toList() as $related) { - $table->delete($related, $options); + $success = $table->delete($related, $options); + if (!$success) { + return false; + } } return true; } - return (bool)$association->deleteAll($conditions); + $association->deleteAll($conditions); + + return true; } } diff --git a/app/vendor/cakephp/cakephp/src/ORM/Association/HasMany.php b/app/vendor/cakephp/cakephp/src/ORM/Association/HasMany.php index 235017b7e..fa59e21f7 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Association/HasMany.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Association/HasMany.php @@ -637,16 +637,16 @@ public function defaultRowValue(array $row, bool $joined): array /** * Parse extra options passed in the constructor. * - * @param array $opts original list of options passed in constructor + * @param array $options original list of options passed in constructor * @return void */ - protected function _options(array $opts): void + protected function _options(array $options): void { - if (!empty($opts['saveStrategy'])) { - $this->setSaveStrategy($opts['saveStrategy']); + if (!empty($options['saveStrategy'])) { + $this->setSaveStrategy($options['saveStrategy']); } - if (isset($opts['sort'])) { - $this->setSort($opts['sort']); + if (isset($options['sort'])) { + $this->setSort($options['sort']); } } diff --git a/app/vendor/cakephp/cakephp/src/ORM/Association/Loader/SelectLoader.php b/app/vendor/cakephp/cakephp/src/ORM/Association/Loader/SelectLoader.php index 421baa087..b4c88b336 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Association/Loader/SelectLoader.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Association/Loader/SelectLoader.php @@ -267,7 +267,7 @@ protected function _assertFieldsPresent(Query $fetchQuery, array $key): void throw new InvalidArgumentException( sprintf( 'You are required to select the "%s" field(s)', - implode(', ', (array)$key) + implode(', ', $key) ) ); } @@ -404,7 +404,8 @@ protected function _buildSubquery(Query $query): Query $filterQuery->contain([], true); $filterQuery->setValueBinder(new ValueBinder()); - if (!$filterQuery->clause('limit')) { + // Ignore limit if there is no order since we need all rows to find matches + if (!$filterQuery->clause('limit') || !$filterQuery->clause('order')) { $filterQuery->limit(null); $filterQuery->order([], true); $filterQuery->offset(null); diff --git a/app/vendor/cakephp/cakephp/src/ORM/AssociationCollection.php b/app/vendor/cakephp/cakephp/src/ORM/AssociationCollection.php index 8bfe8a384..6a025dd35 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/AssociationCollection.php +++ b/app/vendor/cakephp/cakephp/src/ORM/AssociationCollection.php @@ -71,7 +71,7 @@ public function add(string $alias, Association $association): Association { [, $alias] = pluginSplit($alias); - return $this->_items[strtolower($alias)] = $association; + return $this->_items[$alias] = $association; } /** @@ -110,7 +110,6 @@ public function load(string $className, string $associated, array $options = []) */ public function get(string $alias): ?Association { - $alias = strtolower($alias); if (isset($this->_items[$alias])) { return $this->_items[$alias]; } @@ -143,7 +142,7 @@ public function getByProperty(string $prop): ?Association */ public function has(string $alias): bool { - return isset($this->_items[strtolower($alias)]); + return isset($this->_items[$alias]); } /** @@ -187,7 +186,7 @@ public function getByType($class): array */ public function remove(string $alias): void { - unset($this->_items[strtolower($alias)]); + unset($this->_items[$alias]); } /** @@ -312,7 +311,7 @@ protected function _save( return true; } if (!empty($nested)) { - $options = (array)$nested + $options; + $options = $nested + $options; } return (bool)$association->saveAssociated($entity, $options); @@ -324,24 +323,9 @@ protected function _save( * * @param \Cake\Datasource\EntityInterface $entity The entity to delete associations for. * @param array $options The options used in the delete operation. - * @return void - */ - public function cascadeDelete(EntityInterface $entity, array $options): void - { - $noCascade = $this->_getNoCascadeItems($entity, $options); - foreach ($noCascade as $assoc) { - $assoc->cascadeDelete($entity, $options); - } - } - - /** - * Returns items that have no cascade callback. - * - * @param \Cake\Datasource\EntityInterface $entity The entity to delete associations for. - * @param array $options The options used in the delete operation. - * @return \Cake\ORM\Association[] + * @return bool */ - protected function _getNoCascadeItems(EntityInterface $entity, array $options): array + public function cascadeDelete(EntityInterface $entity, array $options): bool { $noCascade = []; foreach ($this->_items as $assoc) { @@ -349,10 +333,20 @@ protected function _getNoCascadeItems(EntityInterface $entity, array $options): $noCascade[] = $assoc; continue; } - $assoc->cascadeDelete($entity, $options); + $success = $assoc->cascadeDelete($entity, $options); + if (!$success) { + return false; + } + } + + foreach ($noCascade as $assoc) { + $success = $assoc->cascadeDelete($entity, $options); + if (!$success) { + return false; + } } - return $noCascade; + return true; } /** diff --git a/app/vendor/cakephp/cakephp/src/ORM/AssociationsNormalizerTrait.php b/app/vendor/cakephp/cakephp/src/ORM/AssociationsNormalizerTrait.php index 6d6ba4504..5b27a7dbe 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/AssociationsNormalizerTrait.php +++ b/app/vendor/cakephp/cakephp/src/ORM/AssociationsNormalizerTrait.php @@ -47,6 +47,7 @@ protected function _normalizeAssociations($associations): array $path = explode('.', $table); $table = array_pop($path); + /** @var string $first */ $first = array_shift($path); $pointer += [$first => []]; $pointer = &$pointer[$first]; diff --git a/app/vendor/cakephp/cakephp/src/ORM/Behavior.php b/app/vendor/cakephp/cakephp/src/ORM/Behavior.php index d1562d0be..1f7939ab8 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Behavior.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Behavior.php @@ -16,7 +16,7 @@ */ namespace Cake\ORM; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Core\InstanceConfigTrait; use Cake\Event\EventListenerInterface; use ReflectionClass; @@ -184,8 +184,21 @@ public function initialize(array $config): void * Get the table instance this behavior is bound to. * * @return \Cake\ORM\Table The bound table instance. + * @deprecated 4.2.0 Use table() instead. */ public function getTable(): Table + { + deprecationWarning('Behavior::getTable() is deprecated. Use table() instead.'); + + return $this->table(); + } + + /** + * Get the table instance this behavior is bound to. + * + * @return \Cake\ORM\Table The bound table instance. + */ + public function table(): Table { return $this->_table; } @@ -229,7 +242,7 @@ protected function _resolveMethodAliases(string $key, array $defaults, array $co * Checks that implemented keys contain values pointing at callable. * * @return void - * @throws \Cake\Core\Exception\Exception if config are invalid + * @throws \Cake\Core\Exception\CakeException if config are invalid */ public function verifyConfig(): void { @@ -241,7 +254,7 @@ public function verifyConfig(): void foreach ($this->_config[$key] as $method) { if (!is_callable([$this, $method])) { - throw new Exception(sprintf( + throw new CakeException(sprintf( 'The method %s is not callable on class %s', $method, static::class @@ -266,6 +279,7 @@ public function implementedEvents(): array { $eventMap = [ 'Model.beforeMarshal' => 'beforeMarshal', + 'Model.afterMarshal' => 'afterMarshal', 'Model.beforeFind' => 'beforeFind', 'Model.beforeSave' => 'beforeSave', 'Model.afterSave' => 'afterSave', diff --git a/app/vendor/cakephp/cakephp/src/ORM/Behavior/TimestampBehavior.php b/app/vendor/cakephp/cakephp/src/ORM/Behavior/TimestampBehavior.php index dfeb29137..d2b6f7f82 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Behavior/TimestampBehavior.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Behavior/TimestampBehavior.php @@ -64,7 +64,7 @@ class TimestampBehavior extends Behavior /** * Current timestamp * - * @var \Cake\I18n\Time + * @var \Cake\I18n\Time|null */ protected $_ts; @@ -211,7 +211,7 @@ protected function _updateField(EntityInterface $entity, string $field, bool $re $ts = $this->timestamp(null, $refreshTimestamp); - $columnType = $this->getTable()->getSchema()->getColumnType($field); + $columnType = $this->table()->getSchema()->getColumnType($field); if (!$columnType) { return; } diff --git a/app/vendor/cakephp/cakephp/src/ORM/Behavior/Translate/EavStrategy.php b/app/vendor/cakephp/cakephp/src/ORM/Behavior/Translate/EavStrategy.php index 5c33edfb9..2a9f0e46e 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Behavior/Translate/EavStrategy.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Behavior/Translate/EavStrategy.php @@ -26,6 +26,7 @@ use Cake\ORM\Locator\LocatorAwareTrait; use Cake\ORM\Query; use Cake\ORM\Table; +use Cake\Utility\Hash; /** * This class provides a way to translate dynamic data by keeping translations @@ -79,7 +80,10 @@ public function __construct(Table $table, array $config = []) $this->setConfig($config); $this->table = $table; - $this->translationTable = $this->getTableLocator()->get($this->_config['translationTable']); + $this->translationTable = $this->getTableLocator()->get( + $this->_config['translationTable'], + ['allowFallbackClass' => true] + ); $this->setupAssociations(); } @@ -113,6 +117,7 @@ protected function setupAssociations() 'className' => $table, 'alias' => $name, 'table' => $this->translationTable->getTable(), + 'allowFallbackClass' => true, ]); } else { $fieldTable = $tableLocator->get($name); @@ -162,7 +167,7 @@ protected function setupAssociations() */ public function beforeFind(EventInterface $event, Query $query, ArrayObject $options) { - $locale = $this->getLocale(); + $locale = Hash::get($options, 'locale', $this->getLocale()); if ($locale === $this->getConfig('defaultLocale')) { return; @@ -281,7 +286,6 @@ public function beforeSave(EventInterface $event, EntityInterface $entity, Array $preexistent = []; if ($key) { - /** @psalm-suppress UndefinedClass */ $preexistent = $this->translationTable->find() ->select(['id', 'field']) ->where([ @@ -379,6 +383,7 @@ protected function rowMapper($results, $locale) $row['_locale'] = $locale; if ($hydrated) { + /** @psalm-suppress PossiblyInvalidMethodCall */ $row->clean(); } diff --git a/app/vendor/cakephp/cakephp/src/ORM/Behavior/Translate/ShadowTableStrategy.php b/app/vendor/cakephp/cakephp/src/ORM/Behavior/Translate/ShadowTableStrategy.php index 4365a7f73..f1391aa5d 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Behavior/Translate/ShadowTableStrategy.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Behavior/Translate/ShadowTableStrategy.php @@ -26,6 +26,7 @@ use Cake\ORM\Marshaller; use Cake\ORM\Query; use Cake\ORM\Table; +use Cake\Utility\Hash; /** * This class provides a way to translate dynamic data by keeping translations @@ -81,7 +82,10 @@ public function __construct(Table $table, array $config = []) $this->setConfig($config); $this->table = $table; - $this->translationTable = $this->getTableLocator()->get($this->_config['translationTable']); + $this->translationTable = $this->getTableLocator()->get( + $this->_config['translationTable'], + ['allowFallbackClass' => true] + ); $this->setupAssociations(); } @@ -98,7 +102,8 @@ protected function setupAssociations() { $config = $this->getConfig(); - $this->table->hasMany($config['translationTable'], [ + $targetAlias = $this->translationTable->getAlias(); + $this->table->hasMany($targetAlias, [ 'className' => $config['translationTable'], 'foreignKey' => 'id', 'strategy' => $config['strategy'], @@ -119,14 +124,54 @@ protected function setupAssociations() */ public function beforeFind(EventInterface $event, Query $query, ArrayObject $options) { - $locale = $this->getLocale(); + $locale = Hash::get($options, 'locale', $this->getLocale()); + $config = $this->getConfig(); + + if ($locale === $config['defaultLocale']) { + return; + } + + $this->setupHasOneAssociation($locale, $options); - if ($locale === $this->getConfig('defaultLocale')) { + $fieldsAdded = $this->addFieldsToQuery($query, $config); + $orderByTranslatedField = $this->iterateClause($query, 'order', $config); + $filteredByTranslatedField = $this->traverseClause($query, 'where', $config); + + if (!$fieldsAdded && !$orderByTranslatedField && !$filteredByTranslatedField) { return; } + $query->contain([$config['hasOneAlias']]); + + $query->formatResults(function ($results) use ($locale) { + return $this->rowMapper($results, $locale); + }, $query::PREPEND); + } + + /** + * Create a hasOne association for record with required locale. + * + * @param string $locale Locale + * @param \ArrayObject $options Find options + * @return void + */ + protected function setupHasOneAssociation(string $locale, ArrayObject $options): void + { $config = $this->getConfig(); + [$plugin] = pluginSplit($config['translationTable']); + $hasOneTargetAlias = $plugin ? ($plugin . '.' . $config['hasOneAlias']) : $config['hasOneAlias']; + if (!$this->getTableLocator()->exists($hasOneTargetAlias)) { + // Load table before hand with fallback class usage enabled + $this->getTableLocator()->get( + $hasOneTargetAlias, + [ + 'className' => $config['translationTable'], + 'allowFallbackClass' => true, + ] + ); + } + if (isset($options['filterByCurrentLocale'])) { $joinType = $options['filterByCurrentLocale'] ? 'INNER' : 'LEFT'; } else { @@ -142,20 +187,6 @@ public function beforeFind(EventInterface $event, Query $query, ArrayObject $opt $config['hasOneAlias'] . '.locale' => $locale, ], ]); - - $fieldsAdded = $this->addFieldsToQuery($query, $config); - $orderByTranslatedField = $this->iterateClause($query, 'order', $config); - $filteredByTranslatedField = $this->traverseClause($query, 'where', $config); - - if (!$fieldsAdded && !$orderByTranslatedField && !$filteredByTranslatedField) { - return; - } - - $query->contain([$config['hasOneAlias']]); - - $query->formatResults(function ($results) use ($locale) { - return $this->rowMapper($results, $locale); - }, $query::PREPEND); } /** @@ -232,6 +263,7 @@ function ($c, &$field) use ($fields, $alias, $mainTableAlias, $mainTableFields, return $c; } + /** @psalm-suppress ParadoxicalCondition */ if (in_array($field, $fields, true)) { $joinRequired = true; $field = "$alias.$field"; @@ -288,6 +320,7 @@ function ($expression) use ($fields, $alias, $mainTableAlias, $mainTableFields, return; } + /** @psalm-suppress ParadoxicalCondition */ if (in_array($field, $mainTableFields, true)) { $expression->setField("$mainTableAlias.$field"); } @@ -438,7 +471,7 @@ protected function rowMapper($results, $locale) { $allowEmpty = $this->_config['allowEmptyTranslations']; - return $results->map(function ($row) use ($allowEmpty) { + return $results->map(function ($row) use ($allowEmpty, $locale) { /** @var \Cake\Datasource\EntityInterface|array|null $row */ if ($row === null) { return $row; @@ -447,10 +480,11 @@ protected function rowMapper($results, $locale) $hydrated = !is_array($row); if (empty($row['translation'])) { - $row['_locale'] = $this->getLocale(); + $row['_locale'] = $locale; unset($row['translation']); if ($hydrated) { + /** @psalm-suppress PossiblyInvalidMethodCall */ $row->clean(); } @@ -482,6 +516,7 @@ protected function rowMapper($results, $locale) unset($row['translation']); if ($hydrated) { + /** @psalm-suppress PossiblyInvalidMethodCall */ $row->clean(); } diff --git a/app/vendor/cakephp/cakephp/src/ORM/Behavior/Translate/TranslateStrategyTrait.php b/app/vendor/cakephp/cakephp/src/ORM/Behavior/Translate/TranslateStrategyTrait.php index 42ce87a4f..ea1fc2902 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Behavior/Translate/TranslateStrategyTrait.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Behavior/Translate/TranslateStrategyTrait.php @@ -111,7 +111,7 @@ protected function unsetEmptyFields($entity) foreach ($translations as $locale => $translation) { $fields = $translation->extract($this->_config['fields'], false); foreach ($fields as $field => $value) { - if (strlen($value) === 0) { + if ($value === null || $value === '') { $translation->unset($field); } } @@ -152,27 +152,32 @@ public function buildMarshalMap(Marshaller $marshaller, array $map, array $optio return [ '_translations' => function ($value, $entity) use ($marshaller, $options) { - /** @var \Cake\Datasource\EntityInterface $entity */ + if (!is_array($value)) { + return null; + } + + /** @var array|null $translations */ $translations = $entity->get('_translations'); - foreach ($this->_config['fields'] as $field) { - $options['validate'] = $this->_config['validator']; - $errors = []; - if (!is_array($value)) { - return null; + if ($translations === null) { + $translations = []; + } + + $options['validate'] = $this->_config['validator']; + $errors = []; + foreach ($value as $language => $fields) { + if (!isset($translations[$language])) { + $translations[$language] = $this->table->newEmptyEntity(); } - foreach ($value as $language => $fields) { - if (!isset($translations[$language])) { - $translations[$language] = $this->table->newEmptyEntity(); - } - $marshaller->merge($translations[$language], $fields, $options); - /** @var \Cake\Datasource\EntityInterface $translation */ - $translation = $translations[$language]; - if ((bool)$translation->getErrors()) { - $errors[$language] = $translation->getErrors(); - } + $marshaller->merge($translations[$language], $fields, $options); + + $translationErrors = $translations[$language]->getErrors(); + if ($translationErrors) { + $errors[$language] = $translationErrors; } - // Set errors into the root entity, so validation errors - // match the original form data position. + } + + // Set errors into the root entity, so validation errors match the original form data position. + if ($errors) { $entity->setErrors($errors); } diff --git a/app/vendor/cakephp/cakephp/src/ORM/Behavior/TranslateBehavior.php b/app/vendor/cakephp/cakephp/src/ORM/Behavior/TranslateBehavior.php index d8c0d049f..5daecbb6b 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Behavior/TranslateBehavior.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Behavior/TranslateBehavior.php @@ -335,7 +335,7 @@ public function findTranslations(Query $query, array $options): Query */ public function __call($method, $args) { - return call_user_func_array([$this->strategy, $method], $args); + return $this->strategy->{$method}(...$args); } /** diff --git a/app/vendor/cakephp/cakephp/src/ORM/Behavior/TreeBehavior.php b/app/vendor/cakephp/cakephp/src/ORM/Behavior/TreeBehavior.php index 66971c4a4..af4f3282e 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Behavior/TreeBehavior.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Behavior/TreeBehavior.php @@ -135,7 +135,7 @@ public function beforeSave(EventInterface $event, EntityInterface $entity) return; } - if (!$isNew && $dirty && $parent) { + if ($dirty && $parent) { $this->_setParent($entity, $parent); if ($level) { @@ -146,7 +146,7 @@ public function beforeSave(EventInterface $event, EntityInterface $entity) return; } - if (!$isNew && $dirty && !$parent) { + if ($dirty && !$parent) { $this->_setAsRoot($entity); if ($level) { diff --git a/app/vendor/cakephp/cakephp/src/ORM/BehaviorRegistry.php b/app/vendor/cakephp/cakephp/src/ORM/BehaviorRegistry.php index 772145a0e..7f7b2c2c1 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/BehaviorRegistry.php +++ b/app/vendor/cakephp/cakephp/src/ORM/BehaviorRegistry.php @@ -249,7 +249,7 @@ public function call(string $method, array $args = []) if ($this->hasMethod($method) && $this->has($this->_methodMap[$method][0])) { [$behavior, $callMethod] = $this->_methodMap[$method]; - return call_user_func_array([$this->_loaded[$behavior], $callMethod], $args); + return $this->_loaded[$behavior]->{$callMethod}(...$args); } throw new BadMethodCallException( @@ -271,8 +271,9 @@ public function callFinder(string $type, array $args = []): Query if ($this->hasFinder($type) && $this->has($this->_finderMap[$type][0])) { [$behavior, $callMethod] = $this->_finderMap[$type]; + $callable = [$this->_loaded[$behavior], $callMethod]; - return call_user_func_array([$this->_loaded[$behavior], $callMethod], $args); + return $callable(...$args); } throw new BadMethodCallException( diff --git a/app/vendor/cakephp/cakephp/src/ORM/EagerLoadable.php b/app/vendor/cakephp/cakephp/src/ORM/EagerLoadable.php index 820f9fcbf..c6e98c881 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/EagerLoadable.php +++ b/app/vendor/cakephp/cakephp/src/ORM/EagerLoadable.php @@ -217,7 +217,7 @@ public function propertyPath(): ?string */ public function setCanBeJoined(bool $possible) { - $this->_canBeJoined = (bool)$possible; + $this->_canBeJoined = $possible; return $this; } @@ -311,4 +311,16 @@ public function asContainArray(): array ], ]; } + + /** + * Handles cloning eager loadables. + * + * @return void + */ + public function __clone() + { + foreach ($this->_associations as $i => $association) { + $this->_associations[$i] = clone $association; + } + } } diff --git a/app/vendor/cakephp/cakephp/src/ORM/EagerLoader.php b/app/vendor/cakephp/cakephp/src/ORM/EagerLoader.php index 252673123..671acfa3f 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/EagerLoader.php +++ b/app/vendor/cakephp/cakephp/src/ORM/EagerLoader.php @@ -308,7 +308,7 @@ public function normalized(Table $repository): array $contain = []; foreach ($this->_containments as $alias => $options) { if (!empty($options['instance'])) { - $contain = (array)$this->_containments; + $contain = $this->_containments; break; } $contain[$alias] = $this->_normalizeContain( @@ -336,7 +336,7 @@ protected function _reformatContain(array $associations, array $original): array { $result = $original; - foreach ((array)$associations as $table => $options) { + foreach ($associations as $table => $options) { $pointer = &$result; if (is_int($table)) { $table = $options; @@ -388,6 +388,7 @@ protected function _reformatContain(array $associations, array $original): array } if (!is_array($options)) { + /** @psalm-suppress InvalidArrayOffset */ $options = [$options => []]; } @@ -627,7 +628,6 @@ protected function _resolveJoins(array $associations, array $matching = []): arr */ public function loadExternal(Query $query, StatementInterface $statement): StatementInterface { - /** @var \Cake\ORM\Table $table */ $table = $query->getRepository(); $external = $this->externalAssociations($table); if (empty($external)) { @@ -862,9 +862,7 @@ protected function _groupKeys(BufferedStatement $statement, array $collectKeys): } /** - * Clone hook implementation - * - * Clone the _matching eager loader as well. + * Handles cloning eager loaders and eager loadables. * * @return void */ diff --git a/app/vendor/cakephp/cakephp/src/ORM/Exception/MissingBehaviorException.php b/app/vendor/cakephp/cakephp/src/ORM/Exception/MissingBehaviorException.php index eaaf511ad..fe6411eb0 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Exception/MissingBehaviorException.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Exception/MissingBehaviorException.php @@ -14,12 +14,12 @@ */ namespace Cake\ORM\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Used when a behavior cannot be found. */ -class MissingBehaviorException extends Exception +class MissingBehaviorException extends CakeException { /** * @var string diff --git a/app/vendor/cakephp/cakephp/src/ORM/Exception/MissingEntityException.php b/app/vendor/cakephp/cakephp/src/ORM/Exception/MissingEntityException.php index 50001358d..c363c123c 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Exception/MissingEntityException.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Exception/MissingEntityException.php @@ -18,12 +18,12 @@ */ namespace Cake\ORM\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Exception raised when an Entity could not be found. */ -class MissingEntityException extends Exception +class MissingEntityException extends CakeException { /** * @var string diff --git a/app/vendor/cakephp/cakephp/src/ORM/Exception/MissingTableClassException.php b/app/vendor/cakephp/cakephp/src/ORM/Exception/MissingTableClassException.php index 699a0f296..accf334b2 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Exception/MissingTableClassException.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Exception/MissingTableClassException.php @@ -16,12 +16,12 @@ */ namespace Cake\ORM\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Exception raised when a Table could not be found. */ -class MissingTableClassException extends Exception +class MissingTableClassException extends CakeException { /** * @var string diff --git a/app/vendor/cakephp/cakephp/src/ORM/Exception/PersistenceFailedException.php b/app/vendor/cakephp/cakephp/src/ORM/Exception/PersistenceFailedException.php index edf142318..5d9d03028 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Exception/PersistenceFailedException.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Exception/PersistenceFailedException.php @@ -14,7 +14,7 @@ */ namespace Cake\ORM\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Datasource\EntityInterface; use Cake\Utility\Hash; use Throwable; @@ -22,7 +22,7 @@ /** * Used when a strict save or delete fails */ -class PersistenceFailedException extends Exception +class PersistenceFailedException extends CakeException { /** * The entity on which the persistence operation failed diff --git a/app/vendor/cakephp/cakephp/src/ORM/Exception/RolledbackTransactionException.php b/app/vendor/cakephp/cakephp/src/ORM/Exception/RolledbackTransactionException.php index ebabcd79d..b1f107bd8 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Exception/RolledbackTransactionException.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Exception/RolledbackTransactionException.php @@ -14,12 +14,12 @@ */ namespace Cake\ORM\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Used when a transaction was rolled back from a callback event. */ -class RolledbackTransactionException extends Exception +class RolledbackTransactionException extends CakeException { /** * @var string diff --git a/app/vendor/cakephp/cakephp/src/ORM/LICENSE.txt b/app/vendor/cakephp/cakephp/src/ORM/LICENSE.txt index 0c4b7932c..b938c9e8e 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/LICENSE.txt +++ b/app/vendor/cakephp/cakephp/src/ORM/LICENSE.txt @@ -1,7 +1,7 @@ The MIT License (MIT) CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) -Copyright (c) 2005-2016, Cake Software Foundation, Inc. (https://cakefoundation.org) +Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/vendor/cakephp/cakephp/src/ORM/Locator/LocatorAwareTrait.php b/app/vendor/cakephp/cakephp/src/ORM/Locator/LocatorAwareTrait.php index 203d78b0a..3c17d4376 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Locator/LocatorAwareTrait.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Locator/LocatorAwareTrait.php @@ -16,7 +16,7 @@ */ namespace Cake\ORM\Locator; -use Cake\ORM\TableRegistry; +use Cake\Datasource\FactoryLocator; /** * Contains method for setting and accessing LocatorInterface instance @@ -51,9 +51,11 @@ public function setTableLocator(LocatorInterface $tableLocator) public function getTableLocator(): LocatorInterface { if ($this->_tableLocator === null) { - $this->_tableLocator = TableRegistry::getTableLocator(); + /** @psalm-suppress InvalidPropertyAssignmentValue */ + $this->_tableLocator = FactoryLocator::get('Table'); } + /** @var \Cake\ORM\Locator\LocatorInterface */ return $this->_tableLocator; } } diff --git a/app/vendor/cakephp/cakephp/src/ORM/Locator/LocatorInterface.php b/app/vendor/cakephp/cakephp/src/ORM/Locator/LocatorInterface.php index 7559e87f3..68db76d87 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Locator/LocatorInterface.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Locator/LocatorInterface.php @@ -16,12 +16,13 @@ */ namespace Cake\ORM\Locator; +use Cake\Datasource\RepositoryInterface; use Cake\ORM\Table; /** * Registries for Table objects should implement this interface. */ -interface LocatorInterface +interface LocatorInterface extends \Cake\Datasource\Locator\LocatorInterface { /** * Returns configuration for an alias or the full configuration array for @@ -55,34 +56,12 @@ public function setConfig($alias, $options = null); public function get(string $alias, array $options = []): Table; /** - * Check to see if an instance exists in the registry. - * - * @param string $alias The alias to check for. - * @return bool - */ - public function exists(string $alias): bool; - - /** - * Set an instance. + * Set a table instance. * * @param string $alias The alias to set. - * @param \Cake\ORM\Table $object The table to set. + * @param \Cake\ORM\Table $repository The table to set. * @return \Cake\ORM\Table + * @psalm-suppress MoreSpecificImplementedParamType */ - public function set(string $alias, Table $object): Table; - - /** - * Clears the registry of configuration and instances. - * - * @return void - */ - public function clear(): void; - - /** - * Removes an instance from the registry. - * - * @param string $alias The alias to remove. - * @return void - */ - public function remove(string $alias): void; + public function set(string $alias, RepositoryInterface $repository): Table; } diff --git a/app/vendor/cakephp/cakephp/src/ORM/Locator/TableLocator.php b/app/vendor/cakephp/cakephp/src/ORM/Locator/TableLocator.php index 70b0b4489..a89d14014 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Locator/TableLocator.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Locator/TableLocator.php @@ -18,7 +18,10 @@ use Cake\Core\App; use Cake\Datasource\ConnectionManager; +use Cake\Datasource\Locator\AbstractLocator; +use Cake\Datasource\RepositoryInterface; use Cake\ORM\AssociationCollection; +use Cake\ORM\Exception\MissingTableClassException; use Cake\ORM\Table; use Cake\Utility\Inflector; use RuntimeException; @@ -26,7 +29,7 @@ /** * Provides a default registry/factory for Table objects. */ -class TableLocator implements LocatorInterface +class TableLocator extends AbstractLocator implements LocatorInterface { /** * Contains a list of locations where table classes should be looked for. @@ -45,9 +48,9 @@ class TableLocator implements LocatorInterface /** * Instances that belong to the registry. * - * @var \Cake\ORM\Table[] + * @var array */ - protected $_instances = []; + protected $instances = []; /** * Contains a list of Table objects that were created out of the @@ -58,11 +61,19 @@ class TableLocator implements LocatorInterface protected $_fallbacked = []; /** - * Contains a list of options that were passed to get() method. + * Fallback class to use * - * @var array + * @var string + * @psalm-var class-string<\Cake\ORM\Table> */ - protected $_options = []; + protected $fallbackClassName = Table::class; + + /** + * Whether fallback class should be used if a table class could not be found. + * + * @var bool + */ + protected $allowFallbackClass = true; /** * Constructor. @@ -84,13 +95,41 @@ public function __construct(?array $locations = null) } /** - * Stores a list of options to be used when instantiating an object - * with a matching alias. + * Set if fallback class should be used. + * + * Controls whether a fallback class should be used to create a table + * instance if a concrete class for alias used in `get()` could not be found. + * + * @param bool $allow Flag to enable or disable fallback + * @return $this + */ + public function allowFallbackClass(bool $allow) + { + $this->allowFallbackClass = $allow; + + return $this; + } + + /** + * Set fallback class name. + * + * The class that should be used to create a table instance if a concrete + * class for alias used in `get()` could not be found. Defaults to + * `Cake\ORM\Table`. * - * @param string|array $alias Name of the alias or array to completely overwrite current config. - * @param array|null $options list of options for the alias + * @param string $className Fallback class name * @return $this - * @throws \RuntimeException When you attempt to configure an existing table instance. + * @psalm-param class-string<\Cake\ORM\Table> $className + */ + public function setFallbackClassName($className) + { + $this->fallbackClassName = $className; + + return $this; + } + + /** + * @inheritDoc */ public function setConfig($alias, $options = null) { @@ -100,7 +139,7 @@ public function setConfig($alias, $options = null) return $this; } - if (isset($this->_instances[$alias])) { + if (isset($this->instances[$alias])) { throw new RuntimeException(sprintf( 'You cannot configure "%s", it has already been constructed.', $alias @@ -113,10 +152,7 @@ public function setConfig($alias, $options = null) } /** - * Returns configuration for an alias or the full configuration array for all aliases. - * - * @param string|null $alias Alias to get config for, null for complete config. - * @return array The config data. + * @inheritDoc */ public function getConfig(?string $alias = null): array { @@ -165,29 +201,33 @@ public function getConfig(?string $alias = null): array */ public function get(string $alias, array $options = []): Table { - if (isset($this->_instances[$alias])) { - if (!empty($options) && $this->_options[$alias] !== $options) { - throw new RuntimeException(sprintf( - 'You cannot configure "%s", it already exists in the registry.', - $alias - )); - } + /** @var \Cake\ORM\Table */ + return parent::get($alias, $options); + } - return $this->_instances[$alias]; + /** + * @inheritDoc + */ + protected function createInstance(string $alias, array $options) + { + if (strpos($alias, '\\') === false) { + [, $classAlias] = pluginSplit($alias); + $options = ['alias' => $classAlias] + $options; + } elseif (!isset($options['alias'])) { + $options['className'] = $alias; + /** @psalm-suppress PossiblyFalseOperand */ + $alias = substr($alias, strrpos($alias, '\\') + 1, -5); } - $this->_options[$alias] = $options; - [, $classAlias] = pluginSplit($alias); - $options = ['alias' => $classAlias] + $options; - if (isset($this->_config[$alias])) { $options += $this->_config[$alias]; } + $allowFallbackClass = $options['allowFallbackClass'] ?? $this->allowFallbackClass; $className = $this->_getClassName($alias, $options); if ($className) { $options['className'] = $className; - } else { + } elseif ($allowFallbackClass) { if (empty($options['className'])) { $options['className'] = $alias; } @@ -195,7 +235,14 @@ public function get(string $alias, array $options = []): Table [, $table] = pluginSplit($options['className']); $options['table'] = Inflector::underscore($table); } - $options['className'] = Table::class; + $options['className'] = $this->fallbackClassName; + } else { + $message = $options['className'] ?? $alias; + $message = '`' . $message . '`'; + if (strpos($message, '\\') === false) { + $message = 'for alias ' . $message; + } + throw new MissingTableClassException([$message]); } if (empty($options['connection'])) { @@ -214,13 +261,13 @@ public function get(string $alias, array $options = []): Table } $options['registryAlias'] = $alias; - $this->_instances[$alias] = $this->_create($options); + $instance = $this->_create($options); - if ($options['className'] === Table::class) { - $this->_fallbacked[$alias] = $this->_instances[$alias]; + if ($options['className'] === $this->fallbackClassName) { + $this->_fallbacked[$alias] = $instance; } - return $this->_instances[$alias]; + return $instance; } /** @@ -258,25 +305,21 @@ protected function _getClassName(string $alias, array $options = []): ?string */ protected function _create(array $options): Table { - // phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.InvalidFormat /** @var \Cake\ORM\Table */ return new $options['className']($options); } /** - * @inheritDoc - */ - public function exists(string $alias): bool - { - return isset($this->_instances[$alias]); - } - - /** - * @inheritDoc + * Set a Table instance. + * + * @param string $alias The alias to set. + * @param \Cake\ORM\Table $repository The Table to set. + * @return \Cake\ORM\Table + * @psalm-suppress MoreSpecificImplementedParamType */ - public function set(string $alias, Table $object): Table + public function set(string $alias, RepositoryInterface $repository): Table { - return $this->_instances[$alias] = $object; + return $this->instances[$alias] = $repository; } /** @@ -284,10 +327,10 @@ public function set(string $alias, Table $object): Table */ public function clear(): void { - $this->_instances = []; - $this->_config = []; + parent::clear(); + $this->_fallbacked = []; - $this->_options = []; + $this->_config = []; } /** @@ -308,11 +351,9 @@ public function genericInstances(): array */ public function remove(string $alias): void { - unset( - $this->_instances[$alias], - $this->_config[$alias], - $this->_fallbacked[$alias] - ); + parent::remove($alias); + + unset($this->_fallbacked[$alias]); } /** diff --git a/app/vendor/cakephp/cakephp/src/ORM/Marshaller.php b/app/vendor/cakephp/cakephp/src/ORM/Marshaller.php index 5586b4b17..ad3e1429e 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Marshaller.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Marshaller.php @@ -72,6 +72,7 @@ protected function _buildPropertyMap(array $data, array $options): array // Is a concrete column? foreach (array_keys($data) as $prop) { + $prop = (string)$prop; $columnType = $schema->getColumnType($prop); if ($columnType) { $map[$prop] = function ($value, $entity) use ($columnType) { @@ -143,7 +144,7 @@ protected function _buildPropertyMap(array $data, array $options): array * - validate: Set to false to disable validation. Can also be a string of the validator ruleset to be applied. * Defaults to true/default. * - associated: Associations listed here will be marshalled as well. Defaults to null. - * - fields: A whitelist of fields to be assigned to the entity. If not present, + * - fields: An allowed list of fields to be assigned to the entity. If not present, * the accessible fields list in the entity will be used. Defaults to null. * - accessibleFields: A list of fields to allow or deny in entity accessible fields. Defaults to null * - forceNew: When enabled, belongsToMany associations will have 'new' entities created @@ -180,7 +181,6 @@ public function one(array $data, array $options = []): EntityInterface $primaryKey = (array)$this->_table->getPrimaryKey(); $entityClass = $this->_table->getEntityClass(); - /** @var \Cake\Datasource\EntityInterface $entity */ $entity = new $entityClass(); $entity->setSource($this->_table->getRegistryAlias()); @@ -232,6 +232,7 @@ public function one(array $data, array $options = []): EntityInterface } $entity->setErrors($errors); + $this->dispatchAfterMarshal($entity, $data, $options); return $entity; } @@ -312,7 +313,7 @@ protected function _marshalAssociation(Association $assoc, $value, array $option $types = [Association::ONE_TO_ONE, Association::MANY_TO_ONE]; $type = $assoc->type(); if (in_array($type, $types, true)) { - return $marshaller->one($value, (array)$options); + return $marshaller->one($value, $options); } if ($type === Association::ONE_TO_MANY || $type === Association::MANY_TO_MANY) { $hasIds = array_key_exists('_ids', $value); @@ -326,10 +327,11 @@ protected function _marshalAssociation(Association $assoc, $value, array $option } } if ($type === Association::MANY_TO_MANY) { - return $marshaller->_belongsToMany($assoc, $value, (array)$options); + /** @psalm-suppress ArgumentTypeCoercion */ + return $marshaller->_belongsToMany($assoc, $value, $options); } - return $marshaller->many($value, (array)$options); + return $marshaller->many($value, $options); } /** @@ -340,7 +342,7 @@ protected function _marshalAssociation(Association $assoc, $value, array $option * - validate: Set to false to disable validation. Can also be a string of the validator ruleset to be applied. * Defaults to true/default. * - associated: Associations listed here will be marshalled as well. Defaults to null. - * - fields: A whitelist of fields to be assigned to the entity. If not present, + * - fields: An allowed list of fields to be assigned to the entity. If not present, * the accessible fields list in the entity will be used. Defaults to null. * - accessibleFields: A list of fields to allow or deny in entity accessible fields. Defaults to null * - forceNew: When enabled, belongsToMany associations will have 'new' entities created @@ -487,7 +489,12 @@ protected function _loadAssociatedByIds(Association $assoc, array $ids): array if (!is_array($first) || count($first) !== count($primaryKey)) { return []; } - $filter = new TupleComparison($primaryKey, $ids, [], 'IN'); + $type = []; + $schema = $target->getSchema(); + foreach ((array)$target->getPrimaryKey() as $column) { + $type[] = $schema->getColumnType($column); + } + $filter = new TupleComparison($primaryKey, $ids, $type, 'IN'); } else { $filter = [$primaryKey[0] . ' IN' => $ids]; } @@ -511,7 +518,7 @@ protected function _loadAssociatedByIds(Association $assoc, array $ids): array * - associated: Associations listed here will be marshalled as well. * - validate: Whether or not to validate data before hydrating the entities. Can * also be set to a string to use a specific validator. Defaults to true/default. - * - fields: A whitelist of fields to be assigned to the entity. If not present + * - fields: An allowed list of fields to be assigned to the entity. If not present * the accessible fields list in the entity will be used. * - accessibleFields: A list of fields to allow or deny in entity accessible fields. * @@ -581,7 +588,7 @@ public function merge(EntityInterface $entity, array $data, array $options = []) || ( is_object($value) && !($value instanceof EntityInterface) - && $original === $value + && $original == $value ) ) { continue; @@ -599,6 +606,7 @@ public function merge(EntityInterface $entity, array $data, array $options = []) $entity->setDirty($field, $value->isDirty()); } } + $this->dispatchAfterMarshal($entity, $data, $options); return $entity; } @@ -612,6 +620,7 @@ public function merge(EntityInterface $entity, array $data, array $options = []) $entity->setDirty($field, $properties[$field]->isDirty()); } } + $this->dispatchAfterMarshal($entity, $data, $options); return $entity; } @@ -636,7 +645,7 @@ public function merge(EntityInterface $entity, array $data, array $options = []) * - validate: Whether or not to validate data before hydrating the entities. Can * also be set to a string to use a specific validator. Defaults to true/default. * - associated: Associations listed here will be marshalled as well. - * - fields: A whitelist of fields to be assigned to the entity. If not present, + * - fields: An allowed list of fields to be assigned to the entity. If not present, * the accessible fields list in the entity will be used. * - accessibleFields: A list of fields to allow or deny in entity accessible fields. * @@ -666,9 +675,8 @@ public function mergeMany(iterable $entities, array $data, array $options = []): }) ->toArray(); - $new = $indexed[null] ?? []; - /** @psalm-suppress PossiblyNullArrayOffset */ - unset($indexed[null]); + $new = $indexed[''] ?? []; + unset($indexed['']); $output = []; foreach ($entities as $entity) { @@ -743,12 +751,12 @@ protected function _mergeAssociation($original, Association $assoc, $value, arra $types = [Association::ONE_TO_ONE, Association::MANY_TO_ONE]; $type = $assoc->type(); if (in_array($type, $types, true)) { - /** @psalm-suppress PossiblyInvalidArgument */ - return $marshaller->merge($original, $value, (array)$options); + /** @psalm-suppress PossiblyInvalidArgument, ArgumentTypeCoercion */ + return $marshaller->merge($original, $value, $options); } if ($type === Association::MANY_TO_MANY) { - /** @psalm-suppress PossiblyInvalidArgument */ - return $marshaller->_mergeBelongsToMany($original, $assoc, $value, (array)$options); + /** @psalm-suppress PossiblyInvalidArgument, ArgumentTypeCoercion */ + return $marshaller->_mergeBelongsToMany($original, $assoc, $value, $options); } if ($type === Association::ONE_TO_MANY) { @@ -763,7 +771,7 @@ protected function _mergeAssociation($original, Association $assoc, $value, arra } /** @psalm-suppress PossiblyInvalidArgument */ - return $marshaller->mergeMany($original, $value, (array)$options); + return $marshaller->mergeMany($original, $value, $options); } /** @@ -857,4 +865,19 @@ protected function _mergeJoinData(array $original, BelongsToMany $assoc, array $ return $records; } + + /** + * dispatch Model.afterMarshal event. + * + * @param \Cake\Datasource\EntityInterface $entity The entity that was marshaled. + * @param array $data readOnly $data to use. + * @param array $options List of options that are readOnly. + * @return void + */ + protected function dispatchAfterMarshal(EntityInterface $entity, array $data, array $options = []): void + { + $data = new ArrayObject($data); + $options = new ArrayObject($options); + $this->_table->dispatchEvent('Model.afterMarshal', compact('entity', 'data', 'options')); + } } diff --git a/app/vendor/cakephp/cakephp/src/ORM/Query.php b/app/vendor/cakephp/cakephp/src/ORM/Query.php index de6ac1033..d7dba8ece 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Query.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Query.php @@ -43,7 +43,7 @@ * @method \Cake\ORM\Table getRepository() Returns the default table object that will be used by this query, * that is, the table that will appear in the from clause. * @method \Cake\Collection\CollectionInterface each(callable $c) Passes each of the query results to the callable - * @method \Cake\Collection\CollectionInterface sortBy($callback, int $dir, int $type) Sorts the query with the callback + * @method \Cake\Collection\CollectionInterface sortBy($callback, int $dir) Sorts the query with the callback * @method \Cake\Collection\CollectionInterface filter(callable $c = null) Keeps the results using passing the callable test * @method \Cake\Collection\CollectionInterface reject(callable $c) Removes the results passing the callable test * @method bool every(callable $c) Returns true if all the results pass the callable test @@ -51,8 +51,8 @@ * @method \Cake\Collection\CollectionInterface map(callable $c) Modifies each of the results using the callable * @method mixed reduce(callable $c, $zero = null) Folds all the results into a single value using the callable. * @method \Cake\Collection\CollectionInterface extract($field) Extracts a single column from each row - * @method mixed max($field, int $type) Returns the maximum value for a single column in all the results. - * @method mixed min($field, int $type) Returns the minimum value for a single column in all the results. + * @method mixed max($field) Returns the maximum value for a single column in all the results. + * @method mixed min($field) Returns the minimum value for a single column in all the results. * @method \Cake\Collection\CollectionInterface groupBy(string|callable $field) In-memory group all results by the value of a column. * @method \Cake\Collection\CollectionInterface indexBy(string|callable $callback) Returns the results indexed by the value of a column. * @method \Cake\Collection\CollectionInterface countBy(string|callable $field) Returns the number of unique values for a column @@ -130,6 +130,13 @@ class Query extends DatabaseQuery implements JsonSerializable, QueryInterface */ protected $_hydrate = true; + /** + * Whether aliases are generated for fields. + * + * @var bool + */ + protected $aliasingEnabled = true; + /** * A callable function that can be used to calculate the total amount of * records this query will match when not using `limit` @@ -225,7 +232,11 @@ public function select($fields = [], bool $overwrite = false) } if ($fields instanceof Table) { - $fields = $this->aliasFields($fields->getSchema()->columns(), $fields->getAlias()); + if ($this->aliasingEnabled) { + $fields = $this->aliasFields($fields->getSchema()->columns(), $fields->getAlias()); + } else { + $fields = $fields->getSchema()->columns(); + } } return parent::select($fields, $overwrite); @@ -255,9 +266,11 @@ public function selectAllExcept($table, array $excludedFields, bool $overwrite = } $fields = array_diff($table->getSchema()->columns(), $excludedFields); - $aliasedFields = $this->aliasFields($fields); + if ($this->aliasingEnabled) { + $fields = $this->aliasFields($fields); + } - return $this->select($aliasedFields, $overwrite); + return $this->select($fields, $overwrite); } /** @@ -513,7 +526,7 @@ protected function _addAssociationsToTypeMap(Table $table, TypeMap $typeMap, arr * // Bring only articles that were tagged with 'cake' * $query->matching('Tags', function ($q) { * return $q->where(['name' => 'cake']); - * ); + * }); * ``` * * It is possible to filter by deep associations by using dot notation: @@ -524,7 +537,7 @@ protected function _addAssociationsToTypeMap(Table $table, TypeMap $typeMap, arr * // Bring only articles that were commented by 'markstory' * $query->matching('Comments.Users', function ($q) { * return $q->where(['username' => 'markstory']); - * ); + * }); * ``` * * As this function will create `INNER JOIN`, you might want to consider @@ -537,9 +550,9 @@ protected function _addAssociationsToTypeMap(Table $table, TypeMap $typeMap, arr * ``` * // Bring unique articles that were commented by 'markstory' * $query->distinct(['Articles.id']) - * ->matching('Comments.Users', function ($q) { - * return $q->where(['username' => 'markstory']); - * ); + * ->matching('Comments.Users', function ($q) { + * return $q->where(['username' => 'markstory']); + * }); * ``` * * Please note that the query passed to the closure will only accept calling @@ -607,11 +620,11 @@ public function matching(string $assoc, ?callable $builder = null) * ``` * // Total comments in articles by 'markstory' * $query - * ->select(['total_comments' => $query->func()->count('Comments.id')]) - * ->leftJoinWith('Comments.Users', function ($q) { - * return $q->where(['username' => 'markstory']); - * ) - * ->group(['Users.id']); + * ->select(['total_comments' => $query->func()->count('Comments.id')]) + * ->leftJoinWith('Comments.Users', function ($q) { + * return $q->where(['username' => 'markstory']); + * }) + * ->group(['Users.id']); * ``` * * Please note that the query passed to the closure will only accept calling @@ -650,7 +663,7 @@ public function leftJoinWith(string $assoc, ?callable $builder = null) * // Bring only articles that were tagged with 'cake' * $query->innerJoinWith('Tags', function ($q) { * return $q->where(['name' => 'cake']); - * ); + * }); * ``` * * This will create the following SQL: @@ -698,7 +711,7 @@ public function innerJoinWith(string $assoc, ?callable $builder = null) * // Bring only articles that were not tagged with 'cake' * $query->notMatching('Tags', function ($q) { * return $q->where(['name' => 'cake']); - * ); + * }); * ``` * * It is possible to filter by deep associations by using dot notation: @@ -709,7 +722,7 @@ public function innerJoinWith(string $assoc, ?callable $builder = null) * // Bring only articles that weren't commented by 'markstory' * $query->notMatching('Comments.Users', function ($q) { * return $q->where(['username' => 'markstory']); - * ); + * }); * ``` * * As this function will create a `LEFT JOIN`, you might want to consider @@ -722,9 +735,9 @@ public function innerJoinWith(string $assoc, ?callable $builder = null) * ``` * // Bring unique articles that were commented by 'markstory' * $query->distinct(['Articles.id']) - * ->notMatching('Comments.Users', function ($q) { - * return $q->where(['username' => 'markstory']); - * ); + * ->notMatching('Comments.Users', function ($q) { + * return $q->where(['username' => 'markstory']); + * }); * ``` * * Please note that the query passed to the closure will only accept calling @@ -753,7 +766,9 @@ public function notMatching(string $assoc, ?callable $builder = null) /** * Populates or adds parts to current query clauses using an array. - * This is handy for passing all query clauses at once. The option array accepts: + * This is handy for passing all query clauses at once. + * + * The method accepts the following query clause related options: * * - fields: Maps to the select method * - conditions: Maps to the where method @@ -766,6 +781,10 @@ public function notMatching(string $assoc, ?callable $builder = null) * - join: Maps to the join method * - page: Maps to the page method * + * All other options will not affect the query, but will be stored + * as custom options that can be read via `getOptions()`. Furthermore + * they are automatically passed to `Model.beforeFind`. + * * ### Example: * * ``` @@ -774,7 +793,7 @@ public function notMatching(string $assoc, ?callable $builder = null) * 'conditions' => [ * 'created >=' => '2013-01-01' * ], - * 'limit' => 10 + * 'limit' => 10, * ]); * ``` * @@ -787,8 +806,26 @@ public function notMatching(string $assoc, ?callable $builder = null) * ->limit(10) * ``` * - * @param array $options the options to be applied + * Custom options can be read via `getOptions()`: + * + * ``` + * $query->applyOptions([ + * 'fields' => ['id', 'name'], + * 'custom' => 'value', + * ]); + * ``` + * + * Here `$options` will hold `['custom' => 'value']` (the `fields` + * option will be applied to the query instead of being stored, as + * it's a query clause related option): + * + * ``` + * $options = $query->getOptions(); + * ``` + * + * @param array $options The options to be applied * @return $this + * @see getOptions() */ public function applyOptions(array $options) { @@ -837,7 +874,6 @@ public function applyOptions(array $options) public function cleanCopy() { $clone = clone $this; - $clone->setEagerLoader(clone $this->getEagerLoader()); $clone->triggerBeforeFind(); $clone->disableAutoFields(); $clone->limit(null); @@ -852,11 +888,22 @@ public function cleanCopy() } /** - * Object clone hook. + * Clears the internal result cache and the internal count value from the current + * query object. * - * Destroys the clones inner iterator and clones the value binder, and eagerloader instances. + * @return $this + */ + public function clearResult() + { + $this->_dirty(); + + return $this; + } + + /** + * {@inheritDoc} * - * @return void + * Handles cloning eager loaders. */ public function __clone() { @@ -1061,7 +1108,6 @@ public function triggerBeforeFind(): void if (!$this->_beforeFindFired && $this->_type === 'select') { $this->_beforeFindFired = true; - /** @var \Cake\Event\EventDispatcherInterface $repository */ $repository = $this->getRepository(); $repository->dispatchEvent('Model.beforeFind', [ $this, @@ -1074,13 +1120,13 @@ public function triggerBeforeFind(): void /** * @inheritDoc */ - public function sql(?ValueBinder $generator = null): string + public function sql(?ValueBinder $binder = null): string { $this->triggerBeforeFind(); $this->_transformQuery(); - return parent::sql($generator); + return parent::sql($binder); } /** @@ -1122,7 +1168,6 @@ protected function _transformQuery(): void return; } - /** @var \Cake\ORM\Table $repository */ $repository = $this->getRepository(); if (empty($this->_parts['from'])) { @@ -1144,7 +1189,6 @@ protected function _addDefaultFields(): void $select = $this->clause('select'); $this->_hasFields = true; - /** @var \Cake\ORM\Table $repository */ $repository = $this->getRepository(); if (!count($select) || $this->_autoFields === true) { @@ -1153,8 +1197,10 @@ protected function _addDefaultFields(): void $select = $this->clause('select'); } - $aliased = $this->aliasFields($select, $repository->getAlias()); - $this->select($aliased, true); + if ($this->aliasingEnabled) { + $select = $this->aliasFields($select, $repository->getAlias()); + } + $this->select($select, true); } /** @@ -1193,7 +1239,6 @@ protected function _addDefaultSelectTypes(): void */ public function find(string $finder, array $options = []) { - /** @var \Cake\ORM\Table $table */ $table = $this->getRepository(); /** @psalm-suppress LessSpecificReturnStatement */ @@ -1225,7 +1270,6 @@ protected function _dirty(): void public function update($table = null) { if (!$table) { - /** @var \Cake\ORM\Table $repository */ $repository = $this->getRepository(); $table = $repository->getTable(); } @@ -1244,7 +1288,6 @@ public function update($table = null) */ public function delete(?string $table = null) { - /** @var \Cake\ORM\Table $repository */ $repository = $this->getRepository(); $this->from([$repository->getAlias() => $repository->getTable()]); @@ -1267,7 +1310,6 @@ public function delete(?string $table = null) */ public function insert(array $columns, array $types = []) { - /** @var \Cake\ORM\Table $repository */ $repository = $this->getRepository(); $table = $repository->getTable(); $this->into($table); @@ -1275,6 +1317,20 @@ public function insert(array $columns, array $types = []) return parent::insert($columns, $types); } + /** + * Returns a new Query that has automatic field aliasing disabled. + * + * @param \Cake\ORM\Table $table The table this query is starting on + * @return static + */ + public static function subquery(Table $table) + { + $query = new static($table->getConnection(), $table); + $query->aliasingEnabled = false; + + return $query; + } + /** * {@inheritDoc} * @@ -1378,7 +1434,6 @@ protected function _decorateResults(Traversable $result): ResultSetInterface if (!($result instanceof ResultSet) && $this->isBufferedResultsEnabled()) { $class = $this->_decoratorClass(); - /** @var \Cake\Datasource\ResultSetInterface $result */ $result = new $class($result->buffered()); } diff --git a/app/vendor/cakephp/cakephp/src/ORM/README.md b/app/vendor/cakephp/cakephp/src/ORM/README.md index 077b5fd5c..5af443ac1 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/README.md +++ b/app/vendor/cakephp/cakephp/src/ORM/README.md @@ -59,7 +59,7 @@ use Cake\ORM\Locator\LocatorAwareTrait; $articles = $this->getTableLocator()->get('Articles'); ``` -By default classes using `LocatorAwareTrait` will share a global locator instance. +By default, classes using `LocatorAwareTrait` will share a global locator instance. You can inject your own locator instance into the object: ```php @@ -78,8 +78,8 @@ In your table classes you can define the relations between your tables. CakePHP' supports 4 association types out of the box: * belongsTo - E.g. Many articles belong to a user. -* hasOne - E.g. A user has one profile -* hasMany - E.g. A user has many articles +* hasOne - E.g. A user has one profile. +* hasMany - E.g. A user has many articles. * belongsToMany - E.g. An article belongsToMany tags. You define associations in your table's `initialize()` method. See the @@ -149,7 +149,7 @@ $articles->delete($article); ## Meta Data Cache -It is recommended to enable meta data cache for production systems to avoid performance issues. +It is recommended to enable metadata cache for production systems to avoid performance issues. For e.g. file system strategy your bootstrap file could look like this: ```php @@ -164,7 +164,7 @@ $cacheConfig = [ Cache::setConfig('_cake_model_', $cacheConfig); ``` -Cache configs are optional so you must require ``cachephp/cache`` to add one. +Cache configs are optional, so you must require ``cachephp/cache`` to add one. ## Creating Custom Table and Entity Classes diff --git a/app/vendor/cakephp/cakephp/src/ORM/ResultSet.php b/app/vendor/cakephp/cakephp/src/ORM/ResultSet.php index b5b0333bb..147322b17 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/ResultSet.php +++ b/app/vendor/cakephp/cakephp/src/ORM/ResultSet.php @@ -18,7 +18,7 @@ use Cake\Collection\Collection; use Cake\Collection\CollectionTrait; -use Cake\Database\Exception; +use Cake\Database\Exception\DatabaseException; use Cake\Database\StatementInterface; use Cake\Datasource\EntityInterface; use Cake\Datasource\ResultSetInterface; @@ -159,7 +159,6 @@ class ResultSet implements ResultSetInterface */ public function __construct(Query $query, StatementInterface $statement) { - /** @var \Cake\ORM\Table $repository */ $repository = $query->getRepository(); $this->_statement = $statement; $this->_driver = $query->getConnection()->getDriver(); @@ -219,7 +218,7 @@ public function next(): void * * Part of Iterator interface. * - * @throws \Cake\Database\Exception + * @throws \Cake\Database\Exception\DatabaseException * @return void */ public function rewind(): void @@ -231,7 +230,7 @@ public function rewind(): void if (!$this->_useBuffering) { $msg = 'You cannot rewind an un-buffered ResultSet. ' . 'Use Query::bufferResults() to get a buffered ResultSet.'; - throw new Exception($msg); + throw new DatabaseException($msg); } $this->_index = 0; @@ -303,7 +302,7 @@ public function serialize(): string if (!$this->_useBuffering) { $msg = 'You cannot serialize an un-buffered ResultSet. ' . 'Use Query::bufferResults() to get a buffered ResultSet.'; - throw new Exception($msg); + throw new DatabaseException($msg); } while ($this->valid()) { diff --git a/app/vendor/cakephp/cakephp/src/ORM/Rule/IsUnique.php b/app/vendor/cakephp/cakephp/src/ORM/Rule/IsUnique.php index 76ea4a015..24cfeae05 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Rule/IsUnique.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Rule/IsUnique.php @@ -31,20 +31,28 @@ class IsUnique protected $_fields; /** - * The options to use. + * The unique check options * * @var array */ - protected $_options; + protected $_options = [ + 'allowMultipleNulls' => false, + ]; /** * Constructor. * + * ### Options + * + * - `allowMultipleNulls` Allows any field to have multiple null values. Defaults to false. + * * @param string[] $fields The list of fields to check uniqueness for + * @param array $options The options for unique checks. */ - public function __construct(array $fields) + public function __construct(array $fields, array $options = []) { $this->_fields = $fields; + $this->_options = $options + $this->_options; } /** @@ -61,8 +69,13 @@ public function __invoke(EntityInterface $entity, array $options): bool return true; } + $fields = $entity->extract($this->_fields); + if ($this->_options['allowMultipleNulls'] && array_filter($fields, 'is_null')) { + return true; + } + $alias = $options['repository']->getAlias(); - $conditions = $this->_alias($alias, $entity->extract($this->_fields)); + $conditions = $this->_alias($alias, $fields); if ($entity->isNew() === false) { $keys = (array)$options['repository']->getPrimaryKey(); $keys = $this->_alias($alias, $entity->extract($keys)); diff --git a/app/vendor/cakephp/cakephp/src/ORM/RulesChecker.php b/app/vendor/cakephp/cakephp/src/ORM/RulesChecker.php index bb12b95da..561843225 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/RulesChecker.php +++ b/app/vendor/cakephp/cakephp/src/ORM/RulesChecker.php @@ -37,12 +37,16 @@ class RulesChecker extends BaseRulesChecker * Returns a callable that can be used as a rule for checking the uniqueness of a value * in the table. * - * ### Example: + * ### Example * * ``` * $rules->add($rules->isUnique(['email'], 'The email should be unique')); * ``` * + * ### Options + * + * - `allowMultipleNulls` Allows any field to have multiple null values. Defaults to false. + * * @param string[] $fields The list of fields to check for uniqueness. * @param string|array|null $message The error message to show in case the rule does not pass. Can * also be an array of options. When an array, the 'message' key can be used to provide a message. @@ -50,6 +54,10 @@ class RulesChecker extends BaseRulesChecker */ public function isUnique(array $fields, $message = null): RuleInvoker { + $options = is_array($message) ? $message : ['message' => $message]; + $message = $options['message'] ?? null; + unset($options['message']); + if (!$message) { if ($this->_useI18n) { $message = __d('cake', 'This value is already in use'); @@ -60,7 +68,7 @@ public function isUnique(array $fields, $message = null): RuleInvoker $errorField = current($fields); - return $this->_addError(new IsUnique($fields), '_isUnique', compact('errorField', 'message')); + return $this->_addError(new IsUnique($fields, $options), '_isUnique', compact('errorField', 'message')); } /** diff --git a/app/vendor/cakephp/cakephp/src/ORM/SaveOptionsBuilder.php b/app/vendor/cakephp/cakephp/src/ORM/SaveOptionsBuilder.php index 5553e4e48..12270d58e 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/SaveOptionsBuilder.php +++ b/app/vendor/cakephp/cakephp/src/ORM/SaveOptionsBuilder.php @@ -141,7 +141,7 @@ protected function _checkAssociation(Table $table, string $association): void */ public function guard(bool $guard) { - $this->_options['guard'] = (bool)$guard; + $this->_options['guard'] = $guard; return $this; } @@ -168,7 +168,7 @@ public function validate(string $validate) */ public function checkExisting(bool $checkExisting) { - $this->_options['checkExisting'] = (bool)$checkExisting; + $this->_options['checkExisting'] = $checkExisting; return $this; } @@ -181,7 +181,7 @@ public function checkExisting(bool $checkExisting) */ public function checkRules(bool $checkRules) { - $this->_options['checkRules'] = (bool)$checkRules; + $this->_options['checkRules'] = $checkRules; return $this; } @@ -194,7 +194,7 @@ public function checkRules(bool $checkRules) */ public function atomic(bool $atomic) { - $this->_options['atomic'] = (bool)$atomic; + $this->_options['atomic'] = $atomic; return $this; } diff --git a/app/vendor/cakephp/cakephp/src/ORM/Table.php b/app/vendor/cakephp/cakephp/src/ORM/Table.php index 890777559..bc837d5b1 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/Table.php +++ b/app/vendor/cakephp/cakephp/src/ORM/Table.php @@ -125,6 +125,8 @@ * lifecycle methods below: * * - `beforeFind(EventInterface $event, Query $query, ArrayObject $options, boolean $primary)` + * - `beforeMarshal(EventInterface $event, ArrayObject $data, ArrayObject $options)` + * - `afterMarshal(EventInterface $event, EntityInterface $entity, ArrayObject $options)` * - `buildValidator(EventInterface $event, Validator $validator, string $name)` * - `buildRules(RulesChecker $rules)` * - `beforeRules(EventInterface $event, EntityInterface $entity, ArrayObject $options, string $operation)` @@ -134,8 +136,10 @@ * - `afterSaveCommit(EventInterface $event, EntityInterface $entity, ArrayObject $options)` * - `beforeDelete(EventInterface $event, EntityInterface $entity, ArrayObject $options)` * - `afterDelete(EventInterface $event, EntityInterface $entity, ArrayObject $options)` + * - `afterDeleteCommit(EventInterface $event, EntityInterface $entity, ArrayObject $options)` * * @see \Cake\Event\EventManager for reference on the events system. + * @link https://book.cakephp.org/4/en/orm/table-objects.html#event-list */ class Table implements RepositoryInterface, EventListenerInterface, EventDispatcherInterface, ValidatorAwareInterface { @@ -181,7 +185,7 @@ class Table implements RepositoryInterface, EventListenerInterface, EventDispatc /** * Name of the table as it can be found in the database * - * @var string + * @var string|null */ protected $_table; @@ -189,7 +193,7 @@ class Table implements RepositoryInterface, EventListenerInterface, EventDispatc * Human name giving to this particular instance. Multiple objects representing * the same database table can exist by using different aliases. * - * @var string + * @var string|null */ protected $_alias; @@ -210,14 +214,14 @@ class Table implements RepositoryInterface, EventListenerInterface, EventDispatc /** * The name of the field that represents the primary key in the table * - * @var string|string[] + * @var string|string[]|null */ protected $_primaryKey; /** * The name of the field that represents a human readable representation of a row * - * @var string|string[] + * @var string|string[]|null */ protected $_displayField; @@ -415,7 +419,7 @@ public function getAlias(): string { if ($this->_alias === null) { $alias = namespaceSplit(static::class); - $alias = substr(end($alias), 0, -5) ?: $this->_table; + $alias = substr(end($alias), 0, -5) ?: $this->getTable(); $this->_alias = $alias; } @@ -646,7 +650,7 @@ public function setPrimaryKey($key) public function getPrimaryKey() { if ($this->_primaryKey === null) { - $key = (array)$this->getSchema()->getPrimaryKey(); + $key = $this->getSchema()->getPrimaryKey(); if (count($key) === 1) { $key = $key[0]; } @@ -856,7 +860,6 @@ public function getBehavior(string $name): Behavior )); } - /** @var \Cake\ORM\Behavior $behavior */ $behavior = $this->_behaviors->get($name); return $behavior; @@ -894,7 +897,13 @@ public function getAssociation(string $name): Association { $association = $this->findAssociation($name); if (!$association) { - throw new InvalidArgumentException("The {$name} association is not defined on {$this->getAlias()}."); + $assocations = $this->associations()->keys(); + + $message = "The `{$name}` association is not defined on `{$this->getAlias()}`."; + if ($assocations) { + $message .= "\nValid associations are: " . implode(', ', $assocations); + } + throw new InvalidArgumentException($message); } return $association; @@ -1303,6 +1312,25 @@ public function findAll(Query $query, array $options): Query * ]); * ``` * + * The `valueField` can also be an array, in which case you can also specify + * the `valueSeparator` option to control how the values will be concatinated: + * + * ``` + * $table->find('list', [ + * 'valueField' => ['first_name', 'last_name'], + * 'valueSeparator' => ' | ', + * ]); + * ``` + * + * The results of this finder will be in the following form: + * + * ``` + * [ + * 1 => 'John | Doe', + * 2 => 'Steve | Smith' + * ] + * ``` + * * Results can be put together in bigger groups when they share a property, you * can customize the property to use for grouping by setting `groupField`: * @@ -1336,6 +1364,7 @@ public function findList(Query $query, array $options): Query 'keyField' => $this->getPrimaryKey(), 'valueField' => $this->getDisplayField(), 'groupField' => null, + 'valueSeparator' => ';', ]; if ( @@ -1436,13 +1465,14 @@ protected function _setFieldMatchers(array $options, array $keys): array } $fields = $options[$field]; - $options[$field] = function ($row) use ($fields) { + $glue = in_array($field, ['keyField', 'parentField'], true) ? ';' : $options['valueSeparator']; + $options[$field] = function ($row) use ($fields, $glue) { $matches = []; foreach ($fields as $field) { $matches[] = $row[$field]; } - return implode(';', $matches); + return implode($glue, $matches); }; } @@ -1673,6 +1703,17 @@ public function query(): Query return new Query($this->getConnection(), $this); } + /** + * Creates a new Query::subquery() instance for a table. + * + * @return \Cake\ORM\Query + * @see \Cake\ORM\Query::subquery() + */ + public function subquery(): Query + { + return Query::subquery($this); + } + /** * @inheritDoc */ @@ -2070,7 +2111,7 @@ protected function _insert(EntityInterface $entity, array $data) */ protected function _newId(array $primary) { - if (!$primary || count((array)$primary) > 1) { + if (!$primary || count($primary) > 1) { return null; } /** @var string $typeName */ @@ -2133,9 +2174,9 @@ protected function _update(EntityInterface $entity, array $data) * any one of the records fails to save due to failed validation or database * error. * - * @param \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface $entities Entities to save. + * @param array<\Cake\Datasource\EntityInterface>|\Cake\Datasource\ResultSetInterface $entities Entities to save. * @param array|\ArrayAccess|\Cake\ORM\SaveOptionsBuilder $options Options used when calling Table::save() for each entity. - * @return \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface|false False on failure, entities list on success. + * @return array<\Cake\Datasource\EntityInterface>|\Cake\Datasource\ResultSetInterface|false False on failure, entities list on success. * @throws \Exception */ public function saveMany(iterable $entities, $options = []) @@ -2154,9 +2195,9 @@ public function saveMany(iterable $entities, $options = []) * any one of the records fails to save due to failed validation or database * error. * - * @param \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface $entities Entities to save. + * @param array<\Cake\Datasource\EntityInterface>|\Cake\Datasource\ResultSetInterface $entities Entities to save. * @param array|\ArrayAccess $options Options used when calling Table::save() for each entity. - * @return \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface Entities list. + * @return array<\Cake\Datasource\EntityInterface>|\Cake\Datasource\ResultSetInterface Entities list. * @throws \Exception * @throws \Cake\ORM\Exception\PersistenceFailedException If an entity couldn't be saved. */ @@ -2166,18 +2207,26 @@ public function saveManyOrFail(iterable $entities, $options = []): iterable } /** - * @param \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface $entities Entities to save. + * @param array<\Cake\Datasource\EntityInterface>|\Cake\Datasource\ResultSetInterface $entities Entities to save. * @param array|\ArrayAccess|\Cake\ORM\SaveOptionsBuilder $options Options used when calling Table::save() for each entity. * @throws \Cake\ORM\Exception\PersistenceFailedException If an entity couldn't be saved. * @throws \Exception If an entity couldn't be saved. - * @return \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface Entities list. + * @return array<\Cake\Datasource\EntityInterface>|\Cake\Datasource\ResultSetInterface Entities list. */ protected function _saveMany(iterable $entities, $options = []): iterable { + $options = new ArrayObject( + (array)$options + [ + 'atomic' => true, + 'checkRules' => true, + '_primary' => true, + ] + ); + /** @var bool[] $isNew */ $isNew = []; $cleanup = function ($entities) use (&$isNew): void { - /** @var \Cake\Datasource\EntityInterface[] $entities */ + /** @var array<\Cake\Datasource\EntityInterface> $entities */ foreach ($entities as $key => $entity) { if (isset($isNew[$key]) && $isNew[$key]) { $entity->unset($this->getPrimaryKey()); @@ -2212,6 +2261,12 @@ protected function _saveMany(iterable $entities, $options = []): iterable throw new PersistenceFailedException($failed, ['saveMany']); } + if ($this->_transactionCommitted($options['atomic'], $options['_primary'])) { + foreach ($entities as $entity) { + $this->dispatchEvent('Model.afterSaveCommit', compact('entity', 'options')); + } + } + return $entities; } @@ -2274,9 +2329,9 @@ public function delete(EntityInterface $entity, $options = []): bool * any one of the records fails to delete due to failed validation or database * error. * - * @param \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface $entities Entities to delete. + * @param array<\Cake\Datasource\EntityInterface>|\Cake\Datasource\ResultSetInterface $entities Entities to delete. * @param array|\ArrayAccess $options Options used when calling Table::save() for each entity. - * @return \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface|false Entities list + * @return array<\Cake\Datasource\EntityInterface>|\Cake\Datasource\ResultSetInterface|false Entities list * on success, false on failure. * @see \Cake\ORM\Table::delete() for options and events related to this method. */ @@ -2298,9 +2353,9 @@ public function deleteMany(iterable $entities, $options = []) * any one of the records fails to delete due to failed validation or database * error. * - * @param \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface $entities Entities to delete. + * @param array<\Cake\Datasource\EntityInterface>|\Cake\Datasource\ResultSetInterface $entities Entities to delete. * @param array|\ArrayAccess $options Options used when calling Table::save() for each entity. - * @return \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface Entities list. + * @return array<\Cake\Datasource\EntityInterface>|\Cake\Datasource\ResultSetInterface Entities list. * @throws \Cake\ORM\Exception\PersistenceFailedException * @see \Cake\ORM\Table::delete() for options and events related to this method. */ @@ -2316,7 +2371,7 @@ public function deleteManyOrFail(iterable $entities, $options = []): iterable } /** - * @param \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface $entities Entities to delete. + * @param array<\Cake\Datasource\EntityInterface>|\Cake\Datasource\ResultSetInterface $entities Entities to delete. * @param array|\ArrayAccess $options Options used. * @return \Cake\Datasource\EntityInterface|null */ @@ -2407,13 +2462,16 @@ protected function _processDelete(EntityInterface $entity, ArrayObject $options) return (bool)$event->getResult(); } - $this->_associations->cascadeDelete( + $success = $this->_associations->cascadeDelete( $entity, ['_primary' => false] + $options->getArrayCopy() ); + if (!$success) { + return $success; + } $query = $this->query(); - $conditions = (array)$entity->extract($primaryKey); + $conditions = $entity->extract($primaryKey); $statement = $query->delete() ->where($conditions) ->execute(); @@ -2475,7 +2533,7 @@ public function callFinder(string $type, Query $query, array $options = []): Que } /** - * Provides the dynamic findBy and findByAll methods. + * Provides the dynamic findBy and findAllBy methods. * * @param string $method The method name that was fired. * @param array $args List of arguments passed to the function. @@ -2520,7 +2578,6 @@ protected function _dynamicFinder(string $method, array $args) ); } - $conditions = []; if ($hasOr === false && $hasAnd === false) { $conditions = $makeConditions([$fields], $args); } elseif ($hasOr !== false) { @@ -2682,6 +2739,7 @@ public function newEmptyEntity(): EntityInterface * @param array $data The data to build an entity with. * @param array $options A list of options for the object hydration. * @return \Cake\Datasource\EntityInterface + * @see \Cake\ORM\Marshaller::one() */ public function newEntity(array $data, array $options = []): EntityInterface { @@ -2723,7 +2781,7 @@ public function newEntity(array $data, array $options = []): EntityInterface * * @param array $data The data to build an entity with. * @param array $options A list of options for the objects hydration. - * @return \Cake\Datasource\EntityInterface[] An array of hydrated records. + * @return array<\Cake\Datasource\EntityInterface> An array of hydrated records. */ public function newEntities(array $data, array $options = []): array { @@ -2784,6 +2842,7 @@ public function newEntities(array $data, array $options = []): array * @param array $data key value list of fields to be merged into the entity * @param array $options A list of options for the object hydration. * @return \Cake\Datasource\EntityInterface + * @see \Cake\ORM\Marshaller::merge() */ public function patchEntity(EntityInterface $entity, array $data, array $options = []): EntityInterface { @@ -2820,11 +2879,11 @@ public function patchEntity(EntityInterface $entity, array $data, array $options * You can use the `Model.beforeMarshal` event to modify request data * before it is converted into entities. * - * @param \Cake\Datasource\EntityInterface[]|\Traversable $entities the entities that will get the + * @param array<\Cake\Datasource\EntityInterface>|\Traversable $entities the entities that will get the * data merged in * @param array $data list of arrays to be merged into the entities * @param array $options A list of options for the objects hydration. - * @return \Cake\Datasource\EntityInterface[] + * @return array<\Cake\Datasource\EntityInterface> */ public function patchEntities(iterable $entities, array $data, array $options = []): array { @@ -2912,6 +2971,7 @@ public function validateUnique($value, array $options, ?array $context = null): * The conventional method map is: * * - Model.beforeMarshal => beforeMarshal + * - Model.afterMarshal => afterMarshal * - Model.buildValidator => buildValidator * - Model.beforeFind => beforeFind * - Model.beforeSave => beforeSave @@ -2929,6 +2989,7 @@ public function implementedEvents(): array { $eventMap = [ 'Model.beforeMarshal' => 'beforeMarshal', + 'Model.afterMarshal' => 'afterMarshal', 'Model.buildValidator' => 'buildValidator', 'Model.beforeFind' => 'beforeFind', 'Model.beforeSave' => 'beforeSave', @@ -2999,10 +3060,10 @@ public function getSaveOptionsBuilder(array $options = []): SaveOptionsBuilder * * The properties for the associations to be loaded will be overwritten on each entity. * - * @param \Cake\Datasource\EntityInterface|\Cake\Datasource\EntityInterface[] $entities a single entity or list of entities + * @param \Cake\Datasource\EntityInterface|array<\Cake\Datasource\EntityInterface> $entities a single entity or list of entities * @param array $contain A `contain()` compatible array. * @see \Cake\ORM\Query::contain() - * @return \Cake\Datasource\EntityInterface|\Cake\Datasource\EntityInterface[] + * @return \Cake\Datasource\EntityInterface|array<\Cake\Datasource\EntityInterface> */ public function loadInto($entities, array $contain) { diff --git a/app/vendor/cakephp/cakephp/src/ORM/TableRegistry.php b/app/vendor/cakephp/cakephp/src/ORM/TableRegistry.php index 779003100..52a0c42ae 100644 --- a/app/vendor/cakephp/cakephp/src/ORM/TableRegistry.php +++ b/app/vendor/cakephp/cakephp/src/ORM/TableRegistry.php @@ -16,6 +16,7 @@ */ namespace Cake\ORM; +use Cake\Datasource\FactoryLocator; use Cake\ORM\Locator\LocatorInterface; /** @@ -57,21 +58,6 @@ */ class TableRegistry { - /** - * LocatorInterface implementation instance. - * - * @var \Cake\ORM\Locator\LocatorInterface - */ - protected static $_locator; - - /** - * Default LocatorInterface implementation class. - * - * @var string - * @psalm-var class-string<\Cake\ORM\Locator\TableLocator> - */ - protected static $_defaultLocatorClass = Locator\TableLocator::class; - /** * Returns a singleton instance of LocatorInterface implementation. * @@ -79,11 +65,8 @@ class TableRegistry */ public static function getTableLocator(): LocatorInterface { - if (static::$_locator === null) { - static::$_locator = new static::$_defaultLocatorClass(); - } - - return static::$_locator; + /** @var \Cake\ORM\Locator\LocatorInterface */ + return FactoryLocator::get('Table'); } /** @@ -94,7 +77,7 @@ public static function getTableLocator(): LocatorInterface */ public static function setTableLocator(LocatorInterface $tableLocator): void { - static::$_locator = $tableLocator; + FactoryLocator::add('Table', $tableLocator); } /** diff --git a/app/vendor/cakephp/cakephp/src/Routing/Asset.php b/app/vendor/cakephp/cakephp/src/Routing/Asset.php index eac796eaa..2e5e1215c 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Asset.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Asset.php @@ -214,13 +214,15 @@ public static function url(string $path, array $options = []): string protected static function encodeUrl(string $url): string { $path = parse_url($url, PHP_URL_PATH); + if ($path === false) { + $path = $url; + } + $parts = array_map('rawurldecode', explode('/', $path)); $parts = array_map('rawurlencode', $parts); $encoded = implode('/', $parts); - $url = str_replace($path, $encoded, $url); - - return $url; + return str_replace($path, $encoded, $url); } /** @@ -249,18 +251,23 @@ public static function assetTimestamp(string $path, $timestamp = null): string urldecode($path) ); $webrootPath = Configure::read('App.wwwRoot') . str_replace('/', DIRECTORY_SEPARATOR, $filepath); - if (file_exists($webrootPath)) { + if (is_file($webrootPath)) { return $path . '?' . filemtime($webrootPath); } + // Check for plugins and org prefixed plugins. $segments = explode('/', ltrim($filepath, '/')); $plugin = Inflector::camelize($segments[0]); + if (!Plugin::isLoaded($plugin) && count($segments) > 1) { + $plugin = implode('/', [$plugin, Inflector::camelize($segments[1])]); + unset($segments[1]); + } if (Plugin::isLoaded($plugin)) { unset($segments[0]); $pluginPath = Plugin::path($plugin) . 'webroot' . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $segments); - if (file_exists($pluginPath)) { + if (is_file($pluginPath)) { return $path . '?' . filemtime($pluginPath); } } @@ -299,12 +306,12 @@ public static function webroot(string $file, array $options = []): string $file = str_replace('/', '\\', $file); } - if (file_exists(Configure::read('App.wwwRoot') . $theme . $file)) { + if (is_file(Configure::read('App.wwwRoot') . $theme . $file)) { $webPath = $requestWebroot . $theme . $asset[0]; } else { $themePath = Plugin::path($themeName); $path = $themePath . 'webroot/' . $file; - if (file_exists($path)) { + if (is_file($path)) { $webPath = $requestWebroot . $theme . $asset[0]; } } diff --git a/app/vendor/cakephp/cakephp/src/Routing/Exception/DuplicateNamedRouteException.php b/app/vendor/cakephp/cakephp/src/Routing/Exception/DuplicateNamedRouteException.php index 93372e0c2..4bff190d6 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Exception/DuplicateNamedRouteException.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Exception/DuplicateNamedRouteException.php @@ -14,13 +14,13 @@ */ namespace Cake\Routing\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Throwable; /** * Exception raised when a route names used twice. */ -class DuplicateNamedRouteException extends Exception +class DuplicateNamedRouteException extends CakeException { /** * @inheritDoc diff --git a/app/vendor/cakephp/cakephp/src/Routing/Exception/MissingDispatcherFilterException.php b/app/vendor/cakephp/cakephp/src/Routing/Exception/MissingDispatcherFilterException.php index 6baa91ec4..d3b6985ff 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Exception/MissingDispatcherFilterException.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Exception/MissingDispatcherFilterException.php @@ -14,12 +14,12 @@ */ namespace Cake\Routing\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Exception raised when a Dispatcher filter could not be found */ -class MissingDispatcherFilterException extends Exception +class MissingDispatcherFilterException extends CakeException { /** * @inheritDoc diff --git a/app/vendor/cakephp/cakephp/src/Routing/Exception/MissingRouteException.php b/app/vendor/cakephp/cakephp/src/Routing/Exception/MissingRouteException.php index dacb615e2..b731c61b7 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Exception/MissingRouteException.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Exception/MissingRouteException.php @@ -14,14 +14,14 @@ */ namespace Cake\Routing\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Throwable; /** * Exception raised when a URL cannot be reverse routed * or when a URL cannot be parsed. */ -class MissingRouteException extends Exception +class MissingRouteException extends CakeException { /** * @inheritDoc diff --git a/app/vendor/cakephp/cakephp/src/Routing/Exception/RedirectException.php b/app/vendor/cakephp/cakephp/src/Routing/Exception/RedirectException.php index d000b4b4d..ff1beffdc 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Exception/RedirectException.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Exception/RedirectException.php @@ -16,7 +16,7 @@ */ namespace Cake\Routing\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * An exception subclass used by the routing layer to indicate @@ -27,8 +27,13 @@ * ``` * throw new RedirectException('http://example.com/some/path', 301); * ``` + * + * If you need a more general purpose redirect exception use + * Cake\Http\Exception\RedirectException instead of this class. + * + * @deprecated 4.1.0 Use Cake\Http\Exception\RedirectException instead. */ -class RedirectException extends Exception +class RedirectException extends CakeException { /** * @inheritDoc diff --git a/app/vendor/cakephp/cakephp/src/Routing/Middleware/AssetMiddleware.php b/app/vendor/cakephp/cakephp/src/Routing/Middleware/AssetMiddleware.php index c9ed66f5a..0eb2aee56 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Middleware/AssetMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Middleware/AssetMiddleware.php @@ -73,7 +73,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } $assetFile = $this->_getAssetFile($url); - if ($assetFile === null || !file_exists($assetFile)) { + if ($assetFile === null || !is_file($assetFile)) { return $handler->handle($request); } @@ -158,8 +158,8 @@ protected function deliverAsset(ServerRequestInterface $request, SplFileInfo $fi return $response ->withHeader('Content-Type', $contentType) ->withHeader('Cache-Control', 'public,max-age=' . $maxAge) - ->withHeader('Date', gmdate('D, j M Y G:i:s \G\M\T', time())) - ->withHeader('Last-Modified', gmdate('D, j M Y G:i:s \G\M\T', $modified)) - ->withHeader('Expires', gmdate('D, j M Y G:i:s \G\M\T', $expire)); + ->withHeader('Date', gmdate(DATE_RFC7231, time())) + ->withHeader('Last-Modified', gmdate(DATE_RFC7231, $modified)) + ->withHeader('Expires', gmdate(DATE_RFC7231, $expire)); } } diff --git a/app/vendor/cakephp/cakephp/src/Routing/Middleware/RoutingMiddleware.php b/app/vendor/cakephp/cakephp/src/Routing/Middleware/RoutingMiddleware.php index 04ddeef8f..66d56a80b 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Middleware/RoutingMiddleware.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Middleware/RoutingMiddleware.php @@ -18,9 +18,10 @@ use Cake\Cache\Cache; use Cake\Core\PluginApplicationInterface; +use Cake\Http\Exception\RedirectException; use Cake\Http\MiddlewareQueue; use Cake\Http\Runner; -use Cake\Routing\Exception\RedirectException; +use Cake\Routing\Exception\RedirectException as DeprecatedRedirectException; use Cake\Routing\RouteCollection; use Cake\Routing\Router; use Cake\Routing\RoutingApplicationInterface; @@ -135,11 +136,6 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface $params = (array)$request->getAttribute('params', []); $middleware = []; if (empty($params['controller'])) { - $parsedBody = $request->getParsedBody(); - if (is_array($parsedBody) && isset($parsedBody['_method'])) { - /** @var \Cake\Http\ServerRequest $request */ - $request = $request->withMethod($parsedBody['_method']); - } $params = Router::parseRequest($request) + $params; if (isset($params['_middleware'])) { $middleware = $params['_middleware']; @@ -152,7 +148,12 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } catch (RedirectException $e) { return new RedirectResponse( $e->getMessage(), - (int)$e->getCode() + $e->getCode() + ); + } catch (DeprecatedRedirectException $e) { + return new RedirectResponse( + $e->getMessage(), + $e->getCode() ); } $matching = Router::getRouteCollection()->getMiddleware($middleware); diff --git a/app/vendor/cakephp/cakephp/src/Routing/Route/RedirectRoute.php b/app/vendor/cakephp/cakephp/src/Routing/Route/RedirectRoute.php index 3db3b4b9f..3be731f98 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Route/RedirectRoute.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Route/RedirectRoute.php @@ -16,7 +16,7 @@ */ namespace Cake\Routing\Route; -use Cake\Routing\Exception\RedirectException; +use Cake\Http\Exception\RedirectException; use Cake\Routing\Router; /** @@ -59,7 +59,7 @@ public function __construct(string $template, array $defaults = [], array $optio * @param string $url The URL to parse. * @param string $method The HTTP method being used. * @return array|null Null on failure. An exception is raised on a successful match. Array return type is unused. - * @throws \Cake\Routing\Exception\RedirectException An exception is raised on successful match. + * @throws \Cake\Http\Exception\RedirectException An exception is raised on successful match. * This is used to halt route matching and signal to the middleware that a redirect should happen. */ public function parse(string $url, string $method = ''): ?array diff --git a/app/vendor/cakephp/cakephp/src/Routing/Route/Route.php b/app/vendor/cakephp/cakephp/src/Routing/Route/Route.php index a70ae0cc6..4e55b5796 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Route/Route.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Route/Route.php @@ -115,13 +115,17 @@ class Route * - `_ext` - Defines the extensions used for this route. * - `_middleware` - Define the middleware names for this route. * - `pass` - Copies the listed parameters into params['pass']. + * - `_method` - Defines the HTTP method(s) the route applies to. It can be + * a string or array of valid HTTP method name. * - `_host` - Define the host name pattern if you want this route to only match * specific host names. You can use `.*` and to create wildcard subdomains/hosts * e.g. `*.example.com` matches all subdomains on `example.com`. + * - '_port` - Define the port if you want this route to only match specific port number. * * @param string $template Template string with parameter placeholders * @param array $defaults Defaults for the route. * @param array $options Array of additional options for the Route + * @throws \InvalidArgumentException When `$options['_method']` are not in `VALID_METHODS` list. */ public function __construct(string $template, array $defaults = [], array $options = []) { @@ -168,7 +172,7 @@ public function getExtensions(): array * * @param string[] $methods The HTTP methods to accept. * @return $this - * @throws \InvalidArgumentException + * @throws \InvalidArgumentException When methods are not in `VALID_METHODS` list. */ public function setMethods(array $methods) { @@ -180,8 +184,9 @@ public function setMethods(array $methods) /** * Normalize method names to upper case and validate that they are valid HTTP methods. * - * @param string|array $methods Methods. - * @return string|array + * @param string|string[] $methods Methods. + * @return string|string[] + * @throws \InvalidArgumentException When methods are not in `VALID_METHODS` list. */ protected function normalizeAndValidateMethods($methods) { @@ -291,6 +296,7 @@ public function compile(): string $this->_writeRoute(); } + /** @var string */ return $this->_compiledRoute; } @@ -424,7 +430,7 @@ public function parseRequest(ServerRequestInterface $request): ?array return null; } - return $this->parse($uri->getPath(), (string)$request->getMethod()); + return $this->parse($uri->getPath(), $request->getMethod()); } /** @@ -436,6 +442,7 @@ public function parseRequest(ServerRequestInterface $request): ?array * @param string $url The URL to attempt to parse. * @param string $method The HTTP method of the request being parsed. * @return array|null An array of request parameters, or null on failure. + * @throws \InvalidArgumentException When method is not an empty string or in `VALID_METHODS` list. */ public function parse(string $url, string $method): ?array { @@ -765,7 +772,7 @@ protected function _matchMethod(array $url): bool $defaults = (array)$this->defaults['_method']; $methods = (array)$this->normalizeAndValidateMethods($url['_method']); foreach ($methods as $value) { - if (in_array($value, (array)$this->defaults['_method'], true)) { + if (in_array($value, $defaults, true)) { return true; } } diff --git a/app/vendor/cakephp/cakephp/src/Routing/RouteBuilder.php b/app/vendor/cakephp/cakephp/src/Routing/RouteBuilder.php index 426d903c7..04dc5e1ce 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/RouteBuilder.php +++ b/app/vendor/cakephp/cakephp/src/Routing/RouteBuilder.php @@ -596,7 +596,7 @@ public function loadPlugin(string $name) * Examples: * * ``` - * $routes->connect('/:controller/:action/*'); + * $routes->connect('/{controller}/{action}/*'); * ``` * * The first parameter will be used as a controller name while the second is @@ -613,7 +613,7 @@ public function loadPlugin(string $name) * * ``` * $routes->connect( - * '/:lang/:controller/:action/:id', + * '/{lang}/{controller}/{action}/{id}', * [], * ['id' => '[0-9]+', 'lang' => '[a-z]{3}'] * ); @@ -644,6 +644,10 @@ public function loadPlugin(string $name) * - `_ext` is an array of filename extensions that will be parsed out of the url if present. * See {@link \Cake\Routing\RouteCollection::setExtensions()}. * - `_method` Only match requests with specific HTTP verbs. + * - `_host` - Define the host name pattern if you want this route to only match + * specific host names. You can use `.*` and to create wildcard subdomains/hosts + * e.g. `*.example.com` matches all subdomains on `example.com`. + * - '_port` - Define the port if you want this route to only match specific port number. * * Example of using the `_method` condition: * @@ -766,7 +770,7 @@ protected function _makeRoute($route, $defaults, $options): Route * Examples: * * ``` - * $routes->redirect('/home/*', ['controller' => 'posts', 'action' => 'view']); + * $routes->redirect('/home/*', ['controller' => 'Posts', 'action' => 'view']); * ``` * * Redirects /home/* to /posts/view and passes the parameters to /posts/view. Using an array as the @@ -868,8 +872,14 @@ public function prefix(string $name, $params = [], $callback = null) * Routes connected in the scoped collection will have the correct path segment * prepended, and have a matching plugin routing key set. * + * ### Options + * + * - `path` The path prefix to use. Defaults to `Inflector::dasherize($name)`. + * - `_namePrefix` Set a prefix used for named routes. The prefix is prepended to the + * name of any route created in a scope callback. + * * @param string $name The plugin name to build routes for - * @param array|callable $options Either the options to use, or a callback + * @param array|callable $options Either the options to use, or a callback to build routes. * @param callable|null $callback The callback to invoke that builds the plugin routes * Only required when $options is defined. * @return $this @@ -881,9 +891,10 @@ public function plugin(string $name, $options = [], $callback = null) $options = []; } - $params = ['plugin' => $name] + $this->_params; $path = $options['path'] ?? '/' . Inflector::dasherize($name); - $this->scope($path, $params, $callback); + unset($options['path']); + $options = ['plugin' => $name] + $options; + $this->scope($path, $options, $callback); return $this; } @@ -895,6 +906,11 @@ public function plugin(string $name, $options = [], $callback = null) * added to. This means that both the current path and parameters will be appended * to the supplied parameters. * + * ### Special Keys in $params + * + * - `_namePrefix` Set a prefix used for named routes. The prefix is prepended to the + * name of any route created in a scope callback. + * * @param string $path The path to create a scope for. * @param array|callable $params Either the parameters to add to routes, or a callback. * @param callable|null $callback The callback to invoke that builds the plugin routes. @@ -937,7 +953,7 @@ public function scope(string $path, $params, $callback = null) } /** - * Connect the `/:controller` and `/:controller/:action/*` fallback routes. + * Connect the `/{controller}` and `/{controller}/{action}/*` fallback routes. * * This is a shortcut method for connecting fallback routes in a given scope. * diff --git a/app/vendor/cakephp/cakephp/src/Routing/RouteCollection.php b/app/vendor/cakephp/cakephp/src/Routing/RouteCollection.php index 892eec919..0cb87e739 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/RouteCollection.php +++ b/app/vendor/cakephp/cakephp/src/Routing/RouteCollection.php @@ -39,13 +39,6 @@ class RouteCollection */ protected $_routeTable = []; - /** - * The routes connected to this collection. - * - * @var \Cake\Routing\Route\Route[] - */ - protected $_routes = []; - /** * The hash map of named routes that are in this collection. * @@ -56,7 +49,7 @@ class RouteCollection /** * Routes indexed by path prefix. * - * @var array + * @var array> */ protected $_paths = []; @@ -91,8 +84,6 @@ class RouteCollection */ public function add(Route $route, array $options = []): void { - $this->_routes[] = $route; - // Explicit names if (isset($options['_name'])) { if (isset($this->_named[$options['_name']])) { @@ -136,10 +127,9 @@ public function parse(string $url, string $method = ''): array $decoded = urldecode($url); // Sort path segments matching longest paths first. - $paths = array_keys($this->_paths); - rsort($paths); + krsort($this->_paths); - foreach ($paths as $path) { + foreach ($this->_paths as $path => $routes) { if (strpos($decoded, $path) !== 0) { continue; } @@ -149,8 +139,8 @@ public function parse(string $url, string $method = ''): array [$url, $qs] = explode('?', $url, 2); parse_str($qs, $queryParameters); } - /** @var \Cake\Routing\Route\Route $route */ - foreach ($this->_paths[$path] as $route) { + + foreach ($routes as $route) { $r = $route->parse($url, $method); if ($r === null) { continue; @@ -186,16 +176,14 @@ public function parseRequest(ServerRequestInterface $request): array $urlPath = urldecode($uri->getPath()); // Sort path segments matching longest paths first. - $paths = array_keys($this->_paths); - rsort($paths); + krsort($this->_paths); - foreach ($paths as $path) { + foreach ($this->_paths as $path => $routes) { if (strpos($urlPath, $path) !== 0) { continue; } - /** @var \Cake\Routing\Route\Route $route */ - foreach ($this->_paths[$path] as $route) { + foreach ($routes as $route) { $r = $route->parseRequest($request); if ($r === null) { continue; @@ -320,7 +308,7 @@ public function match(array $url, array $context): string throw new MissingRouteException([ 'url' => $name, 'context' => $context, - 'message' => 'A named route was found for "%s", but matching failed.', + 'message' => "A named route was found for `{$name}`, but matching failed.", ]); } throw new MissingRouteException(['url' => $name, 'context' => $context]); @@ -348,7 +336,13 @@ public function match(array $url, array $context): string */ public function routes(): array { - return $this->_routes; + krsort($this->_paths); + + return array_reduce( + $this->_paths, + 'array_merge', + [] + ); } /** diff --git a/app/vendor/cakephp/cakephp/src/Routing/Router.php b/app/vendor/cakephp/cakephp/src/Routing/Router.php index d8ff527ad..0c2743b6d 100644 --- a/app/vendor/cakephp/cakephp/src/Routing/Router.php +++ b/app/vendor/cakephp/cakephp/src/Routing/Router.php @@ -146,7 +146,6 @@ class Router * parameters to the route collection. * * @var callable[] - * @psalm-var array */ protected static $_urlFilters = []; @@ -204,7 +203,7 @@ public static function getNamedExpressions(): array * shifted into the passed arguments, supplying patterns for routing parameters and supplying the name of a * custom routing class. * @return void - * @throws \Cake\Core\Exception\Exception + * @throws \Cake\Core\Exception\CakeException * @see \Cake\Routing\RouteBuilder::connect() * @see \Cake\Routing\Router::scope() */ @@ -332,7 +331,6 @@ public static function resetRoutes(): void * * @param callable $function The function to add * @return void - * @psalm-param \Closure|callable-string $function */ public static function addUrlFilter(callable $function): void { @@ -357,6 +355,7 @@ protected static function _applyUrlFilters(array $url): array if (is_array($filter)) { $ref = new ReflectionMethod($filter[0], $filter[1]); } else { + /** @psalm-var \Closure|callable-string $filter */ $ref = new ReflectionFunction($filter); } $message = sprintf( @@ -381,7 +380,7 @@ protected static function _applyUrlFilters(array $url): array * * - `Router::url('/posts/edit/1');` Returns the string with the base dir prepended. * This usage does not use reverser routing. - * - `Router::url(['controller' => 'posts', 'action' => 'edit']);` Returns a URL + * - `Router::url(['controller' => 'Posts', 'action' => 'edit']);` Returns a URL * generated through reverse routing. * - `Router::url(['_name' => 'custom-name', ...]);` Returns a URL generated * through reverse routing. This form allows you to leverage named routes. @@ -408,7 +407,7 @@ protected static function _applyUrlFilters(array $url): array * @param bool $full If true, the full base URL will be prepended to the result. * Default is false. * @return string Full translated URL with base path. - * @throws \Cake\Core\Exception\Exception When the route name is not found + * @throws \Cake\Core\Exception\CakeException When the route name is not found */ public static function url($url = null, bool $full = false): string { diff --git a/app/vendor/cakephp/cakephp/src/Routing/functions.php b/app/vendor/cakephp/cakephp/src/Routing/functions.php new file mode 100644 index 000000000..d43f6b788 --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/Routing/functions.php @@ -0,0 +1,40 @@ + false, + 'prefix' => false, + ]; + + return $url + $params; + } +} diff --git a/app/vendor/cakephp/cakephp/src/Shell/Helper/TableHelper.php b/app/vendor/cakephp/cakephp/src/Shell/Helper/TableHelper.php index 8949aabba..b384e9fbc 100644 --- a/app/vendor/cakephp/cakephp/src/Shell/Helper/TableHelper.php +++ b/app/vendor/cakephp/cakephp/src/Shell/Helper/TableHelper.php @@ -16,6 +16,7 @@ namespace Cake\Shell\Helper; use Cake\Console\Helper; +use UnexpectedValueException; /** * Create a visually pleasing ASCII art table @@ -45,7 +46,7 @@ protected function _calculateWidths(array $rows): array $widths = []; foreach ($rows as $line) { foreach (array_values($line) as $k => $v) { - $columnLength = $this->_cellWidth($v); + $columnLength = $this->_cellWidth((string)$v); if ($columnLength >= ($widths[$k] ?? 0)) { $widths[$k] = $columnLength; } @@ -58,12 +59,12 @@ protected function _calculateWidths(array $rows): array /** * Get the width of a cell exclusive of style tags. * - * @param string|null $text The text to calculate a width for. + * @param string $text The text to calculate a width for. * @return int The width of the textual content in visible characters. */ - protected function _cellWidth(?string $text): int + protected function _cellWidth(string $text): int { - if ($text === null) { + if (strlen($text) === 0) { return 0; } @@ -110,11 +111,20 @@ protected function _render(array $row, array $widths, array $options = []): void $out = ''; foreach (array_values($row) as $i => $column) { + $column = (string)$column; $pad = $widths[$i] - $this->_cellWidth($column); if (!empty($options['style'])) { $column = $this->_addStyle($column, $options['style']); } - $out .= '| ' . $column . str_repeat(' ', $pad) . ' '; + if (strlen($column) > 0 && preg_match('#(.*).+(.*)#', $column, $matches)) { + if ($matches[1] !== '' || $matches[2] !== '') { + throw new UnexpectedValueException('You cannot include text before or after the text-right tag.'); + } + $column = str_replace(['', ''], '', $column); + $out .= '| ' . str_repeat(' ', $pad) . $column . ' '; + } else { + $out .= '| ' . $column . str_repeat(' ', $pad) . ' '; + } } $out .= '|'; $this->_io->out($out); @@ -126,29 +136,31 @@ protected function _render(array $row, array $widths, array $options = []): void * Data will be output based on the order of the values * in the array. The keys will not be used to align data. * - * @param array $rows The data to render out. + * @param array $args The data to render out. * @return void */ - public function output(array $rows): void + public function output(array $args): void { - if (empty($rows)) { + if (empty($args)) { return; } + $this->_io->setStyle('text-right', ['text' => null]); + $config = $this->getConfig(); - $widths = $this->_calculateWidths($rows); + $widths = $this->_calculateWidths($args); $this->_rowSeparator($widths); if ($config['headers'] === true) { - $this->_render(array_shift($rows), $widths, ['style' => $config['headerStyle']]); + $this->_render(array_shift($args), $widths, ['style' => $config['headerStyle']]); $this->_rowSeparator($widths); } - if (empty($rows)) { + if (empty($args)) { return; } - foreach ($rows as $line) { + foreach ($args as $line) { $this->_render($line, $widths); if ($config['rowSeparator'] === true) { $this->_rowSeparator($widths); diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/ConsoleIntegrationTestTrait.php b/app/vendor/cakephp/cakephp/src/TestSuite/ConsoleIntegrationTestTrait.php index cb66dd39a..45f02d294 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/ConsoleIntegrationTestTrait.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/ConsoleIntegrationTestTrait.php @@ -19,7 +19,6 @@ use Cake\Console\CommandRunner; use Cake\Console\ConsoleIo; use Cake\Console\Exception\StopException; -use Cake\Core\Configure; use Cake\TestSuite\Constraint\Console\ContentsContain; use Cake\TestSuite\Constraint\Console\ContentsContainRow; use Cake\TestSuite\Constraint\Console\ContentsEmpty; @@ -32,11 +31,16 @@ use RuntimeException; /** - * A test case class intended to make integration tests of cake console commands - * easier. + * A bundle of methods that makes testing commands + * and shell classes easier. + * + * Enables you to call commands/shells with a + * full application context. */ trait ConsoleIntegrationTestTrait { + use ContainerStubTrait; + /** * Whether or not to use the CommandRunner * @@ -44,21 +48,6 @@ trait ConsoleIntegrationTestTrait */ protected $_useCommandRunner = false; - /** - * The customized application class name. - * - * @var string|null - * @psalm-var class-string<\Cake\Core\ConsoleApplicationInterface>|null - */ - protected $_appClass; - - /** - * The customized application constructor arguments. - * - * @var array|null - */ - protected $_appArgs; - /** * Last exit code * @@ -88,7 +77,7 @@ trait ConsoleIntegrationTestTrait protected $_in; /** - * Runs cli integration test + * Runs CLI integration test * * @param string $command Command to run * @param array $input Input values to pass to an interactive shell @@ -142,8 +131,6 @@ public function cleanupConsoleTrait(): void $this->_err = null; $this->_in = null; $this->_useCommandRunner = false; - $this->_appClass = null; - $this->_appArgs = null; } /** @@ -157,20 +144,6 @@ public function useCommandRunner(): void $this->_useCommandRunner = true; } - /** - * Configure the application class to use in console integration tests. - * - * @param string $class The application class name. - * @param array|null $constructorArgs The constructor arguments for your application class. - * @return void - * @psalm-param class-string<\Cake\Core\ConsoleApplicationInterface> $class - */ - public function configApplication(string $class, ?array $constructorArgs): void - { - $this->_appClass = $class; - $this->_appArgs = $constructorArgs; - } - /** * Asserts shell exited with the expected code * @@ -307,15 +280,10 @@ public function assertErrorEmpty(string $message = ''): void protected function makeRunner() { if ($this->_useCommandRunner) { - if ($this->_appClass) { - $appClass = $this->_appClass; - } else { - /** @psalm-var class-string<\Cake\Core\ConsoleApplicationInterface> */ - $appClass = Configure::read('App.namespace') . '\Application'; - } - $appArgs = $this->_appArgs ?: [CONFIG]; + /** @var \Cake\Core\ConsoleApplicationInterface $app */ + $app = $this->createApp(); - return new CommandRunner(new $appClass(...$appArgs)); + return new CommandRunner($app); } return new LegacyCommandRunner(); diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsContain.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsContain.php index 71978a6ab..d700c9664 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsContain.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Console/ContentsContain.php @@ -40,6 +40,6 @@ public function matches($other): bool */ public function toString(): string { - return sprintf('is in %s,' . PHP_EOL . 'actual result:' . PHP_EOL . $this->contents, $this->output); + return sprintf('is in %s,' . PHP_EOL . 'actual result:' . PHP_EOL, $this->output) . $this->contents; } } diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailConstraintBase.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailConstraintBase.php index b9ab96ace..fcb26812d 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailConstraintBase.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailConstraintBase.php @@ -51,7 +51,7 @@ public function getMessages() { $messages = TestEmailTransport::getMessages(); - if ($this->at) { + if ($this->at !== null) { if (!isset($messages[$this->at])) { return []; } diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContains.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContains.php index bbb5407c1..74ba2aace 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContains.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContains.php @@ -38,12 +38,12 @@ class MailContains extends MailConstraintBase */ public function matches($other): bool { + $other = preg_quote($other, '/'); $messages = $this->getMessages(); foreach ($messages as $message) { - $method = 'getBody' . ($this->type ? ucfirst($this->type) : 'String'); + $method = $this->getTypeMethod(); $message = $message->$method(); - $other = preg_quote($other, '/'); if (preg_match("/$other/", $message) > 0) { return true; } @@ -52,6 +52,36 @@ public function matches($other): bool return false; } + /** + * @return string + */ + protected function getTypeMethod(): string + { + return 'getBody' . ($this->type ? ucfirst($this->type) : 'String'); + } + + /** + * Returns the type-dependent strings of all messages + * respects $this->at + * + * @return string + */ + protected function getAssertedMessages(): string + { + $messageMembers = []; + $messages = $this->getMessages(); + foreach ($messages as $message) { + $method = $this->getTypeMethod(); + $messageMembers[] = $message->$method(); + } + if ($this->at && isset($messageMembers[$this->at - 1])) { + $messageMembers = [$messageMembers[$this->at - 1]]; + } + $result = implode(PHP_EOL, $messageMembers); + + return PHP_EOL . 'was: ' . mb_substr($result, 0, 1000); + } + /** * Assertion message string * @@ -60,9 +90,9 @@ public function matches($other): bool public function toString(): string { if ($this->at) { - return sprintf('is in email #%d', $this->at); + return sprintf('is in email #%d', $this->at) . $this->getAssertedMessages(); } - return 'is in an email'; + return 'is in an email' . $this->getAssertedMessages(); } } diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContainsHtml.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContainsHtml.php index d9a351196..70f5c869e 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContainsHtml.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContainsHtml.php @@ -38,9 +38,9 @@ class MailContainsHtml extends MailContains public function toString(): string { if ($this->at) { - return sprintf('is in the html message of email #%d', $this->at); + return sprintf('is in the html message of email #%d', $this->at) . $this->getAssertedMessages(); } - return 'is in the html message of an email'; + return 'is in the html message of an email' . $this->getAssertedMessages(); } } diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContainsText.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContainsText.php index a81fac490..a2c047a19 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContainsText.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailContainsText.php @@ -38,9 +38,9 @@ class MailContainsText extends MailContains public function toString(): string { if ($this->at) { - return sprintf('is in the text message of email #%d', $this->at); + return sprintf('is in the text message of email #%d', $this->at) . $this->getAssertedMessages(); } - return 'is in the text message of an email'; + return 'is in the text message of an email' . $this->getAssertedMessages(); } } diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailSubjectContains.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailSubjectContains.php new file mode 100644 index 000000000..ca5d7c93f --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Email/MailSubjectContains.php @@ -0,0 +1,86 @@ +getMessages(); + foreach ($messages as $message) { + $subject = $message->getOriginalSubject(); + if (strpos($subject, $other) !== false) { + return true; + } + } + + return false; + } + + /** + * Returns the subjects of all messages + * respects $this->at + * + * @return string + */ + protected function getAssertedMessages(): string + { + $messageMembers = []; + $messages = $this->getMessages(); + foreach ($messages as $message) { + $messageMembers[] = $message->getSubject(); + } + if ($this->at && isset($messageMembers[$this->at - 1])) { + $messageMembers = [$messageMembers[$this->at - 1]]; + } + $result = implode(PHP_EOL, $messageMembers); + + return PHP_EOL . 'was: ' . mb_substr($result, 0, 1000); + } + + /** + * Assertion message string + * + * @return string + */ + public function toString(): string + { + if ($this->at) { + return sprintf('is in an email subject #%d', $this->at) . $this->getAssertedMessages(); + } + + return 'is in an email subject' . $this->getAssertedMessages(); + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusCodeBase.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusCodeBase.php index 5e4fc70b8..4940081e6 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusCodeBase.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusCodeBase.php @@ -19,11 +19,13 @@ * StatusCodeBase * * @internal + * @template TCode as int|array */ abstract class StatusCodeBase extends ResponseBase { /** * @var int|array + * @psalm-var TCode */ protected $code; @@ -67,6 +69,7 @@ protected function statusCodeBetween(int $min, int $max): bool */ protected function failureDescription($other): string { + /** @psalm-suppress InternalMethod */ return $this->toString(); } } diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusError.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusError.php index cd3e5712b..189d8112c 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusError.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusError.php @@ -19,11 +19,12 @@ * StatusError * * @internal + * @extends \Cake\TestSuite\Constraint\Response\StatusCodeBase> */ class StatusError extends StatusCodeBase { /** - * @var int[] + * @var array */ protected $code = [400, 429]; diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusFailure.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusFailure.php index b77e43284..33d4df2de 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusFailure.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusFailure.php @@ -19,11 +19,12 @@ * StatusFailure * * @internal + * @extends \Cake\TestSuite\Constraint\Response\StatusCodeBase> */ class StatusFailure extends StatusCodeBase { /** - * @var int[] + * @var array */ protected $code = [500, 505]; diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusOk.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusOk.php index 2497b08e6..8047ff918 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusOk.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusOk.php @@ -19,11 +19,12 @@ * StatusOk * * @internal + * @extends \Cake\TestSuite\Constraint\Response\StatusCodeBase> */ class StatusOk extends StatusCodeBase { /** - * @var int[] + * @var array */ protected $code = [200, 204]; diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusSuccess.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusSuccess.php index 6752bdb6e..10b873dee 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusSuccess.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Response/StatusSuccess.php @@ -19,11 +19,12 @@ * StatusSuccess * * @internal + * @extends \Cake\TestSuite\Constraint\Response\StatusCodeBase> */ class StatusSuccess extends StatusCodeBase { /** - * @var int[] + * @var array */ protected $code = [200, 308]; diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Session/SessionEquals.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Session/SessionEquals.php index 3df049e44..5cb470394 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Session/SessionEquals.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Session/SessionEquals.php @@ -15,9 +15,7 @@ */ namespace Cake\TestSuite\Constraint\Session; -use Cake\Http\Session; use Cake\Utility\Hash; -use PHPUnit\Framework\AssertionFailedError; use PHPUnit\Framework\Constraint\Constraint; /** @@ -27,34 +25,18 @@ */ class SessionEquals extends Constraint { - /** - * @var \Cake\Http\Session - */ - protected $session; - /** * @var string */ protected $path; - /** - * @var mixed - */ - protected $value; - /** * Constructor * - * @param \Cake\Http\Session|null $session Session * @param string $path Session Path */ - public function __construct(?Session $session, string $path) + public function __construct(string $path) { - if (!$session) { - throw new AssertionFailedError('There is no stored session data. Perhaps you need to run a request?'); - } - - $this->session = $session; $this->path = $path; } diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Session/SessionHasKey.php b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Session/SessionHasKey.php new file mode 100644 index 000000000..2240d3e0f --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Constraint/Session/SessionHasKey.php @@ -0,0 +1,66 @@ +path = $path; + } + + /** + * Compare session value + * + * @param mixed $other Value to compare with + * @return bool + */ + public function matches($other): bool + { + // Server::run calls Session::close at the end of the request. + // Which means, that we cannot use Session object here to access the session data. + // Call to Session::read will start new session (and will erase the data). + return Hash::check($_SESSION, $this->path) === true; + } + + /** + * Assertion message + * + * @return string + */ + public function toString(): string + { + return 'is a path present in the session'; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/ContainerStubTrait.php b/app/vendor/cakephp/cakephp/src/TestSuite/ContainerStubTrait.php new file mode 100644 index 000000000..35b68ac7a --- /dev/null +++ b/app/vendor/cakephp/cakephp/src/TestSuite/ContainerStubTrait.php @@ -0,0 +1,169 @@ +|class-string<\Cake\Core\ConsoleApplicationInterface>|null + * @var string|null + */ + protected $_appClass; + + /** + * The customized application constructor arguments. + * + * @var array|null + */ + protected $_appArgs; + + /** + * The collection of container services. + * + * @var array + */ + private $containerServices = []; + + /** + * Configure the application class to use in integration tests. + * + * @param string $class The application class name. + * @param array|null $constructorArgs The constructor arguments for your application class. + * @return void + * @psalm-param class-string<\Cake\Core\HttpApplicationInterface>|class-string<\Cake\Core\ConsoleApplicationInterface> $class + */ + public function configApplication(string $class, ?array $constructorArgs): void + { + $this->_appClass = $class; + $this->_appArgs = $constructorArgs; + } + + /** + * Create an application instance. + * + * Uses the configuration set in `configApplication()`. + * + * @return \Cake\Core\HttpApplicationInterface|\Cake\Core\ConsoleApplicationInterface + */ + protected function createApp() + { + if ($this->_appClass) { + $appClass = $this->_appClass; + } else { + /** @psalm-var class-string<\Cake\Http\BaseApplication> */ + $appClass = Configure::read('App.namespace') . '\Application'; + } + if (!class_exists($appClass)) { + throw new LogicException("Cannot load `{$appClass}` for use in integration testing."); + } + $appArgs = $this->_appArgs ?: [CONFIG]; + + $app = new $appClass(...$appArgs); + if (!empty($this->containerServices) && method_exists($app, 'getEventManager')) { + $app->getEventManager()->on('Application.buildContainer', [$this, 'modifyContainer']); + } + + return $app; + } + + /** + * Add a mocked service to the container. + * + * When the container is created the provided classname + * will be mapped to the factory function. The factory + * function will be used to create mocked services. + * + * @param string $class The class or interface you want to define. + * @param \Closure $factory The factory function for mocked services. + * @return $this + */ + public function mockService(string $class, Closure $factory) + { + $this->containerServices[$class] = $factory; + + return $this; + } + + /** + * Remove a mocked service to the container. + * + * @param string $class The class or interface you want to remove. + * @return $this + */ + public function removeMockService(string $class) + { + unset($this->containerServices[$class]); + + return $this; + } + + /** + * Wrap the application's container with one containing mocks. + * + * If any mocked services are defined, the application's container + * will be replaced with one containing mocks. The original + * container will be set as a delegate to the mock container. + * + * @param \Cake\Event\EventInterface $event The event + * @param \Cake\Core\ContainerInterface $container The container to wrap. + * @return null|\Cake\Core\ContainerInterface + */ + public function modifyContainer(EventInterface $event, ContainerInterface $container): ?ContainerInterface + { + if (empty($this->containerServices)) { + return null; + } + foreach ($this->containerServices as $key => $factory) { + if ($container->has($key)) { + $container->extend($key)->setConcrete($factory); + } else { + $container->add($key, $factory); + } + } + + return $container; + } + + /** + * Clears any mocks that were defined and cleans + * up application class configuration. + * + * @after + * @return void + */ + public function cleanupContainer(): void + { + $this->_appArgs = null; + $this->_appClass = null; + $this->containerServices = []; + } +} diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/EmailTrait.php b/app/vendor/cakephp/cakephp/src/TestSuite/EmailTrait.php index fb54ad535..7d6926af5 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/EmailTrait.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/EmailTrait.php @@ -24,6 +24,7 @@ use Cake\TestSuite\Constraint\Email\MailSentFrom; use Cake\TestSuite\Constraint\Email\MailSentTo; use Cake\TestSuite\Constraint\Email\MailSentWith; +use Cake\TestSuite\Constraint\Email\MailSubjectContains; use Cake\TestSuite\Constraint\Email\NoMailSent; /** @@ -150,7 +151,7 @@ public function assertMailContainsTextAt(int $at, string $contents, string $mess * * @param int $at Email index * @param string $expected Contents - * @param string $parameter Email getter parameter (e.g. "cc", "subject") + * @param string $parameter Email getter parameter (e.g. "cc", "bcc") * @param string $message Message * @return void */ @@ -244,4 +245,29 @@ public function assertMailSentWith(string $expected, string $parameter, string $ { $this->assertThat($expected, new MailSentWith(null, $parameter), $message); } + + /** + * Asserts an email subject contains expected contents + * + * @param string $contents Contents + * @param string $message Message + * @return void + */ + public function assertMailSubjectContains(string $contents, string $message = ''): void + { + $this->assertThat($contents, new MailSubjectContains(), $message); + } + + /** + * Asserts an email at a specific index contains expected html contents + * + * @param int $at Email index + * @param string $contents Contents + * @param string $message Message + * @return void + */ + public function assertMailSubjectContainsAt(int $at, string $contents, string $message = ''): void + { + $this->assertThat($contents, new MailSubjectContains($at), $message); + } } diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureManager.php b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureManager.php index a8bbbd1dc..e0f00874e 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureManager.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureManager.php @@ -17,7 +17,7 @@ namespace Cake\TestSuite\Fixture; use Cake\Core\Configure; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Database\ConstraintsInterface; use Cake\Database\Schema\TableSchema; use Cake\Database\Schema\TableSchemaAwareInterface; @@ -273,7 +273,7 @@ protected function _setupTable( * * @param \Cake\TestSuite\TestCase $test The test to inspect for fixture loading. * @return void - * @throws \Cake\Core\Exception\Exception When fixture records cannot be inserted. + * @throws \Cake\Core\Exception\CakeException When fixture records cannot be inserted. * @throws \RuntimeException */ public function load(TestCase $test): void @@ -307,7 +307,7 @@ public function load(TestCase $test): void get_class($test), $e->getMessage() ); - throw new Exception($msg, null, $e); + throw new CakeException($msg, null, $e); } } } @@ -334,7 +334,7 @@ public function load(TestCase $test): void get_class($test), $e->getMessage() ); - throw new Exception($msg, null, $e); + throw new CakeException($msg, null, $e); } } }; @@ -352,7 +352,7 @@ public function load(TestCase $test): void get_class($test), $e->getMessage() ); - throw new Exception($msg, null, $e); + throw new CakeException($msg, null, $e); } } }; @@ -437,12 +437,6 @@ public function unload(TestCase $test): void $fixture->dropConstraints($db); } } - - foreach ($fixtures as $fixture) { - if ($this->isFixtureSetup($configName, $fixture)) { - $fixture->truncate($db); - } - } }; $this->_runOperation($fixtures, $truncate); } @@ -451,39 +445,39 @@ public function unload(TestCase $test): void * Creates a single fixture table and loads data into it. * * @param string $name of the fixture - * @param \Cake\Datasource\ConnectionInterface|null $db Connection instance or null + * @param \Cake\Datasource\ConnectionInterface|null $connection Connection instance or null * to get a Connection from the fixture. * @param bool $dropTables Whether or not tables should be dropped and re-created. * @return void * @throws \UnexpectedValueException if $name is not a previously loaded class */ - public function loadSingle(string $name, ?ConnectionInterface $db = null, bool $dropTables = true): void + public function loadSingle(string $name, ?ConnectionInterface $connection = null, bool $dropTables = true): void { if (!isset($this->_fixtureMap[$name])) { throw new UnexpectedValueException(sprintf('Referenced fixture class %s not found', $name)); } $fixture = $this->_fixtureMap[$name]; - if (!$db) { - $db = ConnectionManager::get($fixture->connection()); + if (!$connection) { + $connection = ConnectionManager::get($fixture->connection()); } - if (!$this->isFixtureSetup($db->configName(), $fixture)) { - $sources = $db->getSchemaCollection()->listTables(); - $this->_setupTable($fixture, $db, $sources, $dropTables); + if (!$this->isFixtureSetup($connection->configName(), $fixture)) { + $sources = $connection->getSchemaCollection()->listTables(); + $this->_setupTable($fixture, $connection, $sources, $dropTables); } if (!$dropTables) { if ($fixture instanceof ConstraintsInterface) { - $fixture->dropConstraints($db); + $fixture->dropConstraints($connection); } - $fixture->truncate($db); + $fixture->truncate($connection); } if ($fixture instanceof ConstraintsInterface) { - $fixture->createConstraints($db); + $fixture->createConstraints($connection); } - $fixture->insert($db); + $fixture->insert($connection); } /** diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/TestFixture.php b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/TestFixture.php index a97dc8ac1..f51277028 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/TestFixture.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/Fixture/TestFixture.php @@ -15,7 +15,7 @@ */ namespace Cake\TestSuite\Fixture; -use Cake\Core\Exception\Exception as CakeException; +use Cake\Core\Exception\CakeException; use Cake\Database\ConstraintsInterface; use Cake\Database\Schema\TableSchema; use Cake\Database\Schema\TableSchemaAwareInterface; @@ -97,7 +97,7 @@ class TestFixture implements ConstraintsInterface, FixtureInterface, TableSchema /** * Instantiate the fixture. * - * @throws \Cake\Core\Exception\Exception on invalid datasource usage. + * @throws \Cake\Core\Exception\CakeException on invalid datasource usage. */ public function __construct() { @@ -212,7 +212,7 @@ protected function _schemaFromFields(): void * Build fixture schema from a table in another datasource. * * @return void - * @throws \Cake\Core\Exception\Exception when trying to import from an empty table. + * @throws \Cake\Core\Exception\CakeException when trying to import from an empty table. */ protected function _schemaFromImport(): void { @@ -244,7 +244,7 @@ protected function _schemaFromImport(): void * Build fixture schema directly from the datasource * * @return void - * @throws \Cake\Core\Exception\Exception when trying to reflect a table that does not exist + * @throws \Cake\Core\Exception\CakeException when trying to reflect a table that does not exist */ protected function _schemaFromReflection(): void { @@ -268,7 +268,7 @@ protected function _schemaFromReflection(): void /** * @inheritDoc */ - public function create(ConnectionInterface $db): bool + public function create(ConnectionInterface $connection): bool { if (empty($this->_schema)) { return false; @@ -279,9 +279,10 @@ public function create(ConnectionInterface $db): bool } try { - $queries = $this->_schema->createSql($db); + /** @psalm-suppress ArgumentTypeCoercion */ + $queries = $this->_schema->createSql($connection); foreach ($queries as $query) { - $stmt = $db->prepare($query); + $stmt = $connection->prepare($query); $stmt->execute(); $stmt->closeCursor(); } @@ -303,20 +304,21 @@ public function create(ConnectionInterface $db): bool /** * @inheritDoc */ - public function drop(ConnectionInterface $db): bool + public function drop(ConnectionInterface $connection): bool { if (empty($this->_schema)) { return false; } if (empty($this->import) && empty($this->fields)) { - return true; + return $this->truncate($connection); } try { - $sql = $this->_schema->dropSql($db); + /** @psalm-suppress ArgumentTypeCoercion */ + $sql = $this->_schema->dropSql($connection); foreach ($sql as $stmt) { - $db->execute($stmt)->closeCursor(); + $connection->execute($stmt)->closeCursor(); } } catch (Exception $e) { return false; @@ -328,11 +330,11 @@ public function drop(ConnectionInterface $db): bool /** * @inheritDoc */ - public function insert(ConnectionInterface $db) + public function insert(ConnectionInterface $connection) { if (isset($this->records) && !empty($this->records)) { [$fields, $values, $types] = $this->_getRecords(); - $query = $db->newQuery() + $query = $connection->newQuery() ->insert($fields, $types) ->into($this->sourceName()); @@ -351,7 +353,7 @@ public function insert(ConnectionInterface $db) /** * @inheritDoc */ - public function createConstraints(ConnectionInterface $db): bool + public function createConstraints(ConnectionInterface $connection): bool { if (empty($this->_constraints)) { return true; @@ -361,14 +363,15 @@ public function createConstraints(ConnectionInterface $db): bool $this->_schema->addConstraint($name, $data); } - $sql = $this->_schema->addConstraintSql($db); + /** @psalm-suppress ArgumentTypeCoercion */ + $sql = $this->_schema->addConstraintSql($connection); if (empty($sql)) { return true; } foreach ($sql as $stmt) { - $db->execute($stmt)->closeCursor(); + $connection->execute($stmt)->closeCursor(); } return true; @@ -377,20 +380,21 @@ public function createConstraints(ConnectionInterface $db): bool /** * @inheritDoc */ - public function dropConstraints(ConnectionInterface $db): bool + public function dropConstraints(ConnectionInterface $connection): bool { if (empty($this->_constraints)) { return true; } - $sql = $this->_schema->dropConstraintSql($db); + /** @psalm-suppress ArgumentTypeCoercion */ + $sql = $this->_schema->dropConstraintSql($connection); if (empty($sql)) { return true; } foreach ($sql as $stmt) { - $db->execute($stmt)->closeCursor(); + $connection->execute($stmt)->closeCursor(); } foreach ($this->_constraints as $name => $data) { @@ -429,11 +433,12 @@ protected function _getRecords(): array /** * @inheritDoc */ - public function truncate(ConnectionInterface $db): bool + public function truncate(ConnectionInterface $connection): bool { - $sql = $this->_schema->truncateSql($db); + /** @psalm-suppress ArgumentTypeCoercion */ + $sql = $this->_schema->truncateSql($connection); foreach ($sql as $stmt) { - $db->execute($stmt)->closeCursor(); + $connection->execute($stmt)->closeCursor(); } return true; diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestTrait.php b/app/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestTrait.php index e964e2946..929cc79e1 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestTrait.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestTrait.php @@ -17,9 +17,10 @@ use Cake\Controller\Controller; use Cake\Core\Configure; -use Cake\Database\Exception as DatabaseException; +use Cake\Database\Exception\DatabaseException; use Cake\Error\ExceptionRenderer; use Cake\Event\EventInterface; +use Cake\Event\EventManager; use Cake\Form\FormProtector; use Cake\Http\Middleware\CsrfProtectionMiddleware; use Cake\Http\Session; @@ -51,6 +52,7 @@ use Cake\TestSuite\Constraint\Response\StatusSuccess; use Cake\TestSuite\Constraint\Session\FlashParamEquals; use Cake\TestSuite\Constraint\Session\SessionEquals; +use Cake\TestSuite\Constraint\Session\SessionHasKey; use Cake\TestSuite\Constraint\View\LayoutFileEquals; use Cake\TestSuite\Constraint\View\TemplateFileEquals; use Cake\TestSuite\Stub\TestExceptionRenderer; @@ -74,21 +76,7 @@ trait IntegrationTestTrait { use CookieCryptTrait; - - /** - * The customized application class name. - * - * @var string|null - * @psalm-var class-string<\Cake\Core\HttpApplicationInterface>|null - */ - protected $_appClass; - - /** - * The customized application constructor arguments. - * - * @var array|null - */ - protected $_appArgs; + use ContainerStubTrait; /** * The data used to build the next request. @@ -100,7 +88,7 @@ trait IntegrationTestTrait /** * The response for the most recent request. * - * @var \Psr\Http\Message\ResponseInterface + * @var \Psr\Http\Message\ResponseInterface|null */ protected $_response; @@ -128,7 +116,7 @@ trait IntegrationTestTrait /** * The controller used in the last request. * - * @var \Cake\Controller\Controller + * @var \Cake\Controller\Controller|null */ protected $_controller; @@ -180,9 +168,9 @@ trait IntegrationTestTrait /** * Stored flash messages before render * - * @var array|null + * @var array */ - protected $_flashMessages; + protected $_flashMessages = []; /** * @var string|null @@ -214,25 +202,10 @@ public function cleanup(): void $this->_viewName = null; $this->_layoutName = null; $this->_requestSession = null; - $this->_appClass = null; - $this->_appArgs = null; $this->_securityToken = false; $this->_csrfToken = false; $this->_retainFlashMessages = false; - } - - /** - * Configure the application class to use in integration tests. - * - * @param string $class The application class name. - * @param array|null $constructorArgs The constructor arguments for your application class. - * @return void - * @psalm-param class-string<\Cake\Core\HttpApplicationInterface> $class - */ - public function configApplication(string $class, ?array $constructorArgs): void - { - $this->_appClass = $class; - $this->_appArgs = $constructorArgs; + $this->_flashMessages = []; } /** @@ -517,7 +490,11 @@ protected function _sendRequest($url, $method, $data = []): void */ protected function _makeDispatcher(): MiddlewareDispatcher { - return new MiddlewareDispatcher($this, $this->_appClass, $this->_appArgs); + EventManager::instance()->on('Controller.initialize', [$this, 'controllerSpy']); + /** @var \Cake\Core\HttpApplicationInterface $app */ + $app = $this->createApp(); + + return new MiddlewareDispatcher($app); } /** @@ -535,14 +512,22 @@ public function controllerSpy(EventInterface $event, ?Controller $controller = n } $this->_controller = $controller; $events = $controller->getEventManager(); - $events->on('View.beforeRender', function ($event, $viewFile) use ($controller): void { + $flashCapture = function (EventInterface $event): void { + if (!$this->_retainFlashMessages) { + return; + } + $controller = $event->getSubject(); + $this->_flashMessages = Hash::merge( + $this->_flashMessages, + $controller->getRequest()->getSession()->read('Flash') + ); + }; + $events->on('Controller.beforeRedirect', ['priority' => -100], $flashCapture); + $events->on('Controller.beforeRender', ['priority' => -100], $flashCapture); + $events->on('View.beforeRender', function ($event, $viewFile): void { if (!$this->_viewName) { $this->_viewName = $viewFile; } - if ($this->_retainFlashMessages) { - /** @psalm-suppress PossiblyInvalidPropertyAssignmentValue */ - $this->_flashMessages = $controller->getRequest()->getSession()->read('Flash'); - } }); $events->on('View.beforeLayout', function ($event, $viewFile): void { $this->_layoutName = $viewFile; @@ -583,7 +568,6 @@ protected function _buildRequest(string $url, $method, $data = []): array 'defaults' => 'php', ]; $session = Session::create($sessionConfig); - $session->write($this->_session); [$url, $query, $hostInfo] = $this->_url($url); $tokenUrl = $url; @@ -636,6 +620,7 @@ protected function _buildRequest(string $url, $method, $data = []): array } $props['cookies'] = $this->_cookie; + $session->write($this->_session); $props = Hash::merge($props, $this->_request); return $props; @@ -659,7 +644,6 @@ protected function _addTokens(string $url, array $data): array $formProtector = new FormProtector(['unlockedFields' => $this->_unlockedFields]); foreach ($keys as $field) { - /** @psalm-suppress PossiblyNullArgument */ $formProtector->addField($field); } $tokenData = $formProtector->buildTokenData($url, 'cli'); @@ -670,11 +654,23 @@ protected function _addTokens(string $url, array $data): array if ($this->_csrfToken === true) { $middleware = new CsrfProtectionMiddleware(); - if (!isset($this->_cookie['csrfToken'])) { - $this->_cookie['csrfToken'] = $middleware->createToken(); + $token = null; + if (!isset($this->_cookie['csrfToken']) && !isset($this->_session['csrfToken'])) { + $token = $middleware->createToken(); + } elseif (isset($this->_cookie['csrfToken'])) { + $token = $this->_cookie['csrfToken']; + } else { + $token = $this->_session['csrfToken']; } + + // Add the token to both the session and cookie to cover + // both types of CSRF tokens. We generate the token with the cookie + // middleware as cookie tokens will be accepted by session csrf, but not + // the inverse. + $this->_session['csrfToken'] = $token; + $this->_cookie['csrfToken'] = $token; if (!isset($data['_csrfToken'])) { - $data['_csrfToken'] = $this->_cookie['csrfToken']; + $data['_csrfToken'] = $token; } } @@ -829,6 +825,10 @@ public function assertResponseCode(int $code, string $message = ''): void */ public function assertRedirect($url = null, $message = ''): void { + if (!$this->_response) { + $this->fail('No response set, cannot assert header.'); + } + $verboseMessage = $this->extractVerboseMessage($message); $this->assertThat(null, new HeaderSet($this->_response, 'Location'), $verboseMessage); @@ -852,6 +852,10 @@ public function assertRedirect($url = null, $message = ''): void */ public function assertRedirectEquals($url = null, $message = '') { + if (!$this->_response) { + $this->fail('No response set, cannot assert header.'); + } + $verboseMessage = $this->extractVerboseMessage($message); $this->assertThat(null, new HeaderSet($this->_response, 'Location'), $verboseMessage); @@ -869,6 +873,10 @@ public function assertRedirectEquals($url = null, $message = '') */ public function assertRedirectContains(string $url, string $message = ''): void { + if (!$this->_response) { + $this->fail('No response set, cannot assert header.'); + } + $verboseMessage = $this->extractVerboseMessage($message); $this->assertThat(null, new HeaderSet($this->_response, 'Location'), $verboseMessage); $this->assertThat($url, new HeaderContains($this->_response, 'Location'), $verboseMessage); @@ -883,6 +891,10 @@ public function assertRedirectContains(string $url, string $message = ''): void */ public function assertRedirectNotContains(string $url, string $message = ''): void { + if (!$this->_response) { + $this->fail('No response set, cannot assert header.'); + } + $verboseMessage = $this->extractVerboseMessage($message); $this->assertThat(null, new HeaderSet($this->_response, 'Location'), $verboseMessage); $this->assertThat($url, new HeaderNotContains($this->_response, 'Location'), $verboseMessage); @@ -910,6 +922,10 @@ public function assertNoRedirect(string $message = ''): void */ public function assertHeader(string $header, string $content, string $message = ''): void { + if (!$this->_response) { + $this->fail('No response set, cannot assert header.'); + } + $verboseMessage = $this->extractVerboseMessage($message); $this->assertThat(null, new HeaderSet($this->_response, $header), $verboseMessage); $this->assertThat($content, new HeaderEquals($this->_response, $header), $verboseMessage); @@ -925,6 +941,10 @@ public function assertHeader(string $header, string $content, string $message = */ public function assertHeaderContains(string $header, string $content, string $message = ''): void { + if (!$this->_response) { + $this->fail('No response set, cannot assert header.'); + } + $verboseMessage = $this->extractVerboseMessage($message); $this->assertThat(null, new HeaderSet($this->_response, $header), $verboseMessage); $this->assertThat($content, new HeaderContains($this->_response, $header), $verboseMessage); @@ -940,6 +960,10 @@ public function assertHeaderContains(string $header, string $content, string $me */ public function assertHeaderNotContains(string $header, string $content, string $message = ''): void { + if (!$this->_response) { + $this->fail('No response set, cannot assert header.'); + } + $verboseMessage = $this->extractVerboseMessage($message); $this->assertThat(null, new HeaderSet($this->_response, $header), $verboseMessage); $this->assertThat($content, new HeaderNotContains($this->_response, $header), $verboseMessage); @@ -994,6 +1018,10 @@ public function assertResponseNotEquals($content, $message = ''): void */ public function assertResponseContains(string $content, string $message = '', bool $ignoreCase = false): void { + if (!$this->_response) { + $this->fail('No response set, cannot assert content.'); + } + $verboseMessage = $this->extractVerboseMessage($message); $this->assertThat($content, new BodyContains($this->_response, $ignoreCase), $verboseMessage); } @@ -1008,6 +1036,10 @@ public function assertResponseContains(string $content, string $message = '', bo */ public function assertResponseNotContains(string $content, string $message = '', bool $ignoreCase = false): void { + if (!$this->_response) { + $this->fail('No response set, cannot assert content.'); + } + $verboseMessage = $this->extractVerboseMessage($message); $this->assertThat($content, new BodyNotContains($this->_response, $ignoreCase), $verboseMessage); } @@ -1097,7 +1129,33 @@ public function assertLayout(string $content, string $message = ''): void public function assertSession($expected, string $path, string $message = ''): void { $verboseMessage = $this->extractVerboseMessage($message); - $this->assertThat($expected, new SessionEquals($this->_requestSession, $path), $verboseMessage); + $this->assertThat($expected, new SessionEquals($path), $verboseMessage); + } + + /** + * Asserts session key exists. + * + * @param string $path The session data path. Uses Hash::get() compatible notation. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertSessionHasKey(string $path, string $message = ''): void + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($path, new SessionHasKey($path), $verboseMessage); + } + + /** + * Asserts a session key does not exist. + * + * @param string $path The session data path. Uses Hash::get() compatible notation. + * @param string $message The failure message that will be appended to the generated message. + * @return void + */ + public function assertSessionNotHasKey(string $path, string $message = ''): void + { + $verboseMessage = $this->extractVerboseMessage($message); + $this->assertThat($path, $this->logicalNot(new SessionHasKey($path)), $verboseMessage); } /** diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/MiddlewareDispatcher.php b/app/vendor/cakephp/cakephp/src/TestSuite/MiddlewareDispatcher.php index c8803c175..7deff01f4 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/MiddlewareDispatcher.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/MiddlewareDispatcher.php @@ -15,15 +15,14 @@ */ namespace Cake\TestSuite; -use Cake\Core\Configure; +use Cake\Core\HttpApplicationInterface; use Cake\Core\PluginApplicationInterface; -use Cake\Event\EventManager; +use Cake\Http\FlashMessage; use Cake\Http\Server; use Cake\Http\ServerRequest; use Cake\Http\ServerRequestFactory; use Cake\Routing\Router; use Cake\Routing\RoutingApplicationInterface; -use LogicException; use Psr\Http\Message\ResponseInterface; /** @@ -34,28 +33,6 @@ */ class MiddlewareDispatcher { - /** - * The test case being run. - * - * @var \Cake\TestSuite\TestCase - */ - protected $_test; - - /** - * The application class name - * - * @var string - * @psalm-var class-string<\Cake\Core\HttpApplicationInterface> - */ - protected $_class; - - /** - * Constructor arguments for your application class. - * - * @var array - */ - protected $_constructorArgs; - /** * The application that is being dispatched. * @@ -66,31 +43,11 @@ class MiddlewareDispatcher /** * Constructor * - * @param \Cake\TestSuite\TestCase $test The test case to run. - * @param string|null $class The application class name. Defaults to App\Application. - * @param array|null $constructorArgs The constructor arguments for your application class. - * Defaults to `['./config']` - * @throws \LogicException If it cannot load class for use in integration testing. - * @psalm-param class-string<\Cake\Core\HttpApplicationInterface>|null $class + * @param \Cake\Core\HttpApplicationInterface $app The test case to run. */ - public function __construct( - TestCase $test, - ?string $class = null, - ?array $constructorArgs = null - ) { - $this->_test = $test; - if ($class === null) { - /** @psalm-var class-string<\Cake\Core\HttpApplicationInterface> */ - $class = Configure::read('App.namespace') . '\Application'; - } - $this->_class = $class; - $this->_constructorArgs = $constructorArgs ?: [CONFIG]; - - if (!class_exists($this->_class)) { - throw new LogicException("Cannot load `{$this->_class}` for use in integration testing.", 0); - } - - $this->app = new $this->_class(...$this->_constructorArgs); + public function __construct(HttpApplicationInterface $app) + { + $this->app = $app; } /** @@ -164,7 +121,9 @@ protected function _createRequest(array $spec): ServerRequest $spec['cookies'], $spec['files'] ); - $request = $request->withAttribute('session', $spec['session']); + $request = $request + ->withAttribute('session', $spec['session']) + ->withAttribute('flash', new FlashMessage($spec['session'])); return $request; } @@ -178,13 +137,6 @@ protected function _createRequest(array $spec): ServerRequest */ public function execute(array $requestSpec): ResponseInterface { - // Spy on the controller using the initialize hook instead - // of the dispatcher hooks as those will be going away one day. - EventManager::instance()->on( - 'Controller.initialize', - [$this->_test, 'controllerSpy'] - ); - $server = new Server($this->app); return $server->run($this->_createRequest($requestSpec)); diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/StringCompareTrait.php b/app/vendor/cakephp/cakephp/src/TestSuite/StringCompareTrait.php index d83b8381b..bcac1cfa6 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/StringCompareTrait.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/StringCompareTrait.php @@ -64,6 +64,6 @@ public function assertSameAsFile(string $path, string $result): void } $expected = file_get_contents($path); - $this->assertTextEquals($expected, $result); + $this->assertTextEquals($expected, $result, 'Content does not match file ' . $path); } } diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/TestCase.php b/app/vendor/cakephp/cakephp/src/TestSuite/TestCase.php index 439e17600..2d40cda1c 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/TestCase.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/TestCase.php @@ -29,6 +29,10 @@ use Cake\TestSuite\Constraint\EventFiredWith; use Cake\Utility\Inflector; use LogicException; +use PHPUnit\Framework\Constraint\DirectoryExists; +use PHPUnit\Framework\Constraint\FileExists; +use PHPUnit\Framework\Constraint\LogicalNot; +use PHPUnit\Framework\Constraint\RegularExpression; use PHPUnit\Framework\TestCase as BaseTestCase; use ReflectionClass; use ReflectionException; @@ -80,6 +84,72 @@ abstract class TestCase extends BaseTestCase */ protected $_configure = []; + /** + * Asserts that a string matches a given regular expression. + * + * @param string $pattern Regex pattern + * @param string $string String to test + * @param string $message Message + * @return void + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @codeCoverageIgnore + */ + public static function assertMatchesRegularExpression(string $pattern, string $string, string $message = ''): void + { + static::assertThat($string, new RegularExpression($pattern), $message); + } + + /** + * Asserts that a string does not match a given regular expression. + * + * @param string $pattern Regex pattern + * @param string $string String to test + * @param string $message Message + * @return void + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertDoesNotMatchRegularExpression( + string $pattern, + string $string, + string $message = '' + ): void { + static::assertThat( + $string, + new LogicalNot( + new RegularExpression($pattern) + ), + $message + ); + } + + /** + * Asserts that a file does not exist. + * + * @param string $filename Filename + * @param string $message Message + * @return void + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @codeCoverageIgnore + */ + public static function assertFileDoesNotExist(string $filename, string $message = ''): void + { + static::assertThat($filename, new LogicalNot(new FileExists()), $message); + } + + /** + * Asserts that a directory does not exist. + * + * @param string $directory Directory + * @param string $message Message + * @return void + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @codeCoverageIgnore + */ + public static function assertDirectoryDoesNotExist(string $directory, string $message = ''): void + { + static::assertThat($directory, new LogicalNot(new DirectoryExists()), $message); + } + /** * Overrides SimpleTestCase::skipIf to provide a boolean return value * @@ -138,7 +208,7 @@ public function deprecated(callable $callable): void * * @return void */ - public function setUp(): void + protected function setUp(): void { parent::setUp(); @@ -157,7 +227,7 @@ public function setUp(): void * * @return void */ - public function tearDown(): void + protected function tearDown(): void { parent::tearDown(); if ($this->_configure) { @@ -182,9 +252,13 @@ public function tearDown(): void */ public function loadFixtures(): void { + if ($this->autoFixtures) { + throw new RuntimeException('Cannot use `loadFixtures()` with `$autoFixtures` enabled.'); + } if ($this->fixtureManager === null) { throw new RuntimeException('No fixture manager to load the test fixture'); } + $args = func_get_args(); foreach ($args as $class) { $this->fixtureManager->loadSingle($class, null, $this->dropTables); @@ -206,7 +280,7 @@ public function loadFixtures(): void * `Cake\TestSuite\IntegrationTestCaseTrait` to better simulate all routes * and plugins being loaded. * - * @param array|null $appArgs Constuctor parameters for the application class. + * @param array|null $appArgs Constructor parameters for the application class. * @return void * @since 4.0.1 */ @@ -474,6 +548,41 @@ public function assertTextNotContains( } } + /** + * Assert that a string matches SQL with db-specific characters like quotes removed. + * + * @param string $expected The expected sql + * @param string $actual The sql to compare + * @param string $message The message to display on failure + * @return void + */ + public function assertEqualsSql( + string $expected, + string $actual, + string $message = '' + ): void { + $this->assertEquals($expected, preg_replace('/[`"\[\]]/', '', $actual), $message); + } + + /** + * Assertion for comparing a regex pattern against a query having its identifiers + * quoted. It accepts queries quoted with the characters `<` and `>`. If the third + * parameter is set to true, it will alter the pattern to both accept quoted and + * unquoted queries + * + * @param string $pattern The expected sql pattern + * @param string $actual The sql to compare + * @param bool $optional Whether quote characters (marked with <>) are optional + * @return void + */ + public function assertRegExpSql(string $pattern, string $actual, bool $optional = false): void + { + $optional = $optional ? '?' : ''; + $pattern = str_replace('<', '[`"\[]' . $optional, $pattern); + $pattern = str_replace('>', '[`"\]]' . $optional, $pattern); + $this->assertMatchesRegularExpression('#' . $pattern . '#', $actual); + } + /** * Asserts HTML tags. * @@ -522,7 +631,7 @@ public function assertHtml(array $expected, string $string, bool $fullDebug = fa { $regex = []; $normalized = []; - foreach ((array)$expected as $key => $val) { + foreach ($expected as $key => $val) { if (!is_numeric($key)) { $normalized[] = [$key => $val]; } else { @@ -536,6 +645,7 @@ public function assertHtml(array $expected, string $string, bool $fullDebug = fa } $i++; if (is_string($tags) && $tags[0] === '<') { + /** @psalm-suppress InvalidArrayOffset */ $tags = [substr($tags, 1) => []]; } elseif (is_string($tags)) { $tagsTrimmed = preg_replace('/\s+/m', '', $tags); @@ -651,7 +761,7 @@ public function assertHtml(array $expected, string $string, bool $fullDebug = fa debug($string); debug($regex); } - $this->assertRegExp( + $this->assertMatchesRegularExpression( $expression, $string, sprintf('Item #%d / regex #%d failed: %s', $itemNum, $i, $description) @@ -802,12 +912,27 @@ public function getMockForModel(string $alias, array $methods = [], array $optio [, $baseClass] = pluginSplit($alias); $options += ['alias' => $baseClass, 'connection' => $connection]; $options += $locator->getConfig($alias); + $reflection = new ReflectionClass($className); + $classMethods = array_map(function ($method) { + return $method->name; + }, $reflection->getMethods()); + + $existingMethods = array_intersect($classMethods, $methods); + $nonExistingMethods = array_diff($methods, $existingMethods); + + $builder = $this->getMockBuilder($className) + ->setConstructorArgs([$options]); + + if ($existingMethods || !$nonExistingMethods) { + $builder->onlyMethods($existingMethods); + } + + if ($nonExistingMethods) { + $builder->addMethods($nonExistingMethods); + } /** @var \Cake\ORM\Table $mock */ - $mock = $this->getMockBuilder($className) - ->onlyMethods($methods) - ->setConstructorArgs([$options]) - ->getMock(); + $mock = $builder->getMock(); if (empty($options['entityClass']) && $mock->getEntityClass() === Entity::class) { $parts = explode('\\', $className); @@ -856,11 +981,35 @@ protected function _getTableClassName(string $alias, array $options): string * Set the app namespace * * @param string $appNamespace The app namespace, defaults to "TestApp". - * @return void + * @return string|null The previous app namespace or null if not set. */ - public static function setAppNamespace(string $appNamespace = 'TestApp'): void + public static function setAppNamespace(string $appNamespace = 'TestApp'): ?string { + $previous = Configure::read('App.namespace'); Configure::write('App.namespace', $appNamespace); + + return $previous; + } + + /** + * Adds a fixture to this test case. + * + * Examples: + * - core.Tags + * - app.MyRecords + * - plugin.MyPluginName.MyModelName + * + * Use this method inside your test cases' {@link getFixtures()} method + * to build up the fixture list. + * + * @param string $fixture Fixture + * @return $this + */ + protected function addFixture(string $fixture) + { + $this->fixtures[] = $fixture; + + return $this; } /** diff --git a/app/vendor/cakephp/cakephp/src/TestSuite/TestListenerTrait.php b/app/vendor/cakephp/cakephp/src/TestSuite/TestListenerTrait.php index 5821a2ab1..440530d7d 100644 --- a/app/vendor/cakephp/cakephp/src/TestSuite/TestListenerTrait.php +++ b/app/vendor/cakephp/cakephp/src/TestSuite/TestListenerTrait.php @@ -86,14 +86,14 @@ public function addFailure(Test $test, AssertionFailedError $e, float $time): vo /** * @inheritDoc */ - public function addRiskyTest(Test $test, Throwable $e, float $time): void + public function addRiskyTest(Test $test, Throwable $t, float $time): void { } /** * @inheritDoc */ - public function addIncompleteTest(Test $test, Throwable $e, float $time): void + public function addIncompleteTest(Test $test, Throwable $t, float $time): void { } } diff --git a/app/vendor/cakephp/cakephp/src/Utility/CookieCryptTrait.php b/app/vendor/cakephp/cakephp/src/Utility/CookieCryptTrait.php index 390b50d43..afe10e028 100644 --- a/app/vendor/cakephp/cakephp/src/Utility/CookieCryptTrait.php +++ b/app/vendor/cakephp/cakephp/src/Utility/CookieCryptTrait.php @@ -156,7 +156,7 @@ protected function _decode(string $value, $encrypt, ?string $key) * Implode method to keep keys are multidimensional arrays * * @param array $array Map of key and values - * @return string A json encoded string. + * @return string A JSON encoded string. */ protected function _implode(array $array): string { diff --git a/app/vendor/cakephp/cakephp/src/Utility/Exception/XmlException.php b/app/vendor/cakephp/cakephp/src/Utility/Exception/XmlException.php index 70d08e331..b4779d6b5 100644 --- a/app/vendor/cakephp/cakephp/src/Utility/Exception/XmlException.php +++ b/app/vendor/cakephp/cakephp/src/Utility/Exception/XmlException.php @@ -14,16 +14,12 @@ */ namespace Cake\Utility\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Exception class for Xml. This exception will be thrown from Xml when it * encounters an error. */ -class XmlException extends Exception +class XmlException extends CakeException { - /** - * @inheritDoc - */ - protected $_defaultCode = 0; } diff --git a/app/vendor/cakephp/cakephp/src/Utility/Hash.php b/app/vendor/cakephp/cakephp/src/Utility/Hash.php index bdc17bf66..827540144 100644 --- a/app/vendor/cakephp/cakephp/src/Utility/Hash.php +++ b/app/vendor/cakephp/cakephp/src/Utility/Hash.php @@ -461,7 +461,7 @@ public static function remove(array $data, string $path): array * following the path specified in `$groupPath`. * * @param array $data Array from where to extract keys and values - * @param string|string[] $keyPath A dot-separated string. + * @param string|string[]|null $keyPath A dot-separated string. * @param string|string[]|null $valuePath A dot-separated string. * @param string|null $groupPath A dot-separated string. * @return array Combined array @@ -478,11 +478,13 @@ public static function combine(array $data, $keyPath, $valuePath = null, ?string $format = array_shift($keyPath); /** @var array $keys */ $keys = static::format($data, $keyPath, $format); + } elseif ($keyPath === null) { + $keys = $keyPath; } else { /** @var array $keys */ $keys = static::extract($data, $keyPath); } - if (empty($keys)) { + if ($keyPath !== null && empty($keys)) { return []; } @@ -496,10 +498,10 @@ public static function combine(array $data, $keyPath, $valuePath = null, ?string $vals = static::extract($data, $valuePath); } if (empty($vals)) { - $vals = array_fill(0, count($keys), null); + $vals = array_fill(0, $keys === null ? count($data) : count($keys), null); } - if (count($keys) !== count($vals)) { + if (is_array($keys) && count($keys) !== count($vals)) { throw new RuntimeException( 'Hash::combine() needs an equal number of keys + values.' ); @@ -508,7 +510,7 @@ public static function combine(array $data, $keyPath, $valuePath = null, ?string if ($groupPath !== null) { $group = static::extract($data, $groupPath); if (!empty($group)) { - $c = count($keys); + $c = is_array($keys) ? count($keys) : count($vals); $out = []; for ($i = 0; $i < $c; $i++) { if (!isset($group[$i])) { @@ -517,7 +519,11 @@ public static function combine(array $data, $keyPath, $valuePath = null, ?string if (!isset($out[$group[$i]])) { $out[$group[$i]] = []; } - $out[$group[$i]][$keys[$i]] = $vals[$i]; + if ($keys === null) { + $out[$group[$i]][] = $vals[$i]; + } else { + $out[$group[$i]][$keys[$i]] = $vals[$i]; + } } return $out; @@ -527,7 +533,7 @@ public static function combine(array $data, $keyPath, $valuePath = null, ?string return []; } - return array_combine($keys, $vals); + return array_combine($keys ?? range(0, count($vals) - 1), $vals); } /** @@ -947,7 +953,7 @@ public static function apply(array $data, string $path, callable $function) { $values = (array)static::extract($data, $path); - return call_user_func($function, $values); + return $function($values); } /** @@ -1105,10 +1111,10 @@ protected static function _squash(array $data, $key = null): array public static function diff(array $data, array $compare): array { if (empty($data)) { - return (array)$compare; + return $compare; } if (empty($compare)) { - return (array)$data; + return $data; } $intersection = array_intersect_key($data, $compare); while (($key = key($intersection)) !== null) { @@ -1140,8 +1146,8 @@ public static function mergeDiff(array $data, array $compare): array foreach ($compare as $key => $value) { if (!array_key_exists($key, $data)) { $data[$key] = $value; - } elseif (is_array($value)) { - $data[$key] = static::mergeDiff($data[$key], $compare[$key]); + } elseif (is_array($value) && is_array($data[$key])) { + $data[$key] = static::mergeDiff($data[$key], $value); } } @@ -1235,7 +1241,7 @@ public static function nest(array $data, array $options = []): array $parentId = static::get($result, $parentKeys); if (isset($idMap[$id][$options['children']])) { - $idMap[$id] = array_merge($result, (array)$idMap[$id]); + $idMap[$id] = array_merge($result, $idMap[$id]); } else { $idMap[$id] = array_merge($result, [$options['children'] => []]); } diff --git a/app/vendor/cakephp/cakephp/src/Utility/Inflector.php b/app/vendor/cakephp/cakephp/src/Utility/Inflector.php index 62abe2ca5..73eed4c1d 100644 --- a/app/vendor/cakephp/cakephp/src/Utility/Inflector.php +++ b/app/vendor/cakephp/cakephp/src/Utility/Inflector.php @@ -271,10 +271,17 @@ public static function pluralize(string $word): string } if (!isset(static::$_cache['irregular']['pluralize'])) { - static::$_cache['irregular']['pluralize'] = '(?:' . implode('|', array_keys(static::$_irregular)) . ')'; + $words = array_keys(static::$_irregular); + static::$_cache['irregular']['pluralize'] = '/(.*?(?:\\b|_))(' . implode('|', $words) . ')$/i'; + + $upperWords = array_map('ucfirst', $words); + static::$_cache['irregular']['upperPluralize'] = '/(.*?(?:\\b|[a-z]))(' . implode('|', $upperWords) . ')$/'; } - if (preg_match('/(.*?(?:\\b|_))(' . static::$_cache['irregular']['pluralize'] . ')$/i', $word, $regs)) { + if ( + preg_match(static::$_cache['irregular']['pluralize'], $word, $regs) || + preg_match(static::$_cache['irregular']['upperPluralize'], $word, $regs) + ) { static::$_cache['pluralize'][$word] = $regs[1] . substr($regs[2], 0, 1) . substr(static::$_irregular[strtolower($regs[2])], 1); @@ -282,10 +289,10 @@ public static function pluralize(string $word): string } if (!isset(static::$_cache['uninflected'])) { - static::$_cache['uninflected'] = '(?:' . implode('|', static::$_uninflected) . ')'; + static::$_cache['uninflected'] = '/^(' . implode('|', static::$_uninflected) . ')$/i'; } - if (preg_match('/^(' . static::$_cache['uninflected'] . ')$/i', $word, $regs)) { + if (preg_match(static::$_cache['uninflected'], $word, $regs)) { static::$_cache['pluralize'][$word] = $word; return $word; @@ -316,10 +323,19 @@ public static function singularize(string $word): string } if (!isset(static::$_cache['irregular']['singular'])) { - static::$_cache['irregular']['singular'] = '(?:' . implode('|', static::$_irregular) . ')'; + $wordList = array_values(static::$_irregular); + static::$_cache['irregular']['singular'] = '/(.*?(?:\\b|_))(' . implode('|', $wordList) . ')$/i'; + + $upperWordList = array_map('ucfirst', $wordList); + static::$_cache['irregular']['singularUpper'] = '/(.*?(?:\\b|[a-z]))(' . + implode('|', $upperWordList) . + ')$/'; } - if (preg_match('/(.*?(?:\\b|_))(' . static::$_cache['irregular']['singular'] . ')$/i', $word, $regs)) { + if ( + preg_match(static::$_cache['irregular']['singular'], $word, $regs) || + preg_match(static::$_cache['irregular']['singularUpper'], $word, $regs) + ) { $suffix = array_search(strtolower($regs[2]), static::$_irregular, true); $suffix = $suffix ? substr($suffix, 1) : ''; static::$_cache['singularize'][$word] = $regs[1] . substr($regs[2], 0, 1) . $suffix; @@ -328,10 +344,10 @@ public static function singularize(string $word): string } if (!isset(static::$_cache['uninflected'])) { - static::$_cache['uninflected'] = '(?:' . implode('|', static::$_uninflected) . ')'; + static::$_cache['uninflected'] = '/^(' . implode('|', static::$_uninflected) . ')$/i'; } - if (preg_match('/^(' . static::$_cache['uninflected'] . ')$/i', $word, $regs)) { + if (preg_match(static::$_cache['uninflected'], $word, $regs)) { static::$_cache['pluralize'][$word] = $word; return $word; diff --git a/app/vendor/cakephp/cakephp/src/Utility/LICENSE.txt b/app/vendor/cakephp/cakephp/src/Utility/LICENSE.txt index 0b3b94303..b938c9e8e 100644 --- a/app/vendor/cakephp/cakephp/src/Utility/LICENSE.txt +++ b/app/vendor/cakephp/cakephp/src/Utility/LICENSE.txt @@ -1,7 +1,7 @@ The MIT License (MIT) CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) -Copyright (c) 2005-2019, Cake Software Foundation, Inc. (https://cakefoundation.org) +Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/vendor/cakephp/cakephp/src/Utility/Security.php b/app/vendor/cakephp/cakephp/src/Utility/Security.php index f5238bc9b..5fcdc4fcd 100644 --- a/app/vendor/cakephp/cakephp/src/Utility/Security.php +++ b/app/vendor/cakephp/cakephp/src/Utility/Security.php @@ -43,7 +43,7 @@ class Security /** * The crypto implementation to use. * - * @var object + * @var object|null */ protected static $_instance; diff --git a/app/vendor/cakephp/cakephp/src/Utility/Text.php b/app/vendor/cakephp/cakephp/src/Utility/Text.php index c7a9376af..6b1e25232 100644 --- a/app/vendor/cakephp/cakephp/src/Utility/Text.php +++ b/app/vendor/cakephp/cakephp/src/Utility/Text.php @@ -16,7 +16,7 @@ */ namespace Cake\Utility; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use InvalidArgumentException; use Transliterator; @@ -198,22 +198,16 @@ public static function insert(string $str, array $data, array $options = []): st 'before' => ':', 'after' => '', 'escape' => '\\', 'format' => null, 'clean' => false, ]; $options += $defaults; - $format = $options['format']; - $data = $data; if (empty($data)) { return $options['clean'] ? static::cleanInsert($str, $options) : $str; } - if (!isset($format)) { - $format = sprintf( - '/(? $tempData */ $tempData = array_combine($dataKeys, $hashKeys); krsort($tempData); @@ -237,7 +241,7 @@ public static function insert(string $str, array $data, array $options = []): st /** @var array $dataReplacements */ $dataReplacements = array_combine($hashKeys, array_values($data)); foreach ($dataReplacements as $tmpHash => $tmpValue) { - $tmpValue = is_array($tmpValue) ? '' : $tmpValue; + $tmpValue = is_array($tmpValue) ? '' : (string)$tmpValue; $str = str_replace($tmpHash, $tmpValue, $str); } @@ -622,8 +626,12 @@ public static function truncate(string $text, int $length = 100, array $options } if ($truncate === '') { - // phpcs:ignore Generic.Files.LineLength - if (!preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/i', $tag[2])) { + if ( + !preg_match( + '/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/i', + $tag[2] + ) + ) { if (preg_match('/<[\w]+[^>]*>/', $tag[0])) { array_unshift($openTags, $tag[2]); } elseif (preg_match('/<\/([\w]+)[^>]*>/', $tag[0], $closeTag)) { @@ -1092,7 +1100,7 @@ public static function setTransliteratorId(string $transliteratorId): void { $transliterator = transliterator_create($transliteratorId); if ($transliterator === null) { - throw new Exception('Unable to create transliterator for id: ' . $transliteratorId); + throw new CakeException('Unable to create transliterator for id: ' . $transliteratorId); } static::setTransliterator($transliterator); @@ -1118,7 +1126,7 @@ public static function transliterate(string $string, $transliterator = null): st $return = transliterator_transliterate($transliterator, $string); if ($return === false) { - throw new Exception(sprintf('Unable to transliterate string: %s', $string)); + throw new CakeException(sprintf('Unable to transliterate string: %s', $string)); } return $return; diff --git a/app/vendor/cakephp/cakephp/src/Utility/Xml.php b/app/vendor/cakephp/cakephp/src/Utility/Xml.php index 17fee8d3b..a888d825a 100644 --- a/app/vendor/cakephp/cakephp/src/Utility/Xml.php +++ b/app/vendor/cakephp/cakephp/src/Utility/Xml.php @@ -17,6 +17,7 @@ namespace Cake\Utility; use Cake\Utility\Exception\XmlException; +use Closure; use DOMDocument; use DOMNode; use DOMText; @@ -141,32 +142,21 @@ public static function build($input, array $options = []) */ protected static function _loadXml(string $input, array $options) { - $internalErrors = libxml_use_internal_errors(true); - if (!$options['loadEntities']) { - libxml_disable_entity_loader(true); - } - $flags = 0; - if (!empty($options['parseHuge'])) { - $flags |= LIBXML_PARSEHUGE; - } - try { - if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') { - $flags |= LIBXML_NOCDATA; - $xml = new SimpleXMLElement($input, $flags); - } else { - $xml = new DOMDocument(); - $xml->loadXML($input, $flags); - } + return static::load( + $input, + $options, + function ($input, $options, $flags) { + if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') { + $flags |= LIBXML_NOCDATA; + $xml = new SimpleXMLElement($input, $flags); + } else { + $xml = new DOMDocument(); + $xml->loadXML($input, $flags); + } - return $xml; - } catch (Exception $e) { - throw new XmlException('Xml cannot be read. ' . $e->getMessage(), null, $e); - } finally { - if (!$options['loadEntities']) { - libxml_disable_entity_loader(false); + return $xml; } - libxml_use_internal_errors($internalErrors); - } + ); } /** @@ -185,28 +175,52 @@ public static function loadHtml(string $input, array $options = []) ]; $options += $defaults; - $internalErrors = libxml_use_internal_errors(true); - if (!$options['loadEntities']) { - libxml_disable_entity_loader(true); - } + return static::load( + $input, + $options, + function ($input, $options, $flags) { + $xml = new DOMDocument(); + $xml->loadHTML($input, $flags); + + if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') { + $xml = simplexml_import_dom($xml); + } + + return $xml; + } + ); + } + + /** + * Parse the input data and create either a SimpleXmlElement object or a DOMDocument. + * + * @param string $input The input to load. + * @param array $options The options to use. See Xml::build() + * @param \Closure $callable Closure that should return SimpleXMLElement or DOMDocument instance. + * @return \SimpleXMLElement|\DOMDocument + * @throws \Cake\Utility\Exception\XmlException + */ + protected static function load(string $input, array $options, Closure $callable) + { $flags = 0; if (!empty($options['parseHuge'])) { $flags |= LIBXML_PARSEHUGE; } - try { - $xml = new DOMDocument(); - $xml->loadHTML($input, $flags); - if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') { - $xml = simplexml_import_dom($xml); - } + $internalErrors = libxml_use_internal_errors(true); + if (LIBXML_VERSION < 20900 && !$options['loadEntities']) { + $previousDisabledEntityLoader = libxml_disable_entity_loader(true); + } elseif ($options['loadEntities']) { + $flags |= LIBXML_NOENT; + } - return $xml; + try { + return $callable($input, $options, $flags); } catch (Exception $e) { throw new XmlException('Xml cannot be read. ' . $e->getMessage(), null, $e); } finally { - if (!$options['loadEntities']) { - libxml_disable_entity_loader(false); + if (isset($previousDisabledEntityLoader)) { + libxml_disable_entity_loader($previousDisabledEntityLoader); } libxml_use_internal_errors($internalErrors); } @@ -217,7 +231,7 @@ public static function loadHtml(string $input, array $options = []) * * ### Options * - * - `format` If create childs ('tags') or attributes ('attributes'). + * - `format` If create children ('tags') or attributes ('attributes'). * - `pretty` Returns formatted Xml when set to `true`. Defaults to `false` * - `version` Version of XML document. Default is 1.0. * - `encoding` Encoding of XML document. If null remove from XML header. @@ -255,7 +269,7 @@ public static function loadHtml(string $input, array $options = []) public static function fromArray($input, array $options = []) { if (is_object($input) && method_exists($input, 'toArray') && is_callable([$input, 'toArray'])) { - $input = call_user_func([$input, 'toArray']); + $input = $input->toArray(); } if (!is_array($input) || count($input) !== 1) { throw new XmlException('Invalid input.'); @@ -289,7 +303,7 @@ public static function fromArray($input, array $options = []) } /** - * Recursive method to create childs from array + * Recursive method to create children from array * * @param \DOMDocument $dom Handler to DOMDocument * @param \DOMDocument|\DOMElement $node Handler to DOMElement (child) @@ -306,7 +320,7 @@ protected static function _fromArray(DOMDocument $dom, $node, &$data, $format): foreach ($data as $key => $value) { if (is_string($key)) { if (is_object($value) && method_exists($value, 'toArray') && is_callable([$value, 'toArray'])) { - $value = call_user_func([$value, 'toArray']); + $value = $value->toArray(); } if (!is_array($value)) { @@ -363,9 +377,9 @@ protected static function _fromArray(DOMDocument $dom, $node, &$data, $format): } /** - * Helper to _fromArray(). It will create childs of arrays + * Helper to _fromArray(). It will create children of arrays * - * @param array $data Array with information to create childs + * @param array $data Array with information to create children * @return void */ protected static function _createChild(array $data): void @@ -386,7 +400,7 @@ protected static function _createChild(array $data): void $childNS = $childValue = null; if (is_object($value) && method_exists($value, 'toArray') && is_callable([$value, 'toArray'])) { - $value = call_user_func([$value, 'toArray']); + $value = $value->toArray(); } if (is_array($value)) { if (isset($value['@'])) { @@ -458,6 +472,7 @@ protected static function _toArray(SimpleXMLElement $xml, array &$parentData, st } foreach ($xml->children($namespace, true) as $child) { + /** @psalm-suppress PossiblyNullArgument */ static::_toArray($child, $data, $namespace, $namespaces); } } diff --git a/app/vendor/cakephp/cakephp/src/Validation/LICENSE.txt b/app/vendor/cakephp/cakephp/src/Validation/LICENSE.txt index 0b3b94303..b938c9e8e 100644 --- a/app/vendor/cakephp/cakephp/src/Validation/LICENSE.txt +++ b/app/vendor/cakephp/cakephp/src/Validation/LICENSE.txt @@ -1,7 +1,7 @@ The MIT License (MIT) CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) -Copyright (c) 2005-2019, Cake Software Foundation, Inc. (https://cakefoundation.org) +Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/vendor/cakephp/cakephp/src/Validation/README.md b/app/vendor/cakephp/cakephp/src/Validation/README.md index 228e0a495..d3484fac3 100644 --- a/app/vendor/cakephp/cakephp/src/Validation/README.md +++ b/app/vendor/cakephp/cakephp/src/Validation/README.md @@ -22,9 +22,9 @@ $validator 'message' => 'E-mail must be valid' ]) ->requirePresence('name') - ->notEmpty('name', 'We need your name.') + ->notEmptyString('name', 'We need your name.') ->requirePresence('comment') - ->notEmpty('comment', 'You need to give a comment.'); + ->notEmptyString('comment', 'You need to give a comment.'); $errors = $validator->validate($_POST); if (!empty($errors)) { diff --git a/app/vendor/cakephp/cakephp/src/Validation/RulesProvider.php b/app/vendor/cakephp/cakephp/src/Validation/RulesProvider.php index f5d8c3d53..843f03255 100644 --- a/app/vendor/cakephp/cakephp/src/Validation/RulesProvider.php +++ b/app/vendor/cakephp/cakephp/src/Validation/RulesProvider.php @@ -21,6 +21,8 @@ /** * A Proxy class used to remove any extra arguments when the user intended to call * a method in another class that is not aware of validation providers signature + * + * @method bool extension(mixed $check, array $extensions, array $context = []) */ class RulesProvider { diff --git a/app/vendor/cakephp/cakephp/src/Validation/Validation.php b/app/vendor/cakephp/cakephp/src/Validation/Validation.php index d27299711..f5edb9781 100644 --- a/app/vendor/cakephp/cakephp/src/Validation/Validation.php +++ b/app/vendor/cakephp/cakephp/src/Validation/Validation.php @@ -96,6 +96,16 @@ class Validation */ public const COMPARE_LESS_OR_EQUAL = '<='; + /** + * @var string[] + */ + protected const COMPARE_STRING = [ + self::COMPARE_EQUAL, + self::COMPARE_NOT_EQUAL, + self::COMPARE_SAME, + self::COMPARE_NOT_SAME, + ]; + /** * Datetime ISO8601 format * @@ -315,19 +325,21 @@ public static function numElements($check, string $operator, int $expectedCount) * Used to compare 2 numeric values. * * @param string|int $check1 The left value to compare. - * @param string $operator Can be either a word or operand - * is greater >, is less <, greater or equal >= - * less or equal <=, is less <, equal to ==, not equal != + * @param string $operator Can be one of following operator strings: + * '>', '<', '>=', '<=', '==', '!=', '===' and '!=='. You can use one of + * the Validation::COMPARE_* constants. * @param string|int $check2 The right value to compare. * @return bool Success */ public static function comparison($check1, string $operator, $check2): bool { - if ((float)$check1 != $check1) { + if ( + (!is_numeric($check1) || !is_numeric($check2)) && + !in_array($operator, static::COMPARE_STRING) + ) { return false; } - $operator = str_replace([' ', "\t", "\n", "\r", "\0", "\x0B"], '', strtolower($operator)); switch ($operator) { case static::COMPARE_GREATER: if ($check1 > $check2) { @@ -370,7 +382,7 @@ public static function comparison($check1, string $operator, $check2): bool } break; default: - static::$errors[] = 'You must define the $operator parameter for Validation::comparison()'; + static::$errors[] = 'You must define a valid $operator parameter for Validation::comparison()'; } return false; @@ -398,7 +410,7 @@ public static function compareWith($check, string $field, array $context): bool * * @param mixed $check The value to find in $field. * @param string $field The field to check $check against. This field must be present in $context. - * @param string $operator Comparison operator. + * @param string $operator Comparison operator. See Validation::comparison(). * @param array $context The validation context. * @return bool * @since 3.6.0 @@ -424,6 +436,7 @@ public static function compareFields($check, string $field, string $operator, ar */ public static function containsNonAlphaNumeric($check, int $count = 1): bool { + deprecationWarning('Validation::containsNonAlphaNumeric() is deprecated. Use notAlphaNumeric() instead.'); if (!is_string($check)) { return false; } @@ -436,12 +449,15 @@ public static function containsNonAlphaNumeric($check, int $count = 1): bool /** * Used when a custom regular expression is needed. * - * @param string $check The value to check. + * @param mixed $check The value to check. * @param string|null $regex If $check is passed as a string, $regex must also be set to valid regular expression * @return bool Success */ - public static function custom(string $check, ?string $regex = null): bool + public static function custom($check, ?string $regex = null): bool { + if (!is_scalar($check)) { + return false; + } if ($regex === null) { static::$errors[] = 'You must define a regular expression for Validation::custom()'; @@ -505,7 +521,7 @@ public static function date($check, $format = 'ymd', ?string $regex = null): boo $fourDigitLeapYear = '(?:(?:(?:(?!0000)[012]\\d)(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))'; $regex['dmy'] = '%^(?:(?:31(\\/|-|\\.|\\x20)(?:0?[13578]|1[02]))\\1|(?:(?:29|30)' . - $separator . '(?:0?[1,3-9]|1[0-2])\\2))' . $year . '$|^(?:29' . + $separator . '(?:0?[13-9]|1[0-2])\\2))' . $year . '$|^(?:29' . $separator . '0?2\\3' . $leapYear . ')$|^(?:0?[1-9]|1\\d|2[0-8])' . $separator . '(?:(?:0?[1-9])|(?:1[0-2]))\\4' . $year . '$%'; @@ -515,7 +531,7 @@ public static function date($check, $format = 'ymd', ?string $regex = null): boo $regex['ymd'] = '%^(?:(?:' . $leapYear . $separator . '(?:0?2\\1(?:29)))|(?:' . $year . - $separator . '(?:(?:(?:0?[13578]|1[02])\\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\\2(?:0?[1-9]|1\\d|2[0-8]))))$%'; + $separator . '(?:(?:(?:0?[13578]|1[02])\\2(?:31))|(?:(?:0?[13-9]|1[0-2])\\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\\2(?:0?[1-9]|1\\d|2[0-8]))))$%'; $regex['dMy'] = '/^((31(?!\\ (Feb(ruary)?|Apr(il)?|June?|(Sep(?=\\b|t)t?|Nov)(ember)?)))|((30|29)(?!\\ Feb(ruary)?))|(29(?=\\ Feb(ruary)?\\ ' . $fourDigitLeapYear . '))|(0?[1-9])|1\\d|2[0-8])\\ (Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)\\ ' . $fourDigitYear . '$/'; @@ -635,7 +651,7 @@ public static function time($check): bool } $meridianClockRegex = '^((0?[1-9]|1[012])(:[0-5]\d){0,2} ?([AP]M|[ap]m))$'; - $standardClockRegex = '^([01]\d|2[0-3])((:[0-5]\d){0,2}|(:[0-5]\d){2}\.\d{0,6})$'; + $standardClockRegex = '^([01]\d|2[0-3])((:[0-5]\d){1,2}|(:[0-5]\d){2}\.\d{0,6})$'; return static::_check($check, '%' . $meridianClockRegex . '|' . $standardClockRegex . '%'); } @@ -658,7 +674,7 @@ public static function localizedTime($check, string $type = 'datetime', $format if ($check instanceof DateTimeInterface) { return true; } - if (is_object($check)) { + if (!is_string($check)) { return false; } static $methods = [ @@ -1085,7 +1101,7 @@ public static function range($check, ?float $lower = null, ?float $upper = null) * The regex checks for the following component parts: * * - a valid, optional, scheme - * - a valid ip address OR + * - a valid IP address OR * a valid domain name as defined by section 2.3.1 of https://www.ietf.org/rfc/rfc1035.txt * with an optional port number * - an optional valid path @@ -1226,15 +1242,13 @@ public static function mimeType($check, $mimeTypes = []): bool throw new RuntimeException('Cannot validate mimetype for a missing file'); } - $finfo = finfo_open(FILEINFO_MIME); - $finfo = finfo_file($finfo, $file); + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $mime = finfo_file($finfo, $file); - if (!$finfo) { + if (!$mime) { throw new RuntimeException('Can not determine the mimetype.'); } - [$mime] = explode(';', $finfo); - if (is_string($mimeTypes)) { return self::_check($mime, $mimeTypes); } @@ -1250,7 +1264,7 @@ public static function mimeType($check, $mimeTypes = []): bool * Helper for reading the file out of the various file implementations * we accept. * - * @param string|array|\Psr\Http\Message\UploadedFileInterface $check The data to read a filename out of. + * @param mixed $check The data to read a filename out of. * @return string|false Either the filename or false on failure. */ protected static function getFilename($check) @@ -1448,12 +1462,12 @@ public static function imageSize($file, array $options): bool /** * Validates the image width. * - * @param array $file The uploaded file data from PHP. + * @param mixed $file The uploaded file data from PHP. * @param string $operator Comparison operator. * @param int $width Min or max width. * @return bool */ - public static function imageWidth(array $file, string $operator, int $width): bool + public static function imageWidth($file, string $operator, int $width): bool { return self::imageSize($file, [ 'width' => [ @@ -1464,14 +1478,14 @@ public static function imageWidth(array $file, string $operator, int $width): bo } /** - * Validates the image width. + * Validates the image height. * - * @param array $file The uploaded file data from PHP. + * @param mixed $file The uploaded file data from PHP. * @param string $operator Comparison operator. - * @param int $height Min or max width. + * @param int $height Min or max height. * @return bool */ - public static function imageHeight(array $file, string $operator, int $height): bool + public static function imageHeight($file, string $operator, int $height): bool { return self::imageSize($file, [ 'height' => [ diff --git a/app/vendor/cakephp/cakephp/src/Validation/ValidationRule.php b/app/vendor/cakephp/cakephp/src/Validation/ValidationRule.php index c98c6b2b8..087c0fd2d 100644 --- a/app/vendor/cakephp/cakephp/src/Validation/ValidationRule.php +++ b/app/vendor/cakephp/cakephp/src/Validation/ValidationRule.php @@ -89,7 +89,7 @@ public function __construct(array $validator = []) */ public function isLast(): bool { - return (bool)$this->_last; + return $this->_last; } /** diff --git a/app/vendor/cakephp/cakephp/src/Validation/Validator.php b/app/vendor/cakephp/cakephp/src/Validation/Validator.php index 097e0129e..c08021f5d 100644 --- a/app/vendor/cakephp/cakephp/src/Validation/Validator.php +++ b/app/vendor/cakephp/cakephp/src/Validation/Validator.php @@ -180,6 +180,13 @@ class Validator implements ArrayAccess, IteratorAggregate, Countable */ protected $_allowEmptyFlags = []; + /** + * Whether to apply last flag to generated rule(s). + * + * @var bool + */ + protected $_stopOnFailure = false; + /** * Constructor */ @@ -189,6 +196,22 @@ public function __construct() $this->_providers = self::$_defaultProviders; } + /** + * Whether to stop validation rule evaluation on the first failed rule. + * + * When enabled the first failing rule per field will cause validation to stop. + * When disabled all rules will be run even if there are failures. + * + * @param bool $stopOnFailure If to apply last flag. + * @return $this + */ + public function setStopOnFailure(bool $stopOnFailure = true) + { + $this->_stopOnFailure = $stopOnFailure; + + return $this; + } + /** * Validates and returns an array of failed fields and their error messages. * @@ -479,7 +502,10 @@ public function add(string $field, $name, $rule = []) foreach ($rules as $name => $rule) { if (is_array($rule)) { - $rule += ['rule' => $name]; + $rule += [ + 'rule' => $name, + 'last' => $this->_stopOnFailure, + ]; } if (!is_string($name)) { /** @psalm-suppress PossiblyUndefinedMethod */ @@ -1691,6 +1717,7 @@ public function lessThanOrEqualToField(string $field, string $secondField, ?stri */ public function containsNonAlphaNumeric(string $field, int $limit = 1, ?string $message = null, $when = null) { + deprecationWarning('Validator::containsNonAlphaNumeric() is deprecated. Use notAlphaNumeric() instead.'); $extra = array_filter(['on' => $when, 'message' => $message]); return $this->add($field, 'containsNonAlphaNumeric', $extra + [ @@ -2099,7 +2126,7 @@ public function urlWithProtocol(string $field, ?string $message = null, $when = } /** - * Add a validation rule to ensure the field value is within a whitelist. + * Add a validation rule to ensure the field value is within an allowed list. * * @param string $field The field you want to apply the rule to. * @param array $list The list of valid options. @@ -2715,6 +2742,7 @@ public function __debugInfo(): array '_allowEmptyMessages' => $this->_allowEmptyMessages, '_allowEmptyFlags' => $this->_allowEmptyFlags, '_useI18n' => $this->_useI18n, + '_stopOnFailure' => $this->_stopOnFailure, '_providers' => array_keys($this->_providers), '_fields' => $fields, ]; diff --git a/app/vendor/cakephp/cakephp/src/Validation/ValidatorAwareTrait.php b/app/vendor/cakephp/cakephp/src/Validation/ValidatorAwareTrait.php index 3327914b9..5d1c68c61 100644 --- a/app/vendor/cakephp/cakephp/src/Validation/ValidatorAwareTrait.php +++ b/app/vendor/cakephp/cakephp/src/Validation/ValidatorAwareTrait.php @@ -68,11 +68,12 @@ trait ValidatorAwareTrait * ``` * public function validationForSubscription($validator) * { - * return $validator - * ->add('email', 'valid-email', ['rule' => 'email']) - * ->add('password', 'valid', ['rule' => 'notBlank']) - * ->requirePresence('username'); + * return $validator + * ->add('email', 'valid-email', ['rule' => 'email']) + * ->add('password', 'valid', ['rule' => 'notBlank']) + * ->requirePresence('username'); * } + * * $validator = $this->getValidator('forSubscription'); * ``` * @@ -143,11 +144,11 @@ protected function createValidator(string $name): Validator * You can build the object by yourself and store it in your object: * * ``` - * $validator = new \Cake\Validation\Validator($table); + * $validator = new \Cake\Validation\Validator(); * $validator - * ->add('email', 'valid-email', ['rule' => 'email']) - * ->add('password', 'valid', ['rule' => 'notBlank']) - * ->allowEmpty('bio'); + * ->add('email', 'valid-email', ['rule' => 'email']) + * ->add('password', 'valid', ['rule' => 'notBlank']) + * ->allowEmpty('bio'); * $this->setValidator('forSubscription', $validator); * ``` * diff --git a/app/vendor/cakephp/cakephp/src/View/AjaxView.php b/app/vendor/cakephp/cakephp/src/View/AjaxView.php index 94b08a0e1..f0fa4ae49 100644 --- a/app/vendor/cakephp/cakephp/src/View/AjaxView.php +++ b/app/vendor/cakephp/cakephp/src/View/AjaxView.php @@ -16,10 +16,6 @@ */ namespace Cake\View; -use Cake\Event\EventManager; -use Cake\Http\Response; -use Cake\Http\ServerRequest; - /** * A view class that is used for AJAX responses. * Currently only switches the default layout and sets the response type - which just maps to @@ -33,23 +29,11 @@ class AjaxView extends View protected $layout = 'ajax'; /** - * Constructor - * - * @param \Cake\Http\ServerRequest|null $request The request object. - * @param \Cake\Http\Response|null $response The response object. - * @param \Cake\Event\EventManager|null $eventManager Event manager object. - * @param array $viewOptions View options. + * @inheritDoc */ - public function __construct( - ?ServerRequest $request = null, - ?Response $response = null, - ?EventManager $eventManager = null, - array $viewOptions = [] - ) { - if ($response) { - $response = $response->withType('ajax'); - } - - parent::__construct($request, $response, $eventManager, $viewOptions); + public function initialize(): void + { + parent::initialize(); + $this->setResponse($this->getResponse()->withType('ajax')); } } diff --git a/app/vendor/cakephp/cakephp/src/View/Cell.php b/app/vendor/cakephp/cakephp/src/View/Cell.php index 1df99d70b..774854c01 100644 --- a/app/vendor/cakephp/cakephp/src/View/Cell.php +++ b/app/vendor/cakephp/cakephp/src/View/Cell.php @@ -90,7 +90,7 @@ abstract class Cell implements EventDispatcherInterface /** * List of valid options (constructor's fourth arguments) - * Override this property in subclasses to whitelist + * Override this property in subclasses to allow * which options you want set as properties in your Cell. * * @var array diff --git a/app/vendor/cakephp/cakephp/src/View/Exception/MissingCellException.php b/app/vendor/cakephp/cakephp/src/View/Exception/MissingCellException.php index e28cab8ae..3f473c831 100644 --- a/app/vendor/cakephp/cakephp/src/View/Exception/MissingCellException.php +++ b/app/vendor/cakephp/cakephp/src/View/Exception/MissingCellException.php @@ -16,12 +16,12 @@ */ namespace Cake\View\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Used when a cell class file cannot be found. */ -class MissingCellException extends Exception +class MissingCellException extends CakeException { /** * @inheritDoc diff --git a/app/vendor/cakephp/cakephp/src/View/Exception/MissingHelperException.php b/app/vendor/cakephp/cakephp/src/View/Exception/MissingHelperException.php index 775fb82bd..76d6c5685 100644 --- a/app/vendor/cakephp/cakephp/src/View/Exception/MissingHelperException.php +++ b/app/vendor/cakephp/cakephp/src/View/Exception/MissingHelperException.php @@ -14,12 +14,12 @@ */ namespace Cake\View\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Used when a helper cannot be found. */ -class MissingHelperException extends Exception +class MissingHelperException extends CakeException { /** * @inheritDoc diff --git a/app/vendor/cakephp/cakephp/src/View/Exception/MissingTemplateException.php b/app/vendor/cakephp/cakephp/src/View/Exception/MissingTemplateException.php index f4f92e043..d5fcfff8d 100644 --- a/app/vendor/cakephp/cakephp/src/View/Exception/MissingTemplateException.php +++ b/app/vendor/cakephp/cakephp/src/View/Exception/MissingTemplateException.php @@ -14,14 +14,19 @@ */ namespace Cake\View\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Throwable; /** * Used when a template file cannot be found. */ -class MissingTemplateException extends Exception +class MissingTemplateException extends CakeException { + /** + * @var string|null + */ + protected $templateName; + /** * @var string */ @@ -47,7 +52,13 @@ class MissingTemplateException extends Exception */ public function __construct($file, array $paths = [], ?int $code = null, ?Throwable $previous = null) { - $this->file = is_array($file) ? array_pop($file) : $file; + if (is_array($file)) { + $this->file = array_pop($file); + $this->templateName = array_pop($file); + } else { + $this->file = $file; + $this->templateName = null; + } $this->paths = $paths; parent::__construct($this->formatMessage(), $code, $previous); @@ -60,7 +71,8 @@ public function __construct($file, array $paths = [], ?int $code = null, ?Throwa */ public function formatMessage(): string { - $message = "{$this->type} file `{$this->file}` could not be found."; + $name = $this->templateName ?? $this->file; + $message = "{$this->type} file `{$name}` could not be found."; if ($this->paths) { $message .= "\n\nThe following paths were searched:\n\n"; foreach ($this->paths as $path) { diff --git a/app/vendor/cakephp/cakephp/src/View/Exception/MissingViewException.php b/app/vendor/cakephp/cakephp/src/View/Exception/MissingViewException.php index 69b1515ae..da0a3774d 100644 --- a/app/vendor/cakephp/cakephp/src/View/Exception/MissingViewException.php +++ b/app/vendor/cakephp/cakephp/src/View/Exception/MissingViewException.php @@ -16,12 +16,12 @@ */ namespace Cake\View\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Used when a view class file cannot be found. */ -class MissingViewException extends Exception +class MissingViewException extends CakeException { /** * @inheritDoc diff --git a/app/vendor/cakephp/cakephp/src/View/Exception/SerializationFailureException.php b/app/vendor/cakephp/cakephp/src/View/Exception/SerializationFailureException.php index c516157fd..3ce691e61 100644 --- a/app/vendor/cakephp/cakephp/src/View/Exception/SerializationFailureException.php +++ b/app/vendor/cakephp/cakephp/src/View/Exception/SerializationFailureException.php @@ -16,11 +16,11 @@ */ namespace Cake\View\Exception; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * Used when a SerializedView class fails to serialize data. */ -class SerializationFailureException extends Exception +class SerializationFailureException extends CakeException { } diff --git a/app/vendor/cakephp/cakephp/src/View/Form/ArrayContext.php b/app/vendor/cakephp/cakephp/src/View/Form/ArrayContext.php index 263d17da7..90a40072e 100644 --- a/app/vendor/cakephp/cakephp/src/View/Form/ArrayContext.php +++ b/app/vendor/cakephp/cakephp/src/View/Form/ArrayContext.php @@ -16,7 +16,6 @@ */ namespace Cake\View\Form; -use Cake\Http\ServerRequest; use Cake\Utility\Hash; /** @@ -27,8 +26,9 @@ * * Important keys: * + * - `data` Holds the current values supplied for the fields. * - `defaults` The default values for fields. These values - * will be used when there is no request data set. Data should be nested following + * will be used when there is no data set. Data should be nested following * the dot separated paths you access your fields with. * - `required` A nested array of fields, relationships and boolean * flags to indicate a field is required. The value can also be a string to be used @@ -45,7 +45,11 @@ * ### Example * * ``` - * $data = [ + * $article = [ + * 'data' => [ + * 'id' => '1', + * 'title' => 'First post!', + * ], * 'schema' => [ * 'id' => ['type' => 'integer'], * 'title' => ['type' => 'string', 'length' => 255], @@ -54,8 +58,7 @@ * ] * ], * 'defaults' => [ - * 'id' => 1, - * 'title' => 'First post!', + * 'title' => 'Default title', * ], * 'required' => [ * 'id' => true, // will use default required message @@ -67,13 +70,6 @@ */ class ArrayContext implements ContextInterface { - /** - * The request object. - * - * @var \Cake\Http\ServerRequest - */ - protected $_request; - /** * Context data for this object. * @@ -84,13 +80,12 @@ class ArrayContext implements ContextInterface /** * Constructor. * - * @param \Cake\Http\ServerRequest $request The request object. * @param array $context Context info. */ - public function __construct(ServerRequest $request, array $context) + public function __construct(array $context) { - $this->_request = $request; $context += [ + 'data' => [], 'schema' => [], 'required' => [], 'defaults' => [], @@ -107,6 +102,8 @@ public function __construct(ServerRequest $request, array $context) */ public function primaryKey(): array { + deprecationWarning('`ArrayContext::primaryKey()` is deprecated. Use `ArrayContext::getPrimaryKey()`.'); + return $this->getPrimaryKey(); } @@ -166,17 +163,16 @@ public function isCreate(): bool /** * Get the current value for a given field. * - * This method will coalesce the current request data and the 'defaults' - * array. + * This method will coalesce the current data and the 'defaults' array. * * @param string $field A dot separated path to the field a value * is needed for. * @param array $options Options: * - * - `default`: Default value to return if no value found in request - * data or context record. + * - `default`: Default value to return if no value found in data or + * context record. * - `schemaDefault`: Boolean indicating whether default value from - * context's schema should be used if it's not explicitly provided. + * context's schema should be used if it's not explicitly provided. * @return mixed */ public function val(string $field, array $options = []) @@ -186,10 +182,10 @@ public function val(string $field, array $options = []) 'schemaDefault' => true, ]; - $val = $this->_request->getData($field); - if ($val !== null) { - return $val; + if (Hash::check($this->_context['data'], $field)) { + return Hash::get($this->_context['data'], $field); } + if ($options['default'] !== null || !$options['schemaDefault']) { return $options['default']; } @@ -320,9 +316,11 @@ public function attributes(string $field): array if ($schema === null) { $schema = Hash::get($this->_context['schema'], $this->stripNesting($field)); } - $whitelist = ['length' => null, 'precision' => null]; - return array_intersect_key((array)$schema, $whitelist); + return array_intersect_key( + (array)$schema, + array_flip(static::VALID_ATTRIBUTES) + ); } /** diff --git a/app/vendor/cakephp/cakephp/src/View/Form/ContextFactory.php b/app/vendor/cakephp/cakephp/src/View/Form/ContextFactory.php index b0e47e45b..490f2aee1 100644 --- a/app/vendor/cakephp/cakephp/src/View/Form/ContextFactory.php +++ b/app/vendor/cakephp/cakephp/src/View/Form/ContextFactory.php @@ -61,17 +61,17 @@ public static function createWithDefaults(array $providers = []) 'type' => 'orm', 'callable' => function ($request, $data) { if ($data['entity'] instanceof EntityInterface) { - return new EntityContext($request, $data); + return new EntityContext($data); } if (isset($data['table'])) { - return new EntityContext($request, $data); + return new EntityContext($data); } if (is_iterable($data['entity'])) { $pass = (new Collection($data['entity']))->first() !== null; if ($pass) { - return new EntityContext($request, $data); + return new EntityContext($data); } else { - return new NullContext($request, $data); + return new NullContext($data); } } }, @@ -80,7 +80,7 @@ public static function createWithDefaults(array $providers = []) 'type' => 'form', 'callable' => function ($request, $data) { if ($data['entity'] instanceof Form) { - return new FormContext($request, $data); + return new FormContext($data); } }, ], @@ -88,7 +88,7 @@ public static function createWithDefaults(array $providers = []) 'type' => 'array', 'callable' => function ($request, $data) { if (is_array($data['entity']) && isset($data['entity']['schema'])) { - return new ArrayContext($request, $data['entity']); + return new ArrayContext($data['entity']); } }, ], @@ -96,7 +96,7 @@ public static function createWithDefaults(array $providers = []) 'type' => 'null', 'callable' => function ($request, $data) { if ($data['entity'] === null) { - return new NullContext($request, $data); + return new NullContext($data); } }, ], @@ -136,7 +136,7 @@ public function addProvider(string $type, callable $check) * @param \Cake\Http\ServerRequest $request Request instance. * @param array $data The data to get a context provider for. * @return \Cake\View\Form\ContextInterface Context provider. - * @throws \RuntimeException When a context instace cannot be generated for given entity. + * @throws \RuntimeException When a context instance cannot be generated for given entity. */ public function get(ServerRequest $request, array $data = []): ContextInterface { diff --git a/app/vendor/cakephp/cakephp/src/View/Form/ContextInterface.php b/app/vendor/cakephp/cakephp/src/View/Form/ContextInterface.php index 314891ff2..0984c9d35 100644 --- a/app/vendor/cakephp/cakephp/src/View/Form/ContextInterface.php +++ b/app/vendor/cakephp/cakephp/src/View/Form/ContextInterface.php @@ -21,6 +21,11 @@ */ interface ContextInterface { + /** + * @var string[] + */ + public const VALID_ATTRIBUTES = ['length', 'precision', 'comment', 'null', 'default']; + /** * Get the fields used in the context as a primary key. * @@ -50,10 +55,10 @@ public function isCreate(): bool; * Classes implementing this method can optionally have a second argument * `$options`. Valid key for `$options` array are: * - * - `default`: Default value to return if no value found in request - * data or context record. + * - `default`: Default value to return if no value found in data or + * context record. * - `schemaDefault`: Boolean indicating whether default value from - * context's schema should be used if it's not explicitly provided. + * context's schema should be used if it's not explicitly provided. * * @param string $field A dot separated path to the field a value * @param array $options Options. diff --git a/app/vendor/cakephp/cakephp/src/View/Form/EntityContext.php b/app/vendor/cakephp/cakephp/src/View/Form/EntityContext.php index 5c072f084..4184c2d85 100644 --- a/app/vendor/cakephp/cakephp/src/View/Form/EntityContext.php +++ b/app/vendor/cakephp/cakephp/src/View/Form/EntityContext.php @@ -19,7 +19,7 @@ use ArrayAccess; use Cake\Collection\Collection; use Cake\Datasource\EntityInterface; -use Cake\Http\ServerRequest; +use Cake\Datasource\InvalidPropertyInterface; use Cake\ORM\Entity; use Cake\ORM\Locator\LocatorAwareTrait; use Cake\ORM\Table; @@ -52,13 +52,6 @@ class EntityContext implements ContextInterface { use LocatorAwareTrait; - /** - * The request object. - * - * @var \Cake\Http\ServerRequest - */ - protected $_request; - /** * Context data for this object. * @@ -98,12 +91,10 @@ class EntityContext implements ContextInterface /** * Constructor. * - * @param \Cake\Http\ServerRequest $request The request object. * @param array $context Context info. */ - public function __construct(ServerRequest $request, array $context) + public function __construct(array $context) { - $this->_request = $request; $context += [ 'entity' => null, 'table' => null, @@ -180,6 +171,8 @@ protected function _prepare(): void */ public function primaryKey(): array { + deprecationWarning('`EntityContext::primaryKey()` is deprecated. Use `EntityContext::getPrimaryKey()`.'); + return (array)$this->_tables[$this->_rootName]->getPrimaryKey(); } @@ -245,8 +238,8 @@ public function isCreate(): bool * @param string $field The dot separated path to the value. * @param array $options Options: * - * - `default`: Default value to return if no value found in request - * data or entity. + * - `default`: Default value to return if no value found in data or + * entity. * - `schemaDefault`: Boolean indicating whether default value from table * schema should be used if it's not explicitly provided. * @return mixed The value of the field or null on a miss. @@ -258,10 +251,6 @@ public function val(string $field, array $options = []) 'schemaDefault' => true, ]; - $val = $this->_request->getData($field); - if ($val !== null) { - return $val; - } if (empty($this->_context['entity'])) { return $options['default']; } @@ -274,6 +263,14 @@ public function val(string $field, array $options = []) if ($entity instanceof EntityInterface) { $part = end($parts); + + if ($entity instanceof InvalidPropertyInterface) { + $val = $entity->getInvalidField($part); + if ($val !== null) { + return $val; + } + } + $val = $entity->get($part); if ($val !== null) { return $val; @@ -632,7 +629,7 @@ protected function _getValidator(array $parts): Validator * * @param string[]|string|\Cake\Datasource\EntityInterface $parts Each one of the parts in a path for a field name * @param bool $fallback Whether or not to fallback to the last found table - * when a non-existent field/property is being encountered. + * when a nonexistent field/property is being encountered. * @return \Cake\ORM\Table|null Table instance or null */ protected function _getTable($parts, $fallback = true): ?Table @@ -714,10 +711,10 @@ public function attributes(string $field): array return []; } - $column = (array)$table->getSchema()->getColumn(array_pop($parts)); - $whitelist = ['length' => null, 'precision' => null]; - - return array_intersect_key($column, $whitelist); + return array_intersect_key( + (array)$table->getSchema()->getColumn(array_pop($parts)), + array_flip(static::VALID_ATTRIBUTES) + ); } /** diff --git a/app/vendor/cakephp/cakephp/src/View/Form/FormContext.php b/app/vendor/cakephp/cakephp/src/View/Form/FormContext.php index f3d5291c2..f0475ec69 100644 --- a/app/vendor/cakephp/cakephp/src/View/Form/FormContext.php +++ b/app/vendor/cakephp/cakephp/src/View/Form/FormContext.php @@ -16,24 +16,16 @@ */ namespace Cake\View\Form; -use Cake\Http\ServerRequest; use Cake\Utility\Hash; /** * Provides a context provider for Cake\Form\Form instances. * * This context provider simply fulfils the interface requirements - * that FormHelper has and allows access to the request data. + * that FormHelper has and allows access to the form data. */ class FormContext implements ContextInterface { - /** - * The request object. - * - * @var \Cake\Http\ServerRequest - */ - protected $_request; - /** * The form object. * @@ -44,12 +36,10 @@ class FormContext implements ContextInterface /** * Constructor. * - * @param \Cake\Http\ServerRequest $request The request object. * @param array $context Context info. */ - public function __construct(ServerRequest $request, array $context) + public function __construct(array $context) { - $this->_request = $request; $context += [ 'entity' => null, ]; @@ -64,6 +54,8 @@ public function __construct(ServerRequest $request, array $context) */ public function primaryKey(): array { + deprecationWarning('`FormContext::primaryKey()` is deprecated. Use `FormContext::getPrimaryKey()`.'); + return []; } @@ -101,11 +93,6 @@ public function val(string $field, array $options = []) 'schemaDefault' => true, ]; - $val = $this->_request->getData($field); - if ($val !== null) { - return $val; - } - $val = $this->_form->getData($field); if ($val !== null) { return $val; @@ -126,7 +113,7 @@ public function val(string $field, array $options = []) */ protected function _schemaDefault(string $field) { - $field = $this->_form->schema()->field($field); + $field = $this->_form->getSchema()->field($field); if ($field) { return $field['default']; } @@ -199,7 +186,7 @@ public function getMaxLength(string $field): ?int */ public function fieldNames(): array { - return $this->_form->schema()->fields(); + return $this->_form->getSchema()->fields(); } /** @@ -207,7 +194,7 @@ public function fieldNames(): array */ public function type(string $field): ?string { - return $this->_form->schema()->fieldType($field); + return $this->_form->getSchema()->fieldType($field); } /** @@ -215,10 +202,10 @@ public function type(string $field): ?string */ public function attributes(string $field): array { - $column = (array)$this->_form->schema()->field($field); - $whiteList = ['length' => null, 'precision' => null]; - - return array_intersect_key($column, $whiteList); + return array_intersect_key( + (array)$this->_form->getSchema()->field($field), + array_flip(static::VALID_ATTRIBUTES) + ); } /** diff --git a/app/vendor/cakephp/cakephp/src/View/Form/NullContext.php b/app/vendor/cakephp/cakephp/src/View/Form/NullContext.php index d78c1f3f3..5a0074684 100644 --- a/app/vendor/cakephp/cakephp/src/View/Form/NullContext.php +++ b/app/vendor/cakephp/cakephp/src/View/Form/NullContext.php @@ -16,32 +16,21 @@ */ namespace Cake\View\Form; -use Cake\Http\ServerRequest; - /** * Provides a context provider that does nothing. * * This context provider simply fulfils the interface requirements - * that FormHelper has and allows access to the request data. + * that FormHelper has. */ class NullContext implements ContextInterface { - /** - * The request object. - * - * @var \Cake\Http\ServerRequest - */ - protected $_request; - /** * Constructor. * - * @param \Cake\Http\ServerRequest $request The request object. * @param array $context Context info. */ - public function __construct(ServerRequest $request, array $context) + public function __construct(array $context) { - $this->_request = $request; } /** @@ -52,6 +41,8 @@ public function __construct(ServerRequest $request, array $context) */ public function primaryKey(): array { + deprecationWarning('`NullContext::primaryKey()` is deprecated. Use `NullContext::getPrimaryKey()`.'); + return []; } @@ -84,7 +75,7 @@ public function isCreate(): bool */ public function val(string $field, array $options = []) { - return $this->_request->getData($field); + return null; } /** diff --git a/app/vendor/cakephp/cakephp/src/View/Helper/BreadcrumbsHelper.php b/app/vendor/cakephp/cakephp/src/View/Helper/BreadcrumbsHelper.php index 93de6826f..11887ead5 100644 --- a/app/vendor/cakephp/cakephp/src/View/Helper/BreadcrumbsHelper.php +++ b/app/vendor/cakephp/cakephp/src/View/Helper/BreadcrumbsHelper.php @@ -34,7 +34,7 @@ class BreadcrumbsHelper extends Helper * * @var array */ - public $helpers = ['Url']; + protected $helpers = ['Url']; /** * Default config for the helper. diff --git a/app/vendor/cakephp/cakephp/src/View/Helper/FlashHelper.php b/app/vendor/cakephp/cakephp/src/View/Helper/FlashHelper.php index 9b7f96193..78e2f252e 100644 --- a/app/vendor/cakephp/cakephp/src/View/Helper/FlashHelper.php +++ b/app/vendor/cakephp/cakephp/src/View/Helper/FlashHelper.php @@ -17,7 +17,6 @@ namespace Cake\View\Helper; use Cake\View\Helper; -use UnexpectedValueException; /** * FlashHelper class to render flash messages. @@ -67,27 +66,16 @@ class FlashHelper extends Helper * Supports the 'params', and 'element' keys that are used in the helper. * @return string|null Rendered flash message or null if flash key does not exist * in session. - * @throws \UnexpectedValueException If value for flash settings key is not an array. */ public function render(string $key = 'flash', array $options = []): ?string { - $session = $this->_View->getRequest()->getSession(); - - if (!$session->check("Flash.$key")) { + $messages = $this->_View->getRequest()->getFlash()->consume($key); + if ($messages === null) { return null; } - $flash = $session->read("Flash.$key"); - if (!is_array($flash)) { - throw new UnexpectedValueException(sprintf( - 'Value for flash setting key "%s" must be an array.', - $key - )); - } - $session->delete("Flash.$key"); - $out = ''; - foreach ($flash as $message) { + foreach ($messages as $message) { $message = $options + $message; $out .= $this->_View->element($message['element'], $message); } diff --git a/app/vendor/cakephp/cakephp/src/View/Helper/FormHelper.php b/app/vendor/cakephp/cakephp/src/View/Helper/FormHelper.php index 8b3deb2f5..322e9a930 100644 --- a/app/vendor/cakephp/cakephp/src/View/Helper/FormHelper.php +++ b/app/vendor/cakephp/cakephp/src/View/Helper/FormHelper.php @@ -17,7 +17,7 @@ namespace Cake\View\Helper; use Cake\Core\Configure; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Form\FormProtector; use Cake\Routing\Router; use Cake\Utility\Hash; @@ -28,6 +28,7 @@ use Cake\View\StringTemplateTrait; use Cake\View\View; use Cake\View\Widget\WidgetLocator; +use InvalidArgumentException; use RuntimeException; /** @@ -54,7 +55,7 @@ class FormHelper extends Helper * * @var array */ - public $helpers = ['Url', 'Html']; + protected $helpers = ['Url', 'Html']; /** * Default config for the helper. @@ -206,7 +207,7 @@ class FormHelper extends Helper /** * Context factory. * - * @var \Cake\View\Form\ContextFactory + * @var \Cake\View\Form\ContextFactory|null */ protected $_contextFactory; @@ -219,11 +220,23 @@ class FormHelper extends Helper protected $_lastAction = ''; /** - * The sources to be used when retrieving prefilled input values. + * The supported sources that can be used to populate input values. + * + * `context` - Corresponds to `ContextInterface` instances. + * `data` - Corresponds to request data (POST/PUT). + * `query` - Corresponds to request's query string. * * @var string[] */ - protected $_valueSources = ['context']; + protected $supportedValueSources = ['context', 'data', 'query']; + + /** + * The default sources. + * + * @see FormHelper::$supportedValueSources for valid values. + * @var string[] + */ + protected $_valueSources = ['data', 'context']; /** * Grouped input types. @@ -559,7 +572,7 @@ public function end(array $secureAttributes = []): string $this->templater()->pop(); $this->requestType = null; $this->_context = null; - $this->_valueSources = ['context']; + $this->_valueSources = ['data', 'context']; $this->_idPrefix = $this->getConfig('idPrefix'); $this->formProtector = null; @@ -595,7 +608,7 @@ public function secure(array $fields = [], array $secureAttributes = []): string $this->formProtector->addField($field, true, $value); } - $debugSecurity = Configure::read('debug'); + $debugSecurity = (bool)Configure::read('debug'); if (isset($secureAttributes['debugSecurity'])) { $debugSecurity = $debugSecurity && $secureAttributes['debugSecurity']; unset($secureAttributes['debugSecurity']); @@ -660,12 +673,15 @@ protected function createFormProtector(array $formTokenData): FormProtector * Get form protector instance. * * @return \Cake\Form\FormProtector - * @throws \Cake\Core\Exception\Exception + * @throws \Cake\Core\Exception\CakeException */ public function getFormProtector(): FormProtector { if ($this->formProtector === null) { - throw new Exception('FormHelper::create() must be called first for FormProtector instance to be created.'); + throw new CakeException( + '`FormProtector` instance has not been created. Ensure you have loaded the `FormProtectionComponent`' + . ' in your controller and called `FormHelper::create()` before calling `FormHelper::unlockField()`.' + ); } return $this->formProtector; @@ -1222,9 +1238,12 @@ protected function _inputType(string $fieldName, array $options): string return 'select'; } + $type = 'text'; $internalType = $context->type($fieldName); $map = $this->_config['typeMap']; - $type = $map[$internalType] ?? 'text'; + if ($internalType !== null && isset($map[$internalType])) { + $type = $map[$internalType]; + } $fieldName = array_slice(explode('.', $fieldName), -1)[0]; switch (true) { @@ -1319,7 +1338,7 @@ protected function _magicOptions(string $fieldName, array $options, bool $allowO } /** - * Set required attribute and custom validity js. + * Set required attribute and custom validity JS. * * @param string $fieldName The name of the field to generate options for. * @param array $options Options list. @@ -1556,13 +1575,13 @@ public function radio(string $fieldName, iterable $options = [], array $attribut * @param string $method Method name / input type to make. * @param array $params Parameters for the method call * @return string Formatted input method. - * @throws \Cake\Core\Exception\Exception When there are no params for the method call. + * @throws \Cake\Core\Exception\CakeException When there are no params for the method call. */ public function __call(string $method, array $params) { $options = []; if (empty($params)) { - throw new Exception(sprintf('Missing field name for FormHelper::%s', $method)); + throw new CakeException(sprintf('Missing field name for FormHelper::%s', $method)); } if (isset($params[1])) { $options = $params[1]; @@ -1619,7 +1638,7 @@ public function hidden(string $fieldName, array $options = []): string $this->formProtector->addField( $options['name'], true, - (string)$options['val'] + $options['val'] === false ? '0' : (string)$options['val'] ); } @@ -2480,18 +2499,43 @@ public function getValueSources(): array return $this->_valueSources; } + /** + * Validate value sources. + * + * @param string[] $sources A list of strings identifying a source. + * @return void + * @throws \InvalidArgumentException If sources list contains invalid value. + */ + protected function validateValueSources(array $sources): void + { + $diff = array_diff($sources, $this->supportedValueSources); + + if ($diff) { + throw new InvalidArgumentException(sprintf( + 'Invalid value source(s): %s. Valid values are: %s', + implode(', ', $diff), + implode(', ', $this->supportedValueSources) + )); + } + } + /** * Sets the value sources. * - * Valid values are `'context'`, `'data'` and `'query'`. - * You need to supply one valid context or multiple, as a list of strings. Order sets priority. + * You need to supply one or more valid sources, as a list of strings. + * Order sets priority. * + * @see FormHelper::$supportedValueSources for valid values. * @param string|string[] $sources A string or a list of strings identifying a source. * @return $this + * @throws \InvalidArgumentException If sources list contains invalid value. */ public function setValueSources($sources) { - $this->_valueSources = array_values(array_intersect((array)$sources, ['context', 'data', 'query'])); + $sources = (array)$sources; + + $this->validateValueSources($sources); + $this->_valueSources = $sources; return $this; } diff --git a/app/vendor/cakephp/cakephp/src/View/Helper/HtmlHelper.php b/app/vendor/cakephp/cakephp/src/View/Helper/HtmlHelper.php index 8da4a652d..089962220 100644 --- a/app/vendor/cakephp/cakephp/src/View/Helper/HtmlHelper.php +++ b/app/vendor/cakephp/cakephp/src/View/Helper/HtmlHelper.php @@ -37,7 +37,7 @@ class HtmlHelper extends Helper * * @var array */ - public $helpers = ['Url']; + protected $helpers = ['Url']; /** * Default config for this class @@ -299,6 +299,30 @@ public function link($title, $url = null, array $options = []): string ]); } + /** + * Creates an HTML link from route path string. + * + * ### Options + * + * - `escape` Set to false to disable escaping of title and attributes. + * - `escapeTitle` Set to false to disable escaping of title. Takes precedence + * over value of `escape`) + * - `confirm` JavaScript confirmation message. + * + * @param string $title The content to be wrapped by `
` tags. + * @param string $path Cake-relative route path. + * @param array $params An array specifying any additional parameters. + * Can be also any special parameters supported by `Router::url()`. + * @param array $options Array of options and HTML attributes. + * @return string An `` element. + * @see \Cake\Routing\Router::pathUrl() + * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-links + */ + public function linkFromPath(string $title, string $path, array $params = [], array $options = []): string + { + return $this->link($title, ['_path' => $path] + $params, $options); + } + /** * Creates a link element for CSS stylesheets. * @@ -704,7 +728,9 @@ public function tableCells( bool $useCount = false, bool $continueOddEven = true ): string { - if (is_string($data) || empty($data[0]) || !is_array($data[0])) { + if (!is_array($data)) { + $data = [[$data]]; + } elseif (empty($data[0]) || !is_array($data[0])) { $data = [$data]; } @@ -766,7 +792,7 @@ protected function _renderCells(array $line, bool $useCount = false): array } } - $cellsOut[] = $this->tableCell($cell, $cellOptions); + $cellsOut[] = $this->tableCell((string)$cell, $cellOptions); } return $cellsOut; diff --git a/app/vendor/cakephp/cakephp/src/View/Helper/NumberHelper.php b/app/vendor/cakephp/cakephp/src/View/Helper/NumberHelper.php index cf21cfd96..1b3cf8fa3 100644 --- a/app/vendor/cakephp/cakephp/src/View/Helper/NumberHelper.php +++ b/app/vendor/cakephp/cakephp/src/View/Helper/NumberHelper.php @@ -17,7 +17,7 @@ namespace Cake\View\Helper; use Cake\Core\App; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\I18n\Number; use Cake\View\Helper; use Cake\View\View; @@ -58,7 +58,7 @@ class NumberHelper extends Helper * * @param \Cake\View\View $view The View this helper is being attached to. * @param array $config Configuration settings for the helper - * @throws \Cake\Core\Exception\Exception When the engine class could not be found. + * @throws \Cake\Core\Exception\CakeException When the engine class could not be found. */ public function __construct(View $view, array $config = []) { @@ -69,7 +69,7 @@ public function __construct(View $view, array $config = []) /** @psalm-var class-string<\Cake\I18n\Number>|null $engineClass */ $engineClass = App::className($config['engine'], 'Utility'); if ($engineClass === null) { - throw new Exception(sprintf('Class for %s could not be found', $config['engine'])); + throw new CakeException(sprintf('Class for %s could not be found', $config['engine'])); } $this->_engine = new $engineClass($config); @@ -84,7 +84,7 @@ public function __construct(View $view, array $config = []) */ public function __call(string $method, array $params) { - return call_user_func_array([$this->_engine, $method], $params); + return $this->_engine->{$method}(...$params); } /** @@ -222,9 +222,14 @@ public function formatDelta($value, array $options = []): string * if $currency argument is not provided. If boolean false is passed, it will clear the * currently stored value. Null reads the current default. * @return string|null Currency + * @deprecated 3.9.0 Use setDefaultCurrency()/getDefaultCurrency() instead. */ public function defaultCurrency($currency): ?string { + deprecationWarning( + 'NumberHelper::defaultCurrency() is deprecated. Use setDefaultCurrency() and getDefaultCurrency() instead.' + ); + return $this->_engine->defaultCurrency($currency); } diff --git a/app/vendor/cakephp/cakephp/src/View/Helper/PaginatorHelper.php b/app/vendor/cakephp/cakephp/src/View/Helper/PaginatorHelper.php index 2f954b6fa..0c9591a19 100644 --- a/app/vendor/cakephp/cakephp/src/View/Helper/PaginatorHelper.php +++ b/app/vendor/cakephp/cakephp/src/View/Helper/PaginatorHelper.php @@ -43,7 +43,7 @@ class PaginatorHelper extends Helper * * @var array */ - public $helpers = ['Url', 'Number', 'Html', 'Form']; + protected $helpers = ['Url', 'Number', 'Html', 'Form']; /** * Default config for this class @@ -260,7 +260,7 @@ public function sortDir(?string $model = null, array $options = []): string $options = $this->params($model); } - if (isset($options['direction'])) { + if (!empty($options['direction'])) { $dir = strtolower($options['direction']); } @@ -567,20 +567,18 @@ public function generateUrlParams(array $options = [], ?string $model = null, ar ) { $options['sort'] = $options['direction'] = null; } - + $baseUrl = $this->_config['options']['url'] ?? []; if (!empty($paging['scope'])) { $scope = $paging['scope']; - $currentParams = $this->_config['options']['url']; - - if (isset($currentParams['?'][$scope]) && is_array($currentParams['?'][$scope])) { - $options += $currentParams['?'][$scope]; + if (isset($baseUrl['?'][$scope]) && is_array($baseUrl['?'][$scope])) { + $options += $baseUrl['?'][$scope]; + unset($baseUrl['?'][$scope]); } $options = [$scope => $options]; } - if (!empty($this->_config['options']['url'])) { - $key = implode('.', array_filter(['options.url', Hash::get($paging, 'scope', null)])); - $url = Hash::merge($url, Hash::get($this->_config, $key, [])); + if (!empty($baseUrl)) { + $url = Hash::merge($url, $baseUrl); } if (!isset($url['?'])) { diff --git a/app/vendor/cakephp/cakephp/src/View/Helper/TextHelper.php b/app/vendor/cakephp/cakephp/src/View/Helper/TextHelper.php index b4d19e1f8..b6cdbab7a 100644 --- a/app/vendor/cakephp/cakephp/src/View/Helper/TextHelper.php +++ b/app/vendor/cakephp/cakephp/src/View/Helper/TextHelper.php @@ -17,7 +17,7 @@ namespace Cake\View\Helper; use Cake\Core\App; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Utility\Security; use Cake\Utility\Text; use Cake\View\Helper; @@ -39,7 +39,7 @@ class TextHelper extends Helper * * @var array */ - public $helpers = ['Html']; + protected $helpers = ['Html']; /** * Default config for this class @@ -75,7 +75,7 @@ class TextHelper extends Helper * * @param \Cake\View\View $view the view object the helper is attached to. * @param array $config Settings array Settings array - * @throws \Cake\Core\Exception\Exception when the engine class could not be found. + * @throws \Cake\Core\Exception\CakeException when the engine class could not be found. */ public function __construct(View $view, array $config = []) { @@ -86,7 +86,7 @@ public function __construct(View $view, array $config = []) /** @psalm-var class-string<\Cake\Utility\Text>|null $engineClass */ $engineClass = App::className($config['engine'], 'Utility'); if ($engineClass === null) { - throw new Exception(sprintf('Class for %s could not be found', $config['engine'])); + throw new CakeException(sprintf('Class for %s could not be found', $config['engine'])); } $this->_engine = new $engineClass($config); @@ -101,7 +101,7 @@ public function __construct(View $view, array $config = []) */ public function __call(string $method, array $params) { - return call_user_func_array([$this->_engine, $method], $params); + return $this->_engine->{$method}(...$params); } /** @@ -397,6 +397,32 @@ public function toList(array $list, ?string $and = null, string $separator = ', return $this->_engine->toList($list, $and, $separator); } + /** + * Returns a string with all spaces converted to dashes (by default), + * characters transliterated to ASCII characters, and non word characters removed. + * + * ### Options: + * + * - `replacement`: Replacement string. Default '-'. + * - `transliteratorId`: A valid transliterator id string. + * If `null` (default) the transliterator (identifier) set via + * `Text::setTransliteratorId()` or `Text::setTransliterator()` will be used. + * If `false` no transliteration will be done, only non words will be removed. + * - `preserve`: Specific non-word character to preserve. Default `null`. + * For e.g. this option can be set to '.' to generate clean file names. + * + * @param string $string the string you want to slug + * @param array|string $options If string it will be use as replacement character + * or an array of options. + * @return string + * @see \Cake\Utility\Text::setTransliterator() + * @see \Cake\Utility\Text::setTransliteratorId() + */ + public function slug(string $string, $options = []): string + { + return $this->_engine->slug($string, $options); + } + /** * Event listeners. * diff --git a/app/vendor/cakephp/cakephp/src/View/Helper/TimeHelper.php b/app/vendor/cakephp/cakephp/src/View/Helper/TimeHelper.php index 614c7d9c1..693ddf874 100644 --- a/app/vendor/cakephp/cakephp/src/View/Helper/TimeHelper.php +++ b/app/vendor/cakephp/cakephp/src/View/Helper/TimeHelper.php @@ -351,7 +351,7 @@ public function gmt($string = null): string * * This method is an alias for TimeHelper::i18nFormat(). * - * @param int|string|\DateTimeInterface $date UNIX timestamp, strtotime() valid string + * @param int|string|\DateTimeInterface|null $date UNIX timestamp, strtotime() valid string * or DateTime object (or a date format string). * @param int|string|null $format date format string (or a UNIX timestamp, * strtotime() valid string or DateTime object). diff --git a/app/vendor/cakephp/cakephp/src/View/Helper/UrlHelper.php b/app/vendor/cakephp/cakephp/src/View/Helper/UrlHelper.php index f232222eb..a13d61140 100644 --- a/app/vendor/cakephp/cakephp/src/View/Helper/UrlHelper.php +++ b/app/vendor/cakephp/cakephp/src/View/Helper/UrlHelper.php @@ -16,6 +16,8 @@ */ namespace Cake\View\Helper; +use Cake\Core\App; +use Cake\Core\Exception\CakeException; use Cake\Routing\Asset; use Cake\Routing\Router; use Cake\View\Helper; @@ -25,6 +27,43 @@ */ class UrlHelper extends Helper { + /** + * Default config for this class + * + * @var array + */ + protected $_defaultConfig = [ + 'assetUrlClassName' => Asset::class, + ]; + + /** + * Asset URL engine class name + * + * @var string + * @psalm-var class-string<\Cake\Routing\Asset> + */ + protected $_assetUrlClassName; + + /** + * Check proper configuration + * + * @param array $config The configuration settings provided to this helper. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + $engineClassConfig = $this->getConfig('assetUrlClassName'); + + /** @psalm-var class-string<\Cake\Routing\Asset>|null $engineClass */ + $engineClass = App::className($engineClassConfig, 'Routing'); + if ($engineClass === null) { + throw new CakeException(sprintf('Class for %s could not be found', $engineClassConfig)); + } + + $this->_assetUrlClassName = $engineClass; + } + /** * Returns a URL based on provided parameters. * @@ -57,6 +96,27 @@ public function build($url = null, array $options = []): string return $url; } + /** + * Returns a URL from a route path string. + * + * ### Options: + * + * - `escape`: If false, the URL will be returned unescaped, do only use if it is manually + * escaped afterwards before being displayed. + * - `fullBase`: If true, the full base URL will be prepended to the result + * + * @param string $path Cake-relative route path. + * @param array $params An array specifying any additional parameters. + * Can be also any special parameters supported by `Router::url()`. + * @param array $options Array of options. + * @return string Full translated URL with base path. + * @see \Cake\Routing\Router::pathUrl() + */ + public function buildFromPath(string $path, array $params = [], array $options = []): string + { + return $this->build(['_path' => $path] + $params, $options); + } + /** * Generates URL for given image file. * @@ -78,7 +138,7 @@ public function image(string $path, array $options = []): string { $options += ['theme' => $this->_View->getTheme()]; - return h(Asset::imageUrl($path, $options)); + return h($this->_assetUrlClassName::imageUrl($path, $options)); } /** @@ -103,7 +163,7 @@ public function css(string $path, array $options = []): string { $options += ['theme' => $this->_View->getTheme()]; - return h(Asset::cssUrl($path, $options)); + return h($this->_assetUrlClassName::cssUrl($path, $options)); } /** @@ -128,7 +188,7 @@ public function script(string $path, array $options = []): string { $options += ['theme' => $this->_View->getTheme()]; - return h(Asset::scriptUrl($path, $options)); + return h($this->_assetUrlClassName::scriptUrl($path, $options)); } /** @@ -157,7 +217,7 @@ public function assetUrl(string $path, array $options = []): string { $options += ['theme' => $this->_View->getTheme()]; - return h(Asset::url($path, $options)); + return h($this->_assetUrlClassName::url($path, $options)); } /** @@ -171,7 +231,7 @@ public function assetUrl(string $path, array $options = []): string */ public function assetTimestamp(string $path, $timestamp = null): string { - return h(Asset::assetTimestamp($path, $timestamp)); + return h($this->_assetUrlClassName::assetTimestamp($path, $timestamp)); } /** @@ -184,7 +244,7 @@ public function webroot(string $file): string { $options = ['theme' => $this->_View->getTheme()]; - return h(Asset::webroot($file, $options)); + return h($this->_assetUrlClassName::webroot($file, $options)); } /** diff --git a/app/vendor/cakephp/cakephp/src/View/HelperRegistry.php b/app/vendor/cakephp/cakephp/src/View/HelperRegistry.php index e6ba25d37..bb122741a 100644 --- a/app/vendor/cakephp/cakephp/src/View/HelperRegistry.php +++ b/app/vendor/cakephp/cakephp/src/View/HelperRegistry.php @@ -143,16 +143,16 @@ protected function _throwMissingClassError(string $class, ?string $plugin): void * * @param string $class The class to create. * @param string $alias The alias of the loaded helper. - * @param array $settings An array of settings to use for the helper. + * @param array $config An array of settings to use for the helper. * @return \Cake\View\Helper The constructed helper class. * @psalm-suppress MoreSpecificImplementedParamType */ - protected function _create($class, string $alias, array $settings): Helper + protected function _create($class, string $alias, array $config): Helper { /** @var \Cake\View\Helper $instance */ - $instance = new $class($this->_View, $settings); + $instance = new $class($this->_View, $config); - $enable = $settings['enabled'] ?? true; + $enable = $config['enabled'] ?? true; if ($enable) { $this->getEventManager()->on($instance); } diff --git a/app/vendor/cakephp/cakephp/src/View/JsonView.php b/app/vendor/cakephp/cakephp/src/View/JsonView.php index 7f79d958d..71248a67e 100644 --- a/app/vendor/cakephp/cakephp/src/View/JsonView.php +++ b/app/vendor/cakephp/cakephp/src/View/JsonView.php @@ -49,7 +49,7 @@ * You can also set `'serialize'` to a string or array to serialize only the * specified view variables. * - * If you don't set the `serialize` opton, you will need a view template. + * If you don't set the `serialize` option, you will need a view template. * You can use extended views to provide layout-like functionality. * * You can also enable JSONP support by setting `jsonp` option to true or a @@ -59,7 +59,7 @@ class JsonView extends SerializedView { /** - * JSON layouts are located in the json sub directory of `Layouts/` + * JSON layouts are located in the JSON sub directory of `Layouts/` * * @var string */ @@ -94,8 +94,7 @@ class JsonView extends SerializedView * - Setting it to a string value, uses the provided query string parameter * for finding the JSONP callback name. * - * @var array - * @pslam-var array{serialize:string|bool|null, jsonOptions: int|null, jsonp: bool|string|null} + * @var array */ protected $_defaultConfig = [ 'serialize' => null, diff --git a/app/vendor/cakephp/cakephp/src/View/SerializedView.php b/app/vendor/cakephp/cakephp/src/View/SerializedView.php index 383180a16..a8a83bf04 100644 --- a/app/vendor/cakephp/cakephp/src/View/SerializedView.php +++ b/app/vendor/cakephp/cakephp/src/View/SerializedView.php @@ -16,9 +16,6 @@ */ namespace Cake\View; -use Cake\Event\EventManager; -use Cake\Http\Response; -use Cake\Http\ServerRequest; use Cake\View\Exception\SerializationFailureException; use Exception; use TypeError; @@ -45,32 +42,19 @@ abstract class SerializedView extends View * names. If true all view variables will be serialized. If null or false * normal view template will be rendered. * - * @var array - * @psalm-var array{serialize:string|bool|null} + * @var array */ protected $_defaultConfig = [ 'serialize' => null, ]; /** - * Constructor - * - * @param \Cake\Http\ServerRequest|null $request Request instance. - * @param \Cake\Http\Response|null $response Response instance. - * @param \Cake\Event\EventManager|null $eventManager EventManager instance. - * @param array $viewOptions An array of view options + * @inheritDoc */ - public function __construct( - ?ServerRequest $request = null, - ?Response $response = null, - ?EventManager $eventManager = null, - array $viewOptions = [] - ) { - if ($response) { - $response = $response->withType($this->_responseType); - } - - parent::__construct($request, $response, $eventManager, $viewOptions); + public function initialize(): void + { + parent::initialize(); + $this->setResponse($this->getResponse()->withType($this->_responseType)); } /** diff --git a/app/vendor/cakephp/cakephp/src/View/StringTemplate.php b/app/vendor/cakephp/cakephp/src/View/StringTemplate.php index aebb4d8d6..a0cae660e 100644 --- a/app/vendor/cakephp/cakephp/src/View/StringTemplate.php +++ b/app/vendor/cakephp/cakephp/src/View/StringTemplate.php @@ -17,7 +17,7 @@ namespace Cake\View; use Cake\Core\Configure\Engine\PhpConfig; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; use Cake\Core\InstanceConfigTrait; use Cake\Utility\Hash; use RuntimeException; @@ -203,7 +203,7 @@ protected function _compileTemplates(array $templates = []): void public function load(string $file): void { if ($file === '') { - throw new Exception('String template filename cannot be an empty string'); + throw new CakeException('String template filename cannot be an empty string'); } $loader = new PhpConfig(); diff --git a/app/vendor/cakephp/cakephp/src/View/StringTemplateTrait.php b/app/vendor/cakephp/cakephp/src/View/StringTemplateTrait.php index 03ef78611..2529801f6 100644 --- a/app/vendor/cakephp/cakephp/src/View/StringTemplateTrait.php +++ b/app/vendor/cakephp/cakephp/src/View/StringTemplateTrait.php @@ -29,7 +29,7 @@ trait StringTemplateTrait /** * StringTemplate instance. * - * @var \Cake\View\StringTemplate + * @var \Cake\View\StringTemplate|null */ protected $_templater; diff --git a/app/vendor/cakephp/cakephp/src/View/View.php b/app/vendor/cakephp/cakephp/src/View/View.php index 6df6626fd..11796b3d9 100644 --- a/app/vendor/cakephp/cakephp/src/View/View.php +++ b/app/vendor/cakephp/cakephp/src/View/View.php @@ -34,6 +34,7 @@ use InvalidArgumentException; use LogicException; use RuntimeException; +use Throwable; /** * View, the V in the MVC triad. View interacts with Helpers and view variables passed @@ -120,11 +121,11 @@ class View implements EventDispatcherInterface * * @var string */ - protected $templatePath; + protected $templatePath = ''; /** * The name of the template file to render. The name specified - * is the filename in /templates/ without the .php extension. + * is the filename in `templates//` without the .php extension. * * @var string */ @@ -132,7 +133,7 @@ class View implements EventDispatcherInterface /** * The name of the layout file to render the template inside of. The name specified - * is the filename of the layout in /templates/Layout without the .php + * is the filename of the layout in `templates/layout/` without the .php * extension. * * @var string @@ -222,7 +223,7 @@ class View implements EventDispatcherInterface /** * Default custom config options. * - * @var string[] + * @var array */ protected $_defaultConfig = []; @@ -526,7 +527,7 @@ public function setTheme(?string $theme) /** * Get the name of the template file to render. The name specified is the - * filename in /templates/ without the .php extension. + * filename in `templates//` without the .php extension. * * @return string */ @@ -537,7 +538,7 @@ public function getTemplate(): string /** * Set the name of the template file to render. The name specified is the - * filename in /templates/ without the .php extension. + * filename in `templates//` without the .php extension. * * @param string $name Template file name to set. * @return $this @@ -551,7 +552,7 @@ public function setTemplate(string $name) /** * Get the name of the layout file to render the template inside of. - * The name specified is the filename of the layout in /templates/Layout + * The name specified is the filename of the layout in `templates/layout/` * without the .php extension. * * @return string @@ -563,7 +564,7 @@ public function getLayout(): string /** * Set the name of the layout file to render the template inside of. - * The name specified is the filename of the layout in /templates/Layout + * The name specified is the filename of the layout in `templates/layout/` * without the .php extension. * * @param string $name Layout file name to set. @@ -615,7 +616,7 @@ public function getConfig(?string $key = null, $default = null) * This realizes the concept of Elements, (or "partial layouts") and the $params array is used to send * data to be used in the element. Elements can be cached improving performance by using the `cache` option. * - * @param string $name Name of template file in the /templates/Element/ folder, + * @param string $name Name of template file in the `templates/element/` folder, * or `MyPlugin.template` to use the template element from MyPlugin. If the element * is not found in the plugin, the normal view path cascade will be searched. * @param array $data Array of data to be made available to the rendered view (i.e. the Element) @@ -655,9 +656,9 @@ public function element(string $name, array $data = [], array $options = []): st } if (empty($options['ignoreMissing'])) { - [$plugin] = $this->pluginSplit($name, $pluginCheck); + [$plugin, $elementName] = $this->pluginSplit($name, $pluginCheck); $paths = iterator_to_array($this->getElementPaths($plugin)); - throw new MissingElementException($name . $this->_ext, $paths); + throw new MissingElementException([$name . $this->_ext, $elementName . $this->_ext], $paths); } return ''; @@ -687,8 +688,20 @@ public function cache(callable $block, array $options = []): string if ($result) { return $result; } + + $bufferLevel = ob_get_level(); ob_start(); - $block(); + + try { + $block(); + } catch (Throwable $exception) { + while (ob_get_level() > $bufferLevel) { + ob_end_clean(); + } + + throw $exception; + } + $result = ob_get_clean(); Cache::write($options['key'], $result, $options['config']); @@ -699,7 +712,7 @@ public function cache(callable $block, array $options = []): string /** * Checks if an element exists * - * @param string $name Name of template file in the /templates/Element/ folder, + * @param string $name Name of template file in the `templates/element/` folder, * or `MyPlugin.template` to check the template element from MyPlugin. If the element * is not found in the plugin, the normal view path cascade will be searched. * @return bool Success @@ -722,14 +735,14 @@ public function elementExists(string $name): bool * * If View::$autoLayout is set to `false`, the template will be returned bare. * - * Template and layout names can point to plugin templates/layouts. Using the `Plugin.template` syntax - * a plugin template/layout can be used instead of the app ones. If the chosen plugin is not found + * Template and layout names can point to plugin templates or layouts. Using the `Plugin.template` syntax + * a plugin template/layout/ can be used instead of the app ones. If the chosen plugin is not found * the template will be located along the regular view path cascade. * * @param string|null $template Name of template file to use * @param string|false|null $layout Layout to use. False to disable. * @return string Rendered content. - * @throws \Cake\Core\Exception\Exception If there is an error in the view. + * @throws \Cake\Core\Exception\CakeException If there is an error in the view. * @triggers View.beforeRender $this, [$templateFileName] * @triggers View.afterRender $this, [$templateFileName] */ @@ -779,7 +792,7 @@ public function render(?string $template = null, $layout = null): string * @param string $content Content to render in a template, wrapped by the surrounding layout. * @param string|null $layout Layout name * @return string Rendered output. - * @throws \Cake\Core\Exception\Exception if there is an error in the view. + * @throws \Cake\Core\Exception\CakeException if there is an error in the view. * @triggers View.beforeLayout $this, [$layoutFileName] * @triggers View.afterLayout $this, [$layoutFileName] */ @@ -795,7 +808,7 @@ public function renderLayout(string $content, ?string $layout = null): string $title = $this->Blocks->get('title'); if ($title === '') { - $title = Inflector::humanize(str_replace(DIRECTORY_SEPARATOR, '/', (string)$this->templatePath)); + $title = Inflector::humanize(str_replace(DIRECTORY_SEPARATOR, '/', $this->templatePath)); $this->Blocks->set('title', $title); } @@ -1155,9 +1168,19 @@ protected function _render(string $templateFile, array $data = []): string protected function _evaluate(string $templateFile, array $dataForView): string { extract($dataForView); + + $bufferLevel = ob_get_level(); ob_start(); - include func_get_arg(0); + try { + include func_get_arg(0); + } catch (Throwable $exception) { + while (ob_get_level() > $bufferLevel) { + ob_end_clean(); + } + + throw $exception; + } return ob_get_clean(); } @@ -1316,14 +1339,14 @@ protected function _getTemplateFileName(?string $name = null): string } elseif (!$plugin || $this->templatePath !== $this->name) { $name = $templatePath . $subDir . $name; } else { - $name = DIRECTORY_SEPARATOR . $subDir . $name; + $name = $subDir . $name; } } $name .= $this->_ext; $paths = $this->_paths($plugin); foreach ($paths as $path) { - if (file_exists($path . $name)) { + if (is_file($path . $name)) { return $this->_checkFilePath($path . $name, $path); } } @@ -1417,7 +1440,7 @@ protected function _getLayoutFileName(?string $name = null): string $name .= $this->_ext; foreach ($this->getLayoutPaths($plugin) as $path) { - if (file_exists($path . $name)) { + if (is_file($path . $name)) { return $this->_checkFilePath($path . $name, $path); } } @@ -1460,7 +1483,7 @@ protected function _getElementFileName(string $name, bool $pluginCheck = true) $name .= $this->_ext; foreach ($this->getElementPaths($plugin) as $path) { - if (file_exists($path . $name)) { + if (is_file($path . $name)) { return $path . $name; } } diff --git a/app/vendor/cakephp/cakephp/src/View/ViewBlock.php b/app/vendor/cakephp/cakephp/src/View/ViewBlock.php index 0ab142108..ec6eb3184 100644 --- a/app/vendor/cakephp/cakephp/src/View/ViewBlock.php +++ b/app/vendor/cakephp/cakephp/src/View/ViewBlock.php @@ -16,7 +16,7 @@ */ namespace Cake\View; -use Cake\Core\Exception\Exception; +use Cake\Core\Exception\CakeException; /** * ViewBlock implements the concept of Blocks or Slots in the View layer. @@ -82,13 +82,13 @@ class ViewBlock * @param string $mode If ViewBlock::OVERRIDE existing content will be overridden by new content. * If ViewBlock::APPEND content will be appended to existing content. * If ViewBlock::PREPEND it will be prepended. - * @throws \Cake\Core\Exception\Exception When starting a block twice + * @throws \Cake\Core\Exception\CakeException When starting a block twice * @return void */ public function start(string $name, string $mode = ViewBlock::OVERRIDE): void { if (array_key_exists($name, $this->_active)) { - throw new Exception(sprintf("A view block with the name '%s' is already/still open.", $name)); + throw new CakeException(sprintf("A view block with the name '%s' is already/still open.", $name)); } $this->_active[$name] = $mode; ob_start(); diff --git a/app/vendor/cakephp/cakephp/src/View/ViewBuilder.php b/app/vendor/cakephp/cakephp/src/View/ViewBuilder.php index 61d47c018..a36fa37b5 100644 --- a/app/vendor/cakephp/cakephp/src/View/ViewBuilder.php +++ b/app/vendor/cakephp/cakephp/src/View/ViewBuilder.php @@ -301,6 +301,25 @@ public function getPlugin(): ?string return $this->_plugin; } + /** + * Adds a helper to use. + * + * @param string $helper Helper to use. + * @param array $options Options. + * @return $this + * @since 4.1.0 + */ + public function addHelper(string $helper, array $options = []) + { + if ($options) { + $array = [$helper => $options]; + } else { + $array = [$helper]; + } + + return $this->setHelpers($array); + } + /** * Sets the helpers to use. * @@ -354,7 +373,7 @@ public function getTheme(): ?string /** * Sets the name of the view file to render. The name specified is the - * filename in /templates/ without the .php extension. + * filename in `templates//` without the .php extension. * * @param string|null $name View file name to set, or null to remove the template name. * @return $this @@ -368,7 +387,7 @@ public function setTemplate(?string $name) /** * Gets the name of the view file to render. The name specified is the - * filename in /templates/ without the .php extension. + * filename in `templates//` without the .php extension. * * @return string|null */ @@ -379,7 +398,7 @@ public function getTemplate(): ?string /** * Sets the name of the layout file to render the view inside of. - * The name specified is the filename of the layout in /templates/Layout + * The name specified is the filename of the layout in `templates/layout/` * without the .php extension. * * @param string|null $name Layout file name to set. @@ -551,7 +570,6 @@ public function build( ]; $data += $this->_options; - // phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.InvalidFormat /** @var \Cake\View\View */ return new $className($request, $response, $events, $data); } diff --git a/app/vendor/cakephp/cakephp/src/View/ViewVarsTrait.php b/app/vendor/cakephp/cakephp/src/View/ViewVarsTrait.php index 71960f271..fe170bb7a 100644 --- a/app/vendor/cakephp/cakephp/src/View/ViewVarsTrait.php +++ b/app/vendor/cakephp/cakephp/src/View/ViewVarsTrait.php @@ -67,6 +67,7 @@ public function createView(?string $viewClass = null): View } } + /** @psalm-suppress RedundantPropertyInitializationCheck */ return $builder->build( [], $this->request ?? null, diff --git a/app/vendor/cakephp/cakephp/src/View/Widget/DateTimeWidget.php b/app/vendor/cakephp/cakephp/src/View/Widget/DateTimeWidget.php index be9652355..8715c9c05 100644 --- a/app/vendor/cakephp/cakephp/src/View/Widget/DateTimeWidget.php +++ b/app/vendor/cakephp/cakephp/src/View/Widget/DateTimeWidget.php @@ -91,6 +91,13 @@ class DateTimeWidget extends BasicWidget * - `escape` Set to false to disable escaping on all attributes. * - `type` A valid HTML date/time input type. Defaults to "datetime-local". * - `timezone` The timezone the input value should be converted to. + * - `step` The "step" attribute. Defaults to `1` for "time" and "datetime-local" type inputs. + * You can set it to `null` or `false` to prevent explicit step attribute being added in HTML. + * - `format` A `date()` function compatible datetime format string. + * By default the widget will use a suitable format based on the input type and + * database type for the context. If an explicit format is provided, then no + * default value will be set for the `step` attribute, and it needs to be + * explicitly set if required. * * All other keys will be converted into HTML attributes. * @@ -109,16 +116,10 @@ public function render(array $data, ContextInterface $context): string )); } - if (!isset($data['step'])) { - $data['step'] = $this->defaultStep[$data['type']]; - - if (isset($data['fieldName'])) { - $data = $this->setStep($data, $context, $data['fieldName']); - } - } + $data = $this->setStep($data, $context, $data['fieldName'] ?? ''); $data['value'] = $this->formatDateTime($data['val'], $data); - unset($data['val'], $data['timezone']); + unset($data['val'], $data['timezone'], $data['format']); return $this->_templates->format('input', [ 'name' => $data['name'], @@ -141,6 +142,20 @@ public function render(array $data, ContextInterface $context): string */ protected function setStep(array $data, ContextInterface $context, string $fieldName): array { + if (array_key_exists('step', $data)) { + return $data; + } + + if (isset($data['format'])) { + $data['step'] = null; + } else { + $data['step'] = $this->defaultStep[$data['type']]; + } + + if (empty($data['fieldName'])) { + return $data; + } + $dbType = $context->type($fieldName); $fractionalTypes = [ TableSchema::TYPE_DATETIME_FRACTIONAL, @@ -192,9 +207,18 @@ protected function formatDateTime($value, array $options): string $dateTime = $dateTime->setTimezone($timezone); } - $format = $this->formatMap[$options['type']]; - if ($options['type'] === 'datetime-local' && $options['step'] < 1) { - $format = 'Y-m-d\TH:i:s.v'; + if (isset($options['format'])) { + $format = $options['format']; + } else { + $format = $this->formatMap[$options['type']]; + + if ( + $options['type'] === 'datetime-local' + && is_numeric($options['step']) + && $options['step'] < 1 + ) { + $format = 'Y-m-d\TH:i:s.v'; + } } return $dateTime->format($format); diff --git a/app/vendor/cakephp/cakephp/src/View/Widget/MultiCheckboxWidget.php b/app/vendor/cakephp/cakephp/src/View/Widget/MultiCheckboxWidget.php index 7afaacb4c..dbc5e1a9e 100644 --- a/app/vendor/cakephp/cakephp/src/View/Widget/MultiCheckboxWidget.php +++ b/app/vendor/cakephp/cakephp/src/View/Widget/MultiCheckboxWidget.php @@ -235,7 +235,7 @@ protected function _renderInput(array $checkbox, ContextInterface $context): str * Helper method for deciding what options are selected. * * @param string $key The key to test. - * @param array|string|null $selected The selected values. + * @param string[]|string|int|false|null $selected The selected values. * @return bool */ protected function _isSelected(string $key, $selected): bool diff --git a/app/vendor/cakephp/cakephp/src/View/Widget/RadioWidget.php b/app/vendor/cakephp/cakephp/src/View/Widget/RadioWidget.php index 587dc3244..95f840236 100644 --- a/app/vendor/cakephp/cakephp/src/View/Widget/RadioWidget.php +++ b/app/vendor/cakephp/cakephp/src/View/Widget/RadioWidget.php @@ -169,7 +169,7 @@ protected function _renderInput($val, $text, $data, $context): string if (empty($radio['id'])) { if (isset($data['id'])) { - $radio['id'] = $data['id'] . '-' . trim( + $radio['id'] = $data['id'] . '-' . rtrim( $this->_idSuffix((string)$radio['value']), '-' ); diff --git a/app/vendor/cakephp/cakephp/src/View/Widget/SelectBoxWidget.php b/app/vendor/cakephp/cakephp/src/View/Widget/SelectBoxWidget.php index 371a87c39..2ba43947b 100644 --- a/app/vendor/cakephp/cakephp/src/View/Widget/SelectBoxWidget.php +++ b/app/vendor/cakephp/cakephp/src/View/Widget/SelectBoxWidget.php @@ -306,7 +306,7 @@ protected function _renderOptions(iterable $options, ?array $disabled, $selected * Helper method for deciding what options are selected. * * @param string $key The key to test. - * @param string[]|string|false|null $selected The selected values. + * @param string[]|string|int|false|null $selected The selected values. * @return bool */ protected function _isSelected(string $key, $selected): bool diff --git a/app/vendor/cakephp/cakephp/src/View/Widget/YearWidget.php b/app/vendor/cakephp/cakephp/src/View/Widget/YearWidget.php index 91380028f..cfa633406 100644 --- a/app/vendor/cakephp/cakephp/src/View/Widget/YearWidget.php +++ b/app/vendor/cakephp/cakephp/src/View/Widget/YearWidget.php @@ -37,6 +37,8 @@ class YearWidget extends BasicWidget protected $defaults = [ 'name' => '', 'val' => null, + 'min' => null, + 'max' => null, 'order' => 'desc', 'templateVars' => [], ]; diff --git a/app/vendor/cakephp/cakephp/src/View/XmlView.php b/app/vendor/cakephp/cakephp/src/View/XmlView.php index 4962cfeaa..3e9969f59 100644 --- a/app/vendor/cakephp/cakephp/src/View/XmlView.php +++ b/app/vendor/cakephp/cakephp/src/View/XmlView.php @@ -61,7 +61,7 @@ class XmlView extends SerializedView { /** - * XML layouts are located in the xml sub directory of `Layouts/` + * XML layouts are located in the `layouts/xml/` sub directory * * @var string */ @@ -103,8 +103,7 @@ class XmlView extends SerializedView * For e.g. 'format' as 'attributes' instead of 'tags'. * - `rootNode`: Root node name. Defaults to "response". * - * @var array - * @psalm-var array{serialize:string|bool|null, xmlOptions: int|null, rootNode: string|null} + * @var array */ protected $_defaultConfig = [ 'serialize' => null, @@ -143,6 +142,7 @@ protected function _serialize($serialize): string $data && (!is_array($data) || Hash::numeric(array_keys($data))) ) { + /** @psalm-suppress InvalidArrayOffset */ $data = [$rootNode => [$serialize => $data]]; } } diff --git a/app/vendor/cakephp/cakephp/src/basics.php b/app/vendor/cakephp/cakephp/src/basics.php index 80e0adb5a..214dbc831 100644 --- a/app/vendor/cakephp/cakephp/src/basics.php +++ b/app/vendor/cakephp/cakephp/src/basics.php @@ -100,11 +100,12 @@ function stackTrace(array $options = []): void * Works the same way as eval(\Psy\sh()); * psy/psysh must be loaded in your project * - * @link http://psysh.org/ * ``` * eval(breakpoint()); * ``` + * * @return string|null + * @link http://psysh.org/ */ function breakpoint(): ?string { diff --git a/app/vendor/cakephp/cakephp/templates/Error/duplicate_named_route.php b/app/vendor/cakephp/cakephp/templates/Error/duplicate_named_route.php index 8f19fb9c0..51bc8a31e 100644 --- a/app/vendor/cakephp/cakephp/templates/Error/duplicate_named_route.php +++ b/app/vendor/cakephp/cakephp/templates/Error/duplicate_named_route.php @@ -34,28 +34,16 @@ even if the names occur in different routing scopes. Remove duplicate route names in your route configuration.

- -

The passed context was:

-
-
-
- -

Duplicate Route

- +
- '; - printf( - '', - h($other->template), - h(Debugger::exportVar($other->defaults)), - h(Debugger::exportVar($other->options)) - ); - echo ''; - ?> + + + + + +
TemplateDefaultsOptions
%s%s%s
template) ?>
defaults) ?>
options) ?>
end() ?> diff --git a/app/vendor/cakephp/cakephp/templates/Error/missing_route.php b/app/vendor/cakephp/cakephp/templates/Error/missing_route.php index cdab9ae90..fbf7e652b 100644 --- a/app/vendor/cakephp/cakephp/templates/Error/missing_route.php +++ b/app/vendor/cakephp/cakephp/templates/Error/missing_route.php @@ -35,25 +35,20 @@

The passed context was:

-
-
-
+
+ +

Connected Routes

- +
-'; - printf( - '', - h($route->template), - h(Debugger::exportVar($route->defaults)), - h(Debugger::exportVar($route->options)) - ); - echo ''; -endforeach; -?> + + + + + + +
TemplateDefaultsOptions
%s%s%s
template) ?>
defaults) ?>
options) ?>
end() ?> diff --git a/app/vendor/cakephp/cakephp/templates/element/auto_table_warning.php b/app/vendor/cakephp/cakephp/templates/element/auto_table_warning.php index dc65ab8ee..32881c413 100644 --- a/app/vendor/cakephp/cakephp/templates/element/auto_table_warning.php +++ b/app/vendor/cakephp/cakephp/templates/element/auto_table_warning.php @@ -27,10 +27,10 @@

This could be the cause for this exception. Auto-Tables are created for you under the following circumstances:

  • The class for the specified table does not exist.
  • -
  • The Table was created with a typo: $this->getTableLocator()->get('Atricles');
  • -
  • The class file has a typo in the name or incorrect namespace: class Atricles extends Table
  • -
  • The file containing the class has a typo or incorrect casing: Atricles.php
  • -
  • The Table was used using associations but the association has a typo: $this->belongsTo('Atricles')
  • +
  • The Table was created with a typo: $this->getTableLocator()->get('Articles');
  • +
  • The class file has a typo in the name or incorrect namespace: class Articles extends Table
  • +
  • The file containing the class has a typo or incorrect casing: Articles.php
  • +
  • The Table was used using associations but the association has a typo: $this->belongsTo('Articles')
  • The table class resides in a Plugin but no plugin notation was used in the association definition.

diff --git a/app/vendor/cakephp/cakephp/templates/element/exception_stack_trace.php b/app/vendor/cakephp/cakephp/templates/element/exception_stack_trace.php index 65c884b1b..c6a8aa4dd 100644 --- a/app/vendor/cakephp/cakephp/templates/element/exception_stack_trace.php +++ b/app/vendor/cakephp/cakephp/templates/element/exception_stack_trace.php @@ -20,8 +20,10 @@ foreach ($trace as $i => $stack): $excerpt = $params = []; + $line = null; if (isset($stack['file'], $stack['line']) && is_numeric($stack['line'])): - $excerpt = Debugger::excerpt($stack['file'], $stack['line'], 4); + $line = $stack['line']; + $excerpt = Debugger::excerpt($stack['file'], $line, 4); endif; if (isset($stack['file'])): @@ -42,7 +44,13 @@ ?>
@@ -56,8 +64,11 @@ - diff --git a/app/vendor/cakephp/cakephp/templates/layout/dev_error.php b/app/vendor/cakephp/cakephp/templates/layout/dev_error.php index d9cf35054..39321bc0a 100644 --- a/app/vendor/cakephp/cakephp/templates/layout/dev_error.php +++ b/app/vendor/cakephp/cakephp/templates/layout/dev_error.php @@ -148,10 +148,11 @@ .stack-frame { background: #e5e5e5; padding: 10px; - margin-bottom: 5px; + margin-bottom: 10px; } .stack-frame:last-child { border-bottom: none; + margin-bottom: 0; } .stack-frame a { display: block; @@ -168,6 +169,9 @@ display: flex; align-items: center; } + .stack-frame-file a { + color: #212121; + } .stack-frame-args { flex: 0 0 150px; @@ -220,7 +224,7 @@ .code-excerpt { width: 100%; - margin: 10px 0; + margin: 10px 0 0 0; background: #fefefe; } .code-highlight { @@ -241,6 +245,9 @@ .excerpt-number:after { content: attr(data-number); } + .cake-debug { + margin-top: 10px; + } table { text-align: left; diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/BinaryUuidItemsBinaryUuidTagsFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/BinaryUuidItemsBinaryUuidTagsFixture.php new file mode 100644 index 000000000..172e12acb --- /dev/null +++ b/app/vendor/cakephp/cakephp/tests/Fixture/BinaryUuidItemsBinaryUuidTagsFixture.php @@ -0,0 +1,48 @@ + ['type' => 'integer'], + 'binary_uuid_item_id' => ['type' => 'binaryuuid', 'null' => false], + 'binary_uuid_tag_id' => ['type' => 'binaryuuid', 'null' => false], + '_constraints' => [ + 'primary' => ['type' => 'primary', 'columns' => ['id']], + 'unique_item_tag' => [ + 'type' => 'unique', + 'columns' => ['binary_uuid_item_id', 'binary_uuid_tag_id'], + ], + ], + ]; + + /** + * records property + * + * @var array + */ + public $records = []; +} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/BinaryUuidItemsFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/BinaryUuidItemsFixture.php new file mode 100644 index 000000000..71cc12472 --- /dev/null +++ b/app/vendor/cakephp/cakephp/tests/Fixture/BinaryUuidItemsFixture.php @@ -0,0 +1,46 @@ + ['type' => 'binaryuuid'], + 'name' => ['type' => 'string', 'null' => false], + 'published' => ['type' => 'boolean', 'null' => false], + '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ]; + + /** + * records property + * + * @var array + */ + public $records = [ + ['id' => '481fc6d0-b920-43e0-a40d-6d1740cf8569', 'published' => true, 'name' => 'Item 1'], + ['id' => '48298a29-81c0-4c26-a7fb-413140cf8569', 'published' => false, 'name' => 'Item 2'], + ['id' => '482b7756-8da0-419a-b21f-27da40cf8569', 'published' => true, 'name' => 'Item 3'], + ]; +} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/BinaryUuidTagsFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/BinaryUuidTagsFixture.php new file mode 100644 index 000000000..9b1c0e6d7 --- /dev/null +++ b/app/vendor/cakephp/cakephp/tests/Fixture/BinaryUuidTagsFixture.php @@ -0,0 +1,44 @@ + ['type' => 'binaryuuid'], + 'name' => ['type' => 'string', 'null' => false], + '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ]; + + /** + * records property + * + * @var array + */ + public $records = [ + ['id' => '481fc6d0-b920-43e0-a40d-111111111111', 'name' => 'Defect'], + ['id' => '48298a29-81c0-4c26-a7fb-222222222222', 'name' => 'Enhancement'], + ]; +} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/BinaryUuiditemsFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/BinaryUuiditemsFixture.php deleted file mode 100644 index 5033abf2c..000000000 --- a/app/vendor/cakephp/cakephp/tests/Fixture/BinaryUuiditemsFixture.php +++ /dev/null @@ -1,46 +0,0 @@ - ['type' => 'binaryuuid'], - 'name' => ['type' => 'string', 'null' => false], - 'published' => ['type' => 'boolean', 'null' => false], - '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], - ]; - - /** - * records property - * - * @var array - */ - public $records = [ - ['id' => '481fc6d0-b920-43e0-a40d-6d1740cf8569', 'published' => true, 'name' => 'Item 1'], - ['id' => '48298a29-81c0-4c26-a7fb-413140cf8569', 'published' => false, 'name' => 'Item 2'], - ['id' => '482b7756-8da0-419a-b21f-27da40cf8569', 'published' => true, 'name' => 'Item 3'], - ]; -} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/GroupsFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/GroupsFixture.php deleted file mode 100644 index 8d7389162..000000000 --- a/app/vendor/cakephp/cakephp/tests/Fixture/GroupsFixture.php +++ /dev/null @@ -1,44 +0,0 @@ - ['type' => 'integer'], - 'title' => ['type' => 'string'], - '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], - ]; - - /** - * records property - * - * @var array - */ - public $records = [ - ['title' => 'foo'], - ['title' => 'bar'], - ]; -} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/GroupsMembersFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/GroupsMembersFixture.php deleted file mode 100644 index bff558e1f..000000000 --- a/app/vendor/cakephp/cakephp/tests/Fixture/GroupsMembersFixture.php +++ /dev/null @@ -1,45 +0,0 @@ - ['type' => 'integer'], - 'group_id' => ['type' => 'integer'], - 'member_id' => ['type' => 'integer'], - '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], - ]; - - /** - * records property - * - * @var array - */ - public $records = [ - ['group_id' => 1, 'member_id' => 1], - ['group_id' => 2, 'member_id' => 1], - ]; -} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/GroupsTranslationsFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/GroupsTranslationsFixture.php deleted file mode 100644 index 5d6496b17..000000000 --- a/app/vendor/cakephp/cakephp/tests/Fixture/GroupsTranslationsFixture.php +++ /dev/null @@ -1,42 +0,0 @@ - ['type' => 'integer'], - 'locale' => ['type' => 'string', 'null' => false], - 'title' => ['type' => 'string'], - '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id', 'locale']]], - ]; - - /** - * records property - * - * @var array - */ - public $records = []; -} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/MembersFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/MembersFixture.php index f76752442..5237859f3 100644 --- a/app/vendor/cakephp/cakephp/tests/Fixture/MembersFixture.php +++ b/app/vendor/cakephp/cakephp/tests/Fixture/MembersFixture.php @@ -28,7 +28,7 @@ class MembersFixture extends TestFixture */ public $fields = [ 'id' => ['type' => 'integer'], - 'group_count' => ['type' => 'integer'], + 'section_count' => ['type' => 'integer'], '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], ]; @@ -38,6 +38,6 @@ class MembersFixture extends TestFixture * @var array */ public $records = [ - ['group_count' => 2], + ['section_count' => 2], ]; } diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/NullableAuthorsFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/NullableAuthorsFixture.php new file mode 100644 index 000000000..050cfb631 --- /dev/null +++ b/app/vendor/cakephp/cakephp/tests/Fixture/NullableAuthorsFixture.php @@ -0,0 +1,41 @@ + ['type' => 'integer'], + 'author_id' => ['type' => 'integer', 'null' => true], + '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ]; + + /** + * records property + * + * @var array + */ + public $records = [ + ['author_id' => 3], + ['author_id' => null], + ]; +} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/OtherArticlesFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/OtherArticlesFixture.php index f5b4a60a1..624e6b57b 100644 --- a/app/vendor/cakephp/cakephp/tests/Fixture/OtherArticlesFixture.php +++ b/app/vendor/cakephp/cakephp/tests/Fixture/OtherArticlesFixture.php @@ -26,31 +26,31 @@ class OtherArticlesFixture implements FixtureInterface { public $table = 'other_articles'; - public function create(ConnectionInterface $db): bool + public function create(ConnectionInterface $connection): bool { return true; } - public function drop(ConnectionInterface $db): bool + public function drop(ConnectionInterface $connection): bool { return true; } - public function insert(ConnectionInterface $db) + public function insert(ConnectionInterface $connection) { } - public function createConstraints(ConnectionInterface $db): bool + public function createConstraints(ConnectionInterface $connection): bool { return true; } - public function dropConstraints(ConnectionInterface $db): bool + public function dropConstraints(ConnectionInterface $connection): bool { return true; } - public function truncate(ConnectionInterface $db): bool + public function truncate(ConnectionInterface $connection): bool { return true; } diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/SectionsFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/SectionsFixture.php new file mode 100644 index 000000000..552670dfd --- /dev/null +++ b/app/vendor/cakephp/cakephp/tests/Fixture/SectionsFixture.php @@ -0,0 +1,44 @@ + ['type' => 'integer'], + 'title' => ['type' => 'string'], + '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ]; + + /** + * records property + * + * @var array + */ + public $records = [ + ['title' => 'foo'], + ['title' => 'bar'], + ]; +} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/SectionsMembersFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/SectionsMembersFixture.php new file mode 100644 index 000000000..6ab9d23fd --- /dev/null +++ b/app/vendor/cakephp/cakephp/tests/Fixture/SectionsMembersFixture.php @@ -0,0 +1,45 @@ + ['type' => 'integer'], + 'section_id' => ['type' => 'integer'], + 'member_id' => ['type' => 'integer'], + '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ]; + + /** + * records property + * + * @var array + */ + public $records = [ + ['section_id' => 1, 'member_id' => 1], + ['section_id' => 2, 'member_id' => 1], + ]; +} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/SectionsTranslationsFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/SectionsTranslationsFixture.php new file mode 100644 index 000000000..7d3ac7093 --- /dev/null +++ b/app/vendor/cakephp/cakephp/tests/Fixture/SectionsTranslationsFixture.php @@ -0,0 +1,42 @@ + ['type' => 'integer'], + 'locale' => ['type' => 'string', 'null' => false], + 'title' => ['type' => 'string'], + '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id', 'locale']]], + ]; + + /** + * records property + * + * @var array + */ + public $records = []; +} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/UniqueAuthorsFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/UniqueAuthorsFixture.php new file mode 100644 index 000000000..59e722cb5 --- /dev/null +++ b/app/vendor/cakephp/cakephp/tests/Fixture/UniqueAuthorsFixture.php @@ -0,0 +1,47 @@ + ['type' => 'integer'], + 'first_author_id' => ['type' => 'integer', 'null' => true], + 'second_author_id' => ['type' => 'integer', 'null' => false], + '_constraints' => [ + 'primary' => ['type' => 'primary', 'columns' => ['id']], + 'nullable_non_nullable_unique' => ['type' => 'unique', 'columns' => ['first_author_id', 'second_author_id']], + ], + ]; + + /** + * records property + * + * @var array + */ + public $records = [ + ['first_author_id' => null, 'second_author_id' => 1], + ]; +} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/UuidItemsFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/UuidItemsFixture.php new file mode 100644 index 000000000..b7456c136 --- /dev/null +++ b/app/vendor/cakephp/cakephp/tests/Fixture/UuidItemsFixture.php @@ -0,0 +1,49 @@ + ['type' => 'uuid'], + 'published' => ['type' => 'boolean', 'null' => false], + 'name' => ['type' => 'string', 'null' => false], + '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ]; + + /** + * records property + * + * @var array + */ + public $records = [ + ['id' => '481fc6d0-b920-43e0-a40d-6d1740cf8569', 'published' => 0, 'name' => 'Item 1'], + ['id' => '48298a29-81c0-4c26-a7fb-413140cf8569', 'published' => 0, 'name' => 'Item 2'], + ['id' => '482b7756-8da0-419a-b21f-27da40cf8569', 'published' => 0, 'name' => 'Item 3'], + ['id' => '482cfd4b-0e7c-4ea3-9582-4cec40cf8569', 'published' => 0, 'name' => 'Item 4'], + ['id' => '4831181b-4020-4983-a29b-131440cf8569', 'published' => 0, 'name' => 'Item 5'], + ['id' => '483798c8-c7cc-430e-8cf9-4fcc40cf8569', 'published' => 0, 'name' => 'Item 6'], + ]; +} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/UuiditemsFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/UuiditemsFixture.php deleted file mode 100644 index 028a92daf..000000000 --- a/app/vendor/cakephp/cakephp/tests/Fixture/UuiditemsFixture.php +++ /dev/null @@ -1,49 +0,0 @@ - ['type' => 'uuid'], - 'published' => ['type' => 'boolean', 'null' => false], - 'name' => ['type' => 'string', 'null' => false], - '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], - ]; - - /** - * records property - * - * @var array - */ - public $records = [ - ['id' => '481fc6d0-b920-43e0-a40d-6d1740cf8569', 'published' => 0, 'name' => 'Item 1'], - ['id' => '48298a29-81c0-4c26-a7fb-413140cf8569', 'published' => 0, 'name' => 'Item 2'], - ['id' => '482b7756-8da0-419a-b21f-27da40cf8569', 'published' => 0, 'name' => 'Item 3'], - ['id' => '482cfd4b-0e7c-4ea3-9582-4cec40cf8569', 'published' => 0, 'name' => 'Item 4'], - ['id' => '4831181b-4020-4983-a29b-131440cf8569', 'published' => 0, 'name' => 'Item 5'], - ['id' => '483798c8-c7cc-430e-8cf9-4fcc40cf8569', 'published' => 0, 'name' => 'Item 6'], - ]; -} diff --git a/app/vendor/cakephp/cakephp/tests/Fixture/UuidportfoliosFixture.php b/app/vendor/cakephp/cakephp/tests/Fixture/UuidportfoliosFixture.php deleted file mode 100644 index ce5f4d9dc..000000000 --- a/app/vendor/cakephp/cakephp/tests/Fixture/UuidportfoliosFixture.php +++ /dev/null @@ -1,44 +0,0 @@ - ['type' => 'uuid'], - 'name' => ['type' => 'string', 'null' => false], - '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], - ]; - - /** - * records property - * - * @var array - */ - public $records = [ - ['id' => '4806e091-6940-4d2b-b227-303740cf8569', 'name' => 'Portfolio 1'], - ['id' => '480af662-eb8c-47d3-886b-230540cf8569', 'name' => 'Portfolio 2'], - ]; -} diff --git a/app/vendor/cakephp/cakephp/tests/bootstrap.php b/app/vendor/cakephp/cakephp/tests/bootstrap.php index 5da9a693e..4f715c2d6 100644 --- a/app/vendor/cakephp/cakephp/tests/bootstrap.php +++ b/app/vendor/cakephp/cakephp/tests/bootstrap.php @@ -17,6 +17,7 @@ use Cake\Chronos\Chronos; use Cake\Core\Configure; use Cake\Datasource\ConnectionManager; +use Cake\Error\Debug\TextFormatter; use Cake\Log\Log; use Cake\Utility\Security; @@ -96,16 +97,17 @@ ]); // Ensure default test connection is defined -if (!getenv('DB_DSN')) { - putenv('DB_DSN=sqlite:///:memory:'); +if (!getenv('DB_URL')) { + putenv('DB_URL=sqlite:///:memory:'); } -ConnectionManager::setConfig('test', ['url' => getenv('DB_DSN')]); -ConnectionManager::setConfig('test_custom_i18n_datasource', ['url' => getenv('DB_DSN')]); +ConnectionManager::setConfig('test', ['url' => getenv('DB_URL')]); +ConnectionManager::setConfig('test_custom_i18n_datasource', ['url' => getenv('DB_URL')]); Configure::write('Session', [ 'defaults' => 'php', ]); +Configure::write('Debugger.exportFormatter', TextFormatter::class); Log::setConfig([ // 'queries' => [ diff --git a/app/vendor/cakephp/debug_kit/README.md b/app/vendor/cakephp/debug_kit/README.md index 53ab6e165..8939aab28 100644 --- a/app/vendor/cakephp/debug_kit/README.md +++ b/app/vendor/cakephp/debug_kit/README.md @@ -1,5 +1,5 @@ # CakePHP DebugKit -[![Build Status](https://secure.travis-ci.org/cakephp/debug_kit.png?branch=master)](http://travis-ci.org/cakephp/debug_kit) +![Build Status](https://github.com/cakephp/debug_kit/actions/workflows/ci.yml/badge.svg?branch=master) [![Coverage Status](https://img.shields.io/codecov/c/github/cakephp/debug_kit.svg?style=flat-square)](https://codecov.io/github/cakephp/debug_kit) [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.txt) [![Total Downloads](https://img.shields.io/packagist/dt/cakephp/cakephp.svg?style=flat-square)](https://packagist.org/packages/cakephp/debug_kit) @@ -15,30 +15,37 @@ configuration data and environment variables hidden. :warning: ## Requirements -The `master` branch has the following requirements: - * SQLite (pdo_sqlite) or another database driver that CakePHP can talk to. By - default DebugKit will use SQLite, if you need to use a different database see - the Database Configuration section below. - -## DebugKit for CakePHP 2.x + default DebugKit will use SQLite, if you need to use a different database see the Database Configuration section in the documentation linked below. -If you want DebugKit for your 2.x application, you can use the latest `2.2.y` tag or the [2.2 branch](https://github.com/cakephp/debug_kit/tree/2.2). +For details and older versions see [version map](https://github.com/cakephp/debug_kit/wiki#version-map). ## Installation * Install the plugin with [Composer](https://getcomposer.org/) from your CakePHP Project's ROOT directory (where the **composer.json** file is located) ```sh -php composer.phar require --dev cakephp/debug_kit:"~3.0" +php composer.phar require --dev cakephp/debug_kit:"^4.0" ``` -* [Load the plugin](http://book.cakephp.org/3.0/en/plugins.html#loading-a-plugin) +* [Load the plugin](https://book.cakephp.org/4/en/plugins.html#loading-a-plugin) ```php // src/Application.php $this->addPlugin('DebugKit'); ``` * Set `'debug' => true,` in `config/app.php`. +## Is DebugKit not working? + +If you don't see a CakePHP icon on the bottom right of your page DebugKit is not be +working correctly. Some common problems are: + +1. Your PHP environment doesn't have SQLite installed. Check your application + logs to confirm if this happening. You can either configure DebugKit to use + a different database, or install the PDO SQLite 3 extension. +2. Your hostname needs to be added to the `DebugKit.safeTld`. If your local + domain isn't a known development environment name, DebugKit will disable + itself to protect a potentially non-development environment. + ## Reporting Issues If you have a problem with DebugKit please open an issue on [GitHub](https://github.com/cakephp/debug_kit/issues). @@ -52,24 +59,7 @@ the project, add features, and send [pull requests](https://help.github.com/articles/using-pull-requests) or open [issues](https://github.com/cakephp/debug_kit/issues). -## Versions - -DebugKit has several releases, each compatible with different releases of -CakePHP. Use the appropriate version by downloading a tag, or checking out the -correct branch. - -* `1.0, 1.1, 1.2` are compatible with CakePHP 1.2.x. These releases of DebugKit - will not work with CakePHP 1.3. You can also use the `1.2-branch` for the mos - recent updates and bugfixes. -* `1.3.0` is compatible with CakePHP 1.3.x only. It will not work with CakePHP - 1.2. You can also use the `1.3` branch to get the most recent updates and - bugfixes. -* `2.2.x` are compatible with CakePHP 2.2.0 and greater. It is a necessary - upgrade for people using CakePHP 2.4 as the naming conventions around loggers - changed in that release. 2.2.x is not actively being developed. -* `3.x` is compatible with CakePHP 3.x and is still under active development. - -# Documentation +## Documentation Documentation for DebugKit can be found in the -[CakePHP documentation](https://book.cakephp.org/debugkit/3.x/en/index.html). +[CakePHP documentation](https://book.cakephp.org/debugkit/4/en/index.html). diff --git a/app/vendor/cakephp/debug_kit/composer.json b/app/vendor/cakephp/debug_kit/composer.json index 34c89f83b..b4edd634f 100644 --- a/app/vendor/cakephp/debug_kit/composer.json +++ b/app/vendor/cakephp/debug_kit/composer.json @@ -24,15 +24,15 @@ }, "require": { "php": ">=7.2", - "cakephp/cakephp": "^4.0", + "cakephp/cakephp": "^4.2.0", "cakephp/chronos": "^2.0", - "composer/composer": "^1.3", + "composer/composer": "^1.3 | ^2.0", "jdorn/sql-formatter": "^1.2" }, "require-dev": { "cakephp/cakephp-codesniffer": "^4.0", "cakephp/authorization": "^2.0", - "phpunit/phpunit": "^8.0" + "phpunit/phpunit": "~8.5.0 | ^9.3" }, "autoload": { "psr-4": { @@ -56,7 +56,7 @@ "cs-fix": "phpcbf --colors --parallel=16 -p src/ tests/", "test": "phpunit", "psalm": "psalm.phar --show-info=false", - "psalm-setup": "cp composer.json composer.backup && composer require --dev psalm/phar:^3.11 && mv composer.backup composer.json" + "psalm-setup": "cp composer.json composer.backup && composer require --dev psalm/phar:^4.3 && mv composer.backup composer.json" }, "prefer-stable": true } diff --git a/app/vendor/cakephp/debug_kit/config/routes.php b/app/vendor/cakephp/debug_kit/config/routes.php index bd5052c90..624414af3 100644 --- a/app/vendor/cakephp/debug_kit/config/routes.php +++ b/app/vendor/cakephp/debug_kit/config/routes.php @@ -15,6 +15,10 @@ '/toolbar/*', ['controller' => 'Requests', 'action' => 'view'] ); + $routes->connect( + '/panels/view/latest-history', + ['controller' => 'Panels', 'action' => 'latestHistory'] + ); $routes->connect( '/panels/view/*', ['controller' => 'Panels', 'action' => 'view'] diff --git a/app/vendor/cakephp/debug_kit/docs/en/index.rst b/app/vendor/cakephp/debug_kit/docs/en/index.rst index aab4ab73f..45cfea1c5 100644 --- a/app/vendor/cakephp/debug_kit/docs/en/index.rst +++ b/app/vendor/cakephp/debug_kit/docs/en/index.rst @@ -51,7 +51,7 @@ Configuration Configure::write('DebugKit.forceEnable', true); * ``DebugKit.ignorePathsPattern`` - Regex pattern (including delimiter) to ignore paths. - DebugKit won't save data for request URLs that match this regex. Defaults to ``null``:: + DebugKit won't save data for request URLs that match this regex. Defaults to ``null``:: // Ignore image paths Configure::write('DebugKit.ignorePathsPattern', '/\.(jpg|png|gif)$/'); @@ -165,7 +165,7 @@ In order to preview emails before sending them, you need to create a preview class that defines the receipient and required template variables for your mailer methods:: - // in src/Mailer/MailPreview/WelcomePreview.php + // in src/Mailer/Preview/WelcomePreview.php namespace App\Mailer\Preview; use DebugKit\Mailer\MailPreview; diff --git a/app/vendor/cakephp/debug_kit/psalm-baseline.xml b/app/vendor/cakephp/debug_kit/psalm-baseline.xml index a2edeb09b..67e93bee8 100644 --- a/app/vendor/cakephp/debug_kit/psalm-baseline.xml +++ b/app/vendor/cakephp/debug_kit/psalm-baseline.xml @@ -1,13 +1,5 @@ - - - - \Cake\Http\Response - - - $this->redirect(['action' => 'index']) - - + $partType @@ -19,16 +11,10 @@ - - string - - $this->_pluginPaths $this->_composerPaths + $this->_pluginPaths - - $name - @@ -40,11 +26,6 @@ self::$_timers - - - $type - - $pos @@ -66,17 +47,12 @@ $request - - - new $class($connection->configName()) - - $pluginName - $vendorName $return['plugins'] $return['vendor'] + $vendorName @@ -87,9 +63,9 @@ $pluginName - $vendorName $return['plugins'] $return['vendor'] + $vendorName @@ -120,20 +96,13 @@ + + !$connection instanceof ConnectionInterface + genericInstances - - - $item - - - $item - $item - $item - - @@ -141,16 +110,21 @@ $row->id - - env('HTTP_HOST') - - - $url === false - - + + makeNeatArray + + + new HtmlFormatter() + + + dump + + + $currentAncestors $value + $values diff --git a/app/vendor/cakephp/debug_kit/psalm.xml b/app/vendor/cakephp/debug_kit/psalm.xml index 02997ec3b..0d36abf97 100644 --- a/app/vendor/cakephp/debug_kit/psalm.xml +++ b/app/vendor/cakephp/debug_kit/psalm.xml @@ -7,7 +7,7 @@ xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" autoloader="tests/bootstrap.php" usePhpDocMethodsWithoutMagicCall="true" - errorBaseline="psalm-baseline.xml" + errorBaseline="./psalm-baseline.xml" > diff --git a/app/vendor/cakephp/debug_kit/src/Cache/Engine/DebugEngine.php b/app/vendor/cakephp/debug_kit/src/Cache/Engine/DebugEngine.php index ec9213e23..f8d279cd7 100644 --- a/app/vendor/cakephp/debug_kit/src/Cache/Engine/DebugEngine.php +++ b/app/vendor/cakephp/debug_kit/src/Cache/Engine/DebugEngine.php @@ -28,7 +28,7 @@ class DebugEngine extends CacheEngine /** * Proxied cache engine config. * - * @var mixed + * @var array */ protected $_config; @@ -159,10 +159,10 @@ public function set($key, $value, $ttl = null): bool /** * @inheritDoc */ - public function setMultiple($data, $ttl = null): bool + public function setMultiple($values, $ttl = null): bool { $start = microtime(true); - $result = $this->_engine->setMultiple($data); + $result = $this->_engine->setMultiple($values); $duration = microtime(true) - $start; $this->track('set'); @@ -196,7 +196,7 @@ public function get($key, $default = null) public function getMultiple($keys, $default = null): iterable { $start = microtime(true); - $result = $this->_engine->getMultiple($keys); + $result = $this->_engine->getMultiple($keys, $default); $duration = microtime(true) - $start; $this->track('get hit'); @@ -253,10 +253,10 @@ public function delete($key): bool /** * @inheritDoc */ - public function deleteMultiple($data): bool + public function deleteMultiple($keys): bool { $start = microtime(true); - $result = $this->_engine->deleteMultiple($data); + $result = $this->_engine->deleteMultiple($keys); $duration = microtime(true) - $start; $this->track('delete'); diff --git a/app/vendor/cakephp/debug_kit/src/Command/BenchmarkCommand.php b/app/vendor/cakephp/debug_kit/src/Command/BenchmarkCommand.php index b20413fe7..90af882f0 100644 --- a/app/vendor/cakephp/debug_kit/src/Command/BenchmarkCommand.php +++ b/app/vendor/cakephp/debug_kit/src/Command/BenchmarkCommand.php @@ -75,7 +75,7 @@ public function execute(Arguments $args, ConsoleIo $io): ?int /** * Prints calculated results * - * @param array $times Array of time values + * @param float[] $times Array of time values * @return void */ protected function _results($times) @@ -89,21 +89,23 @@ protected function _results($times) $this->io->out(''); $this->io->out(Text::insert(__d('debug_kit', 'Requests/Second: :rps req/sec'), [ - 'rps' => round($requests / $duration, 3), + 'rps' => round($requests / $duration, 3), ])); $this->io->out(Text::insert(__d('debug_kit', 'Average request time: :average-time seconds'), [ - 'average-time' => round($duration / $requests, 3), + 'average-time' => round($duration / $requests, 3), ])); $this->io->out(Text::insert(__d('debug_kit', 'Standard deviation of average request time: :std-dev'), [ - 'std-dev' => round($this->_deviation($times, true), 3), + 'std-dev' => round($this->_deviation($times, true), 3), ])); - $this->io->out(Text::insert(__d('debug_kit', 'Longest/shortest request: :longest sec/:shortest sec'), [ + if (!empty($times)) { + $this->io->out(Text::insert(__d('debug_kit', 'Longest/shortest request: :longest sec/:shortest sec'), [ 'longest' => round(max($times), 3), 'shortest' => round(min($times), 3), - ])); + ])); + } $this->io->out(''); } diff --git a/app/vendor/cakephp/debug_kit/src/Controller/DashboardController.php b/app/vendor/cakephp/debug_kit/src/Controller/DashboardController.php index e69b905f5..ca4c7564a 100644 --- a/app/vendor/cakephp/debug_kit/src/Controller/DashboardController.php +++ b/app/vendor/cakephp/debug_kit/src/Controller/DashboardController.php @@ -57,7 +57,7 @@ public function index() /** * Reset SQLite DB. * - * @return \Cake\Http\Response + * @return \Cake\Http\Response|null */ public function reset() { diff --git a/app/vendor/cakephp/debug_kit/src/Controller/PanelsController.php b/app/vendor/cakephp/debug_kit/src/Controller/PanelsController.php index 536bb1091..0f5a0f1a8 100644 --- a/app/vendor/cakephp/debug_kit/src/Controller/PanelsController.php +++ b/app/vendor/cakephp/debug_kit/src/Controller/PanelsController.php @@ -83,11 +83,40 @@ public function index($requestId = null) public function view($id = null) { $this->set('sort', $this->request->getCookie('debugKit_sort')); - $panel = $this->Panels->get($id); + $panel = $this->Panels->get($id, ['contain' => ['Requests']]); $this->set('panel', $panel); // @codingStandardsIgnoreStart $this->set(@unserialize($panel->content)); // @codingStandardsIgnoreEnd } + + /** + * Get Latest request history panel + * + * @return \Cake\Http\Response|null + */ + public function latestHistory() + { + /** @var array{id:string}|null $request */ + $request = $this->Panels->Requests->find('recent') + ->select(['id']) + ->disableHydration() + ->first(); + if (!$request) { + throw new NotFoundException('No requests found'); + } + /** @var array{id:string}|null $historyPanel */ + $historyPanel = $this->Panels->find('byRequest', ['requestId' => $request['id']]) + ->where(['title' => 'History']) + ->select(['id']) + ->first(); + if (!$historyPanel) { + throw new NotFoundException('History Panel from latest request not found'); + } + + return $this->redirect([ + 'action' => 'view', $historyPanel['id'], + ]); + } } diff --git a/app/vendor/cakephp/debug_kit/src/DebugInclude.php b/app/vendor/cakephp/debug_kit/src/DebugInclude.php index 2c1b71061..613f5aac7 100644 --- a/app/vendor/cakephp/debug_kit/src/DebugInclude.php +++ b/app/vendor/cakephp/debug_kit/src/DebugInclude.php @@ -16,6 +16,7 @@ use Cake\Core\Plugin as CorePlugin; use Composer\Json\JsonFile; +use InvalidArgumentException; /** * Contains methods for Providing list of files. @@ -173,6 +174,8 @@ public function niceFileName($file, $type, $name = null) case 'vendor': return str_replace($this->_composerPaths[$name], '', $file); } + + throw new InvalidArgumentException("Type `{$type}` is not supported."); } /** diff --git a/app/vendor/cakephp/debug_kit/src/Log/Engine/DebugKitLog.php b/app/vendor/cakephp/debug_kit/src/Log/Engine/DebugKitLog.php index 4e47cfc22..0b68f455b 100644 --- a/app/vendor/cakephp/debug_kit/src/Log/Engine/DebugKitLog.php +++ b/app/vendor/cakephp/debug_kit/src/Log/Engine/DebugKitLog.php @@ -31,17 +31,17 @@ class DebugKitLog extends BaseLog /** * Captures log messages in memory * - * @param string $type The type of message being logged. + * @param mixed $level The type of message being logged. * @param string $message The message being logged. * @param array $context Additional context data * @return void */ - public function log($type, $message, array $context = []) + public function log($level, $message, array $context = []) { - if (!isset($this->_logs[$type])) { - $this->_logs[$type] = []; + if (!isset($this->_logs[$level])) { + $this->_logs[$level] = []; } - $this->_logs[$type][] = [date('Y-m-d H:i:s'), $this->_format($message)]; + $this->_logs[$level][] = [date('Y-m-d H:i:s'), $this->_format($message)]; } /** diff --git a/app/vendor/cakephp/debug_kit/src/Model/Entity/Panel.php b/app/vendor/cakephp/debug_kit/src/Model/Entity/Panel.php index e395bc591..f862fd959 100644 --- a/app/vendor/cakephp/debug_kit/src/Model/Entity/Panel.php +++ b/app/vendor/cakephp/debug_kit/src/Model/Entity/Panel.php @@ -23,13 +23,15 @@ * @property string $title * @property string $element * @property string $content + * + * @property \DebugKit\Model\Entity\Request $request */ class Panel extends Entity { /** * Some fields should not be in JSON/array exports. * - * @var array + * @var string[] */ protected $_hidden = ['content']; diff --git a/app/vendor/cakephp/debug_kit/src/Model/Table/LazyTableTrait.php b/app/vendor/cakephp/debug_kit/src/Model/Table/LazyTableTrait.php index e8ea8042d..3e7405d84 100644 --- a/app/vendor/cakephp/debug_kit/src/Model/Table/LazyTableTrait.php +++ b/app/vendor/cakephp/debug_kit/src/Model/Table/LazyTableTrait.php @@ -57,9 +57,10 @@ public function ensureTables(array $fixtures) try { foreach ($fixtures as $name) { $class = App::className($name, 'Test/Fixture', 'Fixture'); - if ($class === false) { + if ($class === null) { throw new \RuntimeException("Unknown fixture '$name'."); } + /** @var \Cake\TestSuite\Fixture\TestFixture $fixture */ $fixture = new $class($connection->configName()); if (in_array($fixture->table, $existing)) { diff --git a/app/vendor/cakephp/debug_kit/src/Model/Table/PanelsTable.php b/app/vendor/cakephp/debug_kit/src/Model/Table/PanelsTable.php index 60d6777e3..f70b773fc 100644 --- a/app/vendor/cakephp/debug_kit/src/Model/Table/PanelsTable.php +++ b/app/vendor/cakephp/debug_kit/src/Model/Table/PanelsTable.php @@ -21,6 +21,7 @@ * The panels table collects the information for each panel on * each request. * + * @property \DebugKit\Model\Table\RequestsTable&\Cake\ORM\Association\BelongsTo $Requests * @method \DebugKit\Model\Entity\Panel get($primaryKey, $options = []) * @method \DebugKit\Model\Entity\Panel newEntity($data = null, array $options = []) * @method \DebugKit\Model\Entity\Panel[] newEntities(array $data, array $options = []) diff --git a/app/vendor/cakephp/debug_kit/src/Model/Table/RequestsTable.php b/app/vendor/cakephp/debug_kit/src/Model/Table/RequestsTable.php index 150b3c5ff..80698689a 100644 --- a/app/vendor/cakephp/debug_kit/src/Model/Table/RequestsTable.php +++ b/app/vendor/cakephp/debug_kit/src/Model/Table/RequestsTable.php @@ -112,6 +112,10 @@ public function gc() ->extract('id') ->toArray(); + if (empty($noPurge)) { + return; + } + $query = $this->Panels->query() ->delete() ->where(['request_id NOT IN' => $noPurge]); diff --git a/app/vendor/cakephp/debug_kit/src/Panel/CachePanel.php b/app/vendor/cakephp/debug_kit/src/Panel/CachePanel.php index 70f9f9e91..9c86ca2f0 100644 --- a/app/vendor/cakephp/debug_kit/src/Panel/CachePanel.php +++ b/app/vendor/cakephp/debug_kit/src/Panel/CachePanel.php @@ -56,7 +56,8 @@ public function initialize() } elseif (isset($config['className'])) { Cache::drop($name); $instance = new DebugEngine($config, $name, $this->logger); - Cache::setConfig($name, $instance); + $config['className'] = $instance; + Cache::setConfig($name, $config); } if (isset($instance)) { $this->instances[$name] = $instance; diff --git a/app/vendor/cakephp/debug_kit/src/Panel/MailPanel.php b/app/vendor/cakephp/debug_kit/src/Panel/MailPanel.php index ea25a61be..992c1be7f 100644 --- a/app/vendor/cakephp/debug_kit/src/Panel/MailPanel.php +++ b/app/vendor/cakephp/debug_kit/src/Panel/MailPanel.php @@ -29,7 +29,7 @@ class MailPanel extends DebugPanel /** * The list of emails produced during the request * - * @var \ArrayObject + * @var \ArrayObject|null */ protected $emailLog; diff --git a/app/vendor/cakephp/debug_kit/src/Panel/VariablesPanel.php b/app/vendor/cakephp/debug_kit/src/Panel/VariablesPanel.php index 6eb30d68a..6e8013f8e 100644 --- a/app/vendor/cakephp/debug_kit/src/Panel/VariablesPanel.php +++ b/app/vendor/cakephp/debug_kit/src/Panel/VariablesPanel.php @@ -14,20 +14,12 @@ */ namespace DebugKit\Panel; -use Cake\Collection\Collection; use Cake\Datasource\EntityInterface; +use Cake\Error\Debugger; use Cake\Event\EventInterface; use Cake\Form\Form; -use Cake\ORM\Query; -use Cake\ORM\ResultSet; use Cake\Utility\Hash; -use Closure; use DebugKit\DebugPanel; -use Exception; -use InvalidArgumentException; -use PDO; -use RuntimeException; -use SimpleXMLElement; /** * Provides debug information on the View variables. @@ -97,54 +89,8 @@ public function shutdown(EventInterface $event) /** @var \Cake\Controller\Controller $controller */ $controller = $event->getSubject(); $errors = []; - - $walker = function (&$item) use (&$walker) { - if ( - $item instanceof Collection || - $item instanceof Query || - $item instanceof ResultSet - ) { - try { - $item = $item->toArray(); - } catch (\Cake\Database\Exception $e) { - //Likely issue is unbuffered query; fall back to __debugInfo - $item = $this->_walkDebugInfo($walker, $item); - } catch (RuntimeException $e) { - // Likely a non-select query. - $item = $this->_walkDebugInfo($walker, $item); - } catch (InvalidArgumentException $e) { - $item = $this->_walkDebugInfo($walker, $item); - } - } elseif ( - $item instanceof Closure || - $item instanceof PDO || - $item instanceof SimpleXMLElement - ) { - $item = 'Unserializable object - ' . get_class($item); - } elseif ($item instanceof Exception) { - $item = sprintf( - 'Unserializable object - %s. Error: %s in %s, line %s', - get_class($item), - $item->getMessage(), - $item->getFile(), - $item->getLine() - ); - } elseif (is_object($item)) { - if (method_exists($item, '__debugInfo')) { - // Convert objects into using __debugInfo. - $item = $this->_walkDebugInfo($walker, $item); - } else { - $item = $this->trySerialize($item); - } - } elseif (is_resource($item)) { - $item = sprintf('[%s] %s', get_resource_type($item), $item); - } - - return $this->trySerialize($item); - }; - // Copy so viewVars is not mutated. + $content = []; $vars = $controller->viewBuilder()->getVars(); - array_walk_recursive($vars, $walker); foreach ($vars as $k => $v) { // Get the validation errors for Entity @@ -156,48 +102,15 @@ public function shutdown(EventInterface $event) $errors[$k] = $formErrors; } } + $content[$k] = Debugger::exportVarAsNodes($v, 5); } $this->_data = [ - 'content' => $vars, + 'variables' => $content, 'errors' => $errors, ]; } - /** - * Try to serialize an item, provide an error message if not possible - * - * @param mixed $item Item to check - * @return mixed The $item if it is serializable, error message if not - */ - protected function trySerialize($item) - { - try { - serialize($item); - - return $item; - } catch (\Exception $e) { - if (is_object($item)) { - return __d( - 'debug_kit', - 'Unserializable object - {0}. Error: {1} in {2}, line {3}', - get_class($item), - $e->getMessage(), - $e->getFile(), - $e->getLine() - ); - } - - return __d( - 'debug_kit', - 'Unserializable Error: {1} in {2}, line {3}', - $e->getMessage(), - $e->getFile(), - $e->getLine() - ); - } - } - /** * Get summary data for the variables panel. * @@ -205,10 +118,10 @@ protected function trySerialize($item) */ public function summary() { - if (!isset($this->_data['content'])) { + if (!isset($this->_data['variables'])) { return '0'; } - return (string)count($this->_data['content']); + return (string)count($this->_data['variables']); } } diff --git a/app/vendor/cakephp/debug_kit/src/Plugin.php b/app/vendor/cakephp/debug_kit/src/Plugin.php index 27d4ac077..ba4a8f2b7 100644 --- a/app/vendor/cakephp/debug_kit/src/Plugin.php +++ b/app/vendor/cakephp/debug_kit/src/Plugin.php @@ -60,16 +60,16 @@ public function bootstrap(PluginApplicationInterface $app): void /** * Add middleware for the plugin. * - * @param \Cake\Http\MiddlewareQueue $middleware The middleware queue to update. + * @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to update. * @return \Cake\Http\MiddlewareQueue */ - public function middleware(MiddlewareQueue $middleware): MiddlewareQueue + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { if ($this->service) { - $middleware->insertAt(0, new DebugKitMiddleware($this->service)); + $middlewareQueue->insertAt(0, new DebugKitMiddleware($this->service)); } - return $middleware; + return $middlewareQueue; } /** @@ -104,6 +104,8 @@ function ($code, $message, $file, $line, $context = null) use (&$previousHandler return $previousHandler($code, $message, $file, $line, $context); } + + return false; } ); } diff --git a/app/vendor/cakephp/debug_kit/src/ToolbarService.php b/app/vendor/cakephp/debug_kit/src/ToolbarService.php index fe5c24b6c..668507355 100644 --- a/app/vendor/cakephp/debug_kit/src/ToolbarService.php +++ b/app/vendor/cakephp/debug_kit/src/ToolbarService.php @@ -16,6 +16,7 @@ use Cake\Core\Configure; use Cake\Core\InstanceConfigTrait; use Cake\Core\Plugin as CorePlugin; +use Cake\Datasource\Exception\MissingDatasourceConfigException; use Cake\Event\EventManager; use Cake\Http\ServerRequest; use Cake\Log\Log; @@ -113,48 +114,63 @@ public function isEnabled() } /** - * Returns true if this applications is being executed on a domain with a TLD - * that is commonly associated with a production environment. + * Returns true if this application is being executed on a domain with a TLD + * that is commonly associated with a production environment, or if the IP + * address is not in a private or reserved range. + * + * Private IPv4 = 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16 + * Reserved IPv4 = 0.0.0.0/8, 169.254.0.0/16, 127.0.0.0/8 and 240.0.0.0/4 + * + * Private IPv6 = fc00::/7 + * Reserved IPv6 = ::1/128, ::/128, ::ffff:0:0/96 and fe80::/10 * * @return bool */ protected function isSuspiciouslyProduction() { - $url = parse_url('http://' . env('HTTP_HOST'), PHP_URL_HOST); - if ($url === false) { + $host = parse_url('http://' . env('HTTP_HOST'), PHP_URL_HOST); + if ($host === false) { return false; } - $host = explode('.', $url); - $first = current($host); - $isIP = is_numeric(implode('', $host)); + // IPv6 addresses in URLs are enclosed in brackets. Remove them. + $host = trim($host, '[]'); - if (count($host) === 1) { - return false; + // Check if the host is a private or reserved IPv4/6 address. + $isIp = filter_var($host, FILTER_VALIDATE_IP) !== false; + if ($isIp) { + $flags = FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE; + $isPublicIp = filter_var($host, FILTER_VALIDATE_IP, $flags) !== false; + + return $isPublicIp; } - if ($isIP && in_array($first, ['192', '10', '127'])) { - // Accessing the app by private IP, this is safe + // So it's not an IP address. It must be a domain name. + $parts = explode('.', $host); + if (count($parts) == 1) { return false; } - $tld = end($host); - $safeTopLevelDomains = ['localhost', 'invalid', 'test', 'example', 'local']; - $safeTopLevelDomains = array_merge($safeTopLevelDomains, (array)$this->getConfig('safeTld')); + // Check if the TLD is in the list of safe TLDs. + $tld = end($parts); + $safeTlds = ['localhost', 'invalid', 'test', 'example', 'local']; + $safeTlds = array_merge($safeTlds, (array)$this->getConfig('safeTld')); - if (!in_array($tld, $safeTopLevelDomains, true) && !$this->getConfig('forceEnable')) { - $host = implode('.', $host); - $safeList = implode(', ', $safeTopLevelDomains); + if (in_array($tld, $safeTlds, true)) { + return false; + } + + // Don't log a warning if forceEnable is set. + if (!$this->getConfig('forceEnable')) { + $safeList = implode(', ', $safeTlds); Log::warning( "DebugKit is disabling itself as your host `{$host}` " . "is not in the known safe list of top-level-domains ({$safeList}). " . 'If you would like to force DebugKit on use the `DebugKit.forceEnable` Configure option.' ); - - return true; } - return false; + return true; } /** @@ -214,19 +230,23 @@ public function initializePanels() */ public function saveData(ServerRequest $request, ResponseInterface $response) { - $ignorePathsPattern = $this->getConfig('ignorePathsPattern'); $path = $request->getUri()->getPath(); - $statusCode = $response->getStatusCode(); + $dashboardUrl = '/debug-kit'; + if (strpos($path, 'debug_kit') !== false || strpos($path, 'debug-kit') !== false) { + if (!($path === $dashboardUrl || $path === $dashboardUrl . '/')) { + // internal debug-kit request + return false; + } + // debug-kit dashboard, save request and show toolbar + } + $ignorePathsPattern = $this->getConfig('ignorePathsPattern'); + $statusCode = $response->getStatusCode(); if ( - strpos($path, 'debug_kit') !== false || - strpos($path, 'debug-kit') !== false || - ( - $ignorePathsPattern && - $statusCode >= 200 && - $statusCode <= 299 && - preg_match($ignorePathsPattern, $path) - ) + $ignorePathsPattern && + $statusCode >= 200 && + $statusCode <= 299 && + preg_match($ignorePathsPattern, $path) ) { return false; } @@ -239,9 +259,19 @@ public function saveData(ServerRequest $request, ResponseInterface $response) 'requested_at' => $request->getEnv('REQUEST_TIME'), 'panels' => [], ]; - /** @var \DebugKit\Model\Table\RequestsTable $requests */ - $requests = $this->getTableLocator()->get('DebugKit.Requests'); - $requests->gc(); + try { + /** @var \DebugKit\Model\Table\RequestsTable $requests */ + $requests = $this->getTableLocator()->get('DebugKit.Requests'); + $requests->gc(); + } catch (MissingDatasourceConfigException $e) { + Log::warning( + 'Unable to save request. Check your debug_kit datasource connection ' . + 'or ensure that PDO SQLite extension is enabled.' + ); + Log::warning($e->getMessage()); + + return false; + } $row = $requests->newEntity($data); $row->setNew(true); @@ -268,7 +298,7 @@ public function saveData(ServerRequest $request, ResponseInterface $response) return $requests->save($row); } catch (PDOException $e) { Log::warning('Unable to save request. This is probably due to concurrent requests.'); - Log::warning((string)$e); + Log::warning($e->getMessage()); } return false; diff --git a/app/vendor/cakephp/debug_kit/src/View/Helper/ToolbarHelper.php b/app/vendor/cakephp/debug_kit/src/View/Helper/ToolbarHelper.php index 7c12b3220..df94e531b 100644 --- a/app/vendor/cakephp/debug_kit/src/View/Helper/ToolbarHelper.php +++ b/app/vendor/cakephp/debug_kit/src/View/Helper/ToolbarHelper.php @@ -16,6 +16,11 @@ namespace DebugKit\View\Helper; use ArrayAccess; +use Cake\Error\Debug\ArrayItemNode; +use Cake\Error\Debug\ArrayNode; +use Cake\Error\Debug\HtmlFormatter; +use Cake\Error\Debug\ScalarNode; +use Cake\Error\Debugger; use Cake\View\Helper; use Closure; use Iterator; @@ -55,6 +60,65 @@ public function setSort($sort) $this->sort = $sort; } + /** + * Dump an array of nodes + * + * @param \Cake\Error\Debug\NodeInterface[] $nodes An array of dumped variables. + * Variables should be keyed by the name they had in the view. + * @return string Formatted HTML + */ + public function dumpNodes(array $nodes): string + { + $formatter = new HtmlFormatter(); + if ($this->sort) { + ksort($nodes); + } + $items = []; + foreach ($nodes as $key => $value) { + $items[] = new ArrayItemNode(new ScalarNode('string', $key), $value); + } + $root = new ArrayNode($items); + + return implode([ + '
', + $formatter->dump($root), + '
', + ]); + } + + /** + * Dump the value in $value into an interactive HTML output. + * + * @param mixed $value The value to output. + * @return string Formatted HTML + * @deprecated 4.4.0 + */ + public function dump($value) + { + $debugger = Debugger::getInstance(); + $exportFormatter = $debugger->getConfig('exportFormatter'); + $restore = false; + if ($exportFormatter !== HtmlFormatter::class) { + $restore = true; + $debugger->setConfig('exportFormatter', HtmlFormatter::class); + } + + if ($this->sort && is_array($value)) { + ksort($value); + } + + $contents = Debugger::exportVar($value, 25); + if ($restore) { + $debugger->setConfig('exportFormatter', $exportFormatter); + } + + return implode([ + '
', + $contents, + '
', + ]); + } + /** * Recursively goes through an array and makes neat HTML out of it. * @@ -65,6 +129,7 @@ public function setSort($sort) * @param \SplObjectStorage $currentAncestors Object references found down * the path. * @return string + * @deprecated 4.4.0 Use ToolbarHelper::dump() instead. */ public function makeNeatArray( $values, diff --git a/app/vendor/cakephp/debug_kit/templates/element/cache_panel.php b/app/vendor/cakephp/debug_kit/templates/element/cache_panel.php index 052450a46..4efcf39d3 100644 --- a/app/vendor/cakephp/debug_kit/templates/element/cache_panel.php +++ b/app/vendor/cakephp/debug_kit/templates/element/cache_panel.php @@ -105,7 +105,7 @@ function showMessage(el, text) { var messageEl = el.parent().find('.inline-message'); var xhr = $.ajax({ - headers: {'X-CSRF-TOKEN': 'request->getParam('_csrfToken') ?>'}, + headers: {'X-CSRF-TOKEN': 'request->getAttribute('csrfToken') ?>'}, url: baseUrl, data: {name: name}, dataType: 'json', diff --git a/app/vendor/cakephp/debug_kit/templates/element/environment_panel.php b/app/vendor/cakephp/debug_kit/templates/element/environment_panel.php index 5ce58d8af..cd3a3b5a5 100644 --- a/app/vendor/cakephp/debug_kit/templates/element/environment_panel.php +++ b/app/vendor/cakephp/debug_kit/templates/element/environment_panel.php @@ -39,7 +39,7 @@ $val): ?> - + @@ -64,7 +64,7 @@ $val): ?> - + @@ -140,7 +140,7 @@ $val): ?> - + diff --git a/app/vendor/cakephp/debug_kit/templates/element/history_panel.php b/app/vendor/cakephp/debug_kit/templates/element/history_panel.php index cf2f65759..1ace07940 100644 --- a/app/vendor/cakephp/debug_kit/templates/element/history_panel.php +++ b/app/vendor/cakephp/debug_kit/templates/element/history_panel.php @@ -11,7 +11,6 @@ * @since DebugKit 1.1 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ - /** * @var \DebugKit\View\AjaxView $this * @var \DebugKit\Model\Entity\Panel $panel @@ -20,16 +19,25 @@ ?>
-

+

+ + +

-

+

+ + +

diff --git a/app/vendor/cakephp/debug_kit/templates/element/include_panel.php b/app/vendor/cakephp/debug_kit/templates/element/include_panel.php index ff6935df4..facd38291 100644 --- a/app/vendor/cakephp/debug_kit/templates/element/include_panel.php +++ b/app/vendor/cakephp/debug_kit/templates/element/include_panel.php @@ -28,7 +28,7 @@ } ?>

-Toolbar->makeNeatArray($paths) ?> +Toolbar->dump($paths) ?>

-Toolbar->makeNeatArray(compact('app', 'cake', 'plugins', 'vendor', 'other')) ?> +Toolbar->dump(compact('app', 'cake', 'plugins', 'vendor', 'other')) ?> diff --git a/app/vendor/cakephp/debug_kit/templates/element/packages_panel.php b/app/vendor/cakephp/debug_kit/templates/element/packages_panel.php index 33688ef94..d2d2b1066 100644 --- a/app/vendor/cakephp/debug_kit/templates/element/packages_panel.php +++ b/app/vendor/cakephp/debug_kit/templates/element/packages_panel.php @@ -123,7 +123,7 @@ function buildErrorMessage(response) { showMessage(terminal, buildLoader()); var direct = $('.direct-dependency')[0].checked; var xhr = $.ajax({ - headers: {'X-CSRF-TOKEN': 'request->getParam('_csrfToken') ?>'}, + headers: {'X-CSRF-TOKEN': 'request->getAttribute('csrfToken') ?>'}, url: baseUrl, data: {direct: direct}, dataType: 'json', diff --git a/app/vendor/cakephp/debug_kit/templates/element/request_panel.php b/app/vendor/cakephp/debug_kit/templates/element/request_panel.php index ef090fa0f..6770ceeba 100644 --- a/app/vendor/cakephp/debug_kit/templates/element/request_panel.php +++ b/app/vendor/cakephp/debug_kit/templates/element/request_panel.php @@ -32,14 +32,14 @@

-Toolbar->makeNeatArray($params) ?> +Toolbar->dump($params) ?>

' . __d('debug_kit', 'No post data.') . '

'; else: - echo $this->Toolbar->makeNeatArray($data); + echo $this->Toolbar->dump($data); endif; ?> @@ -48,18 +48,18 @@ if (empty($query)): echo '

' . __d('debug_kit', 'No querystring data.') . '

'; else: - echo $this->Toolbar->makeNeatArray($query); + echo $this->Toolbar->dump($query); endif; ?>

Cookie

- Toolbar->makeNeatArray($cookie) ?> + Toolbar->dump($cookie) ?>

-

Toolbar->makeNeatArray(['template' => $matchedRoute]) ?>

+

Toolbar->dump(['template' => $matchedRoute]) ?>

diff --git a/app/vendor/cakephp/debug_kit/templates/element/session_panel.php b/app/vendor/cakephp/debug_kit/templates/element/session_panel.php index 6490df3f6..c9dcb34cb 100644 --- a/app/vendor/cakephp/debug_kit/templates/element/session_panel.php +++ b/app/vendor/cakephp/debug_kit/templates/element/session_panel.php @@ -17,4 +17,4 @@ * @var array $content */ ?> -Toolbar->makeNeatArray($content); +Toolbar->dump($content); diff --git a/app/vendor/cakephp/debug_kit/templates/element/variables_panel.php b/app/vendor/cakephp/debug_kit/templates/element/variables_panel.php index a15423473..10cc33dfb 100644 --- a/app/vendor/cakephp/debug_kit/templates/element/variables_panel.php +++ b/app/vendor/cakephp/debug_kit/templates/element/variables_panel.php @@ -11,26 +11,34 @@ * @since DebugKit 0.1 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ - /** * @var \DebugKit\View\AjaxView $this * @var string $error * @var bool $sort + * @var array $variables * @var array $content * @var array $errors */ -if (isset($error)): +if (isset($error)) : printf('

%s

', $error); endif; -if (!empty($content)): +// Backwards compatibility for old debug kit data. +if (!empty($content)) : + printf('', $sort ? ' checked="checked"' : '', __d('debug_kit', 'Sort variables by name')); + $this->Toolbar->setSort($sort); + echo $this->Toolbar->dump($content); +endif; + +// New node based data. +if (!empty($variables)) : printf('', $sort ? ' checked="checked"' : '', __d('debug_kit', 'Sort variables by name')); $this->Toolbar->setSort($sort); - echo $this->Toolbar->makeNeatArray($content); + echo $this->Toolbar->dumpNodes($variables); endif; -if (!empty($errors)): +if (!empty($errors)) : echo '

' . __d('debug_kit', 'Validation errors') . '

'; - echo $this->Toolbar->makeNeatArray($errors); + echo $this->Toolbar->dump($errors); endif; diff --git a/app/vendor/cakephp/debug_kit/templates/layout/panel.php b/app/vendor/cakephp/debug_kit/templates/layout/panel.php deleted file mode 100644 index a031e554b..000000000 --- a/app/vendor/cakephp/debug_kit/templates/layout/panel.php +++ /dev/null @@ -1,6 +0,0 @@ -fetch('content'); diff --git a/app/vendor/cakephp/debug_kit/tests/Fixture/PanelsFixture.php b/app/vendor/cakephp/debug_kit/tests/Fixture/PanelsFixture.php index 4ee5163bb..b5a2c494f 100644 --- a/app/vendor/cakephp/debug_kit/tests/Fixture/PanelsFixture.php +++ b/app/vendor/cakephp/debug_kit/tests/Fixture/PanelsFixture.php @@ -10,7 +10,7 @@ * @link http://cakephp.org CakePHP(tm) Project * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -namespace Debugkit\Test\Fixture; +namespace DebugKit\Test\Fixture; use Cake\Database\Schema\TableSchema; use Cake\TestSuite\Fixture\TestFixture; @@ -60,16 +60,7 @@ class PanelsFixture extends TestFixture * * @var array */ - public $records = [ - [ - 'id' => 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', - 'request_id' => 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', - 'panel' => 'DebugKit.Request', - 'title' => 'Request', - 'element' => 'DebugKit.request_panel', - 'content' => 'a:5:{s:6:"params";a:5:{s:6:"plugin";N;s:10:"controller";s:5:"Tasks";s:6:"action";s:3:"add";s:4:"_ext";N;s:4:"pass";a:0:{}}s:5:"query";a:0:{}s:4:"data";a:0:{}s:6:"cookie";a:2:{s:14:"toolbarDisplay";s:4:"show";s:7:"CAKEPHP";s:26:"9pk8sa2ot6pclki9f4iakio560";}s:3:"get";a:0:{}}', - ], - ]; + public $records = []; /** * Constructor diff --git a/app/vendor/cakephp/debug_kit/tests/Fixture/RequestsFixture.php b/app/vendor/cakephp/debug_kit/tests/Fixture/RequestsFixture.php index 87e8c8dd8..ba25e85ef 100644 --- a/app/vendor/cakephp/debug_kit/tests/Fixture/RequestsFixture.php +++ b/app/vendor/cakephp/debug_kit/tests/Fixture/RequestsFixture.php @@ -10,7 +10,7 @@ * @link http://cakephp.org CakePHP(tm) Project * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -namespace Debugkit\Test\Fixture; +namespace DebugKit\Test\Fixture; use Cake\TestSuite\Fixture\TestFixture; @@ -52,15 +52,7 @@ class RequestsFixture extends TestFixture * * @var array */ - public $records = [ - [ - 'id' => 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', - 'url' => '/tasks/add', - 'content_type' => 'text/html', - 'status_code' => 200, - 'requested_at' => '2014-08-21 7:41:12', - ], - ]; + public $records = []; /** * Constructor diff --git a/app/vendor/cakephp/debug_kit/webroot/css/toolbar.css b/app/vendor/cakephp/debug_kit/webroot/css/toolbar.css index 29fc2ffae..f5f1f148f 100644 --- a/app/vendor/cakephp/debug_kit/webroot/css/toolbar.css +++ b/app/vendor/cakephp/debug_kit/webroot/css/toolbar.css @@ -253,7 +253,7 @@ table th { pre, .debug-table td { - font-family: Monaco, Consolas, mono-space; + font-family: Monaco, Consolas, monospace; } /* X column tables have the 2nd+ cell right aligned */ @@ -282,7 +282,7 @@ pre, margin: 10px 0; } .deprecation-list li { - font-family: Monaco, Consolas, mono-space; + font-family: Monaco, Consolas, monospace; } /** @@ -427,7 +427,7 @@ pre, } .history-url { font-size: 14px; - font-family: Monaco, Consolas, mono-space; + font-family: Monaco, Consolas, monospace; } /** diff --git a/app/vendor/cakephp/debug_kit/webroot/js/toolbar-app.js b/app/vendor/cakephp/debug_kit/webroot/js/toolbar-app.js index 3364d0bfe..0b7ba9dc4 100644 --- a/app/vendor/cakephp/debug_kit/webroot/js/toolbar-app.js +++ b/app/vendor/cakephp/debug_kit/webroot/js/toolbar-app.js @@ -150,11 +150,14 @@ Toolbar.prototype = { // Slide panel into place - css transitions. _this.content.addClass('enabled'); contentArea.html(response); + _this.bindVariableSort(); + _this.bindDebugBlock(); + _this.bindNeatArray(); }); }, - bindNeatArray: function() { + bindVariableSort: function() { var sortButton = this.content.find('.neat-array-sort'); var _this = this; sortButton.click(function() { @@ -165,7 +168,16 @@ Toolbar.prototype = { } _this.loadPanel(_this.currentPanel()); }); + }, + + bindDebugBlock: function () { + if (window.__cakeDebugBlockInit) { + window.__cakeDebugBlockInit(); + } + }, + bindNeatArray: function() { + var _this = this; var lists = this.content.find('.depth-0'); lists.find('ul').hide() .parent().addClass('expandable collapsed'); diff --git a/app/vendor/cakephp/twig-view/README.md b/app/vendor/cakephp/twig-view/README.md index 74ce3c511..fca06df62 100644 --- a/app/vendor/cakephp/twig-view/README.md +++ b/app/vendor/cakephp/twig-view/README.md @@ -1,6 +1,6 @@ # TwigView plugin for CakePHP -[![Build Status](https://img.shields.io/travis/com/cakephp/twig-view?style=flat-square)](https://travis-ci.com/cakephp/twig-view) +![Build Status](https://github.com/cakephp/twig-view/actions/workflows/ci.yml/badge.svg?branch=master) [![Latest Stable Version](https://img.shields.io/github/v/release/cakephp/twig-view?sort=semver&style=flat-square)](https://packagist.org/packages/cakephp/twig-view) [![Total Downloads](https://img.shields.io/packagist/dt/cakephp/twig-view?style=flat-square)](https://packagist.org/packages/cakephp/twig-view/stats) [![Code Coverage](https://img.shields.io/coveralls/cakephp/twig-view/master.svg?style=flat-square)](https://coveralls.io/r/cakephp/twig-view?branch=master) @@ -174,6 +174,12 @@ Layout templates are supported and loaded the same way as `View` layouts. ``` +The layout can be set from the template using the `layout` tag. + +```twig +{% layout 'Error' %} +``` + ### Accessing View You can access the `View` instance using the `_view` global. @@ -258,7 +264,7 @@ See `jasny/twig-extensions` for the filters they provide. * `__dn` maps to [`__dn`](https://book.cakephp.org/4/en/core-libraries/internationalization-and-localization.html) * `defaultCurrency` maps to [`Cake\I18n\Number::getDefaultCurrency`](https://book.cakephp.org/4/en/core-libraries/number.html#Cake\\I18n\\Number::getDefaultCurrency) * `uuid` maps to [`Cake\Utility\Text::uuid`](https://book.cakephp.org/4/en/core-libraries/text.html#generating-uuids) -* `time` passed the first and optional second argument into [`new \Cake\I18n\Time()`](https://book.cakephp.org/4/en/core-libraries/time.html#creating-time-instances) -* `timezones` maps to `Cake\I18n\Time::listTimezones` +* `time` passed the first and optional second argument into [`new \Cake\I18n\FrozenTime()`](https://book.cakephp.org/4/en/core-libraries/time.html#creating-time-instances) +* `timezones` maps to `Cake\I18n\FrozenTime::listTimezones` See `jasny/twig-extensions` for the functions they provide. diff --git a/app/vendor/cakephp/twig-view/src/Twig/Extension/TimeExtension.php b/app/vendor/cakephp/twig-view/src/Twig/Extension/TimeExtension.php index d06a81966..190e718ed 100644 --- a/app/vendor/cakephp/twig-view/src/Twig/Extension/TimeExtension.php +++ b/app/vendor/cakephp/twig-view/src/Twig/Extension/TimeExtension.php @@ -18,7 +18,7 @@ namespace Cake\TwigView\Twig\Extension; -use Cake\I18n\Time as CakeTime; +use Cake\I18n\FrozenTime; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; @@ -36,9 +36,9 @@ public function getFunctions(): array { return [ new TwigFunction('time', function ($time = null, $timezone = null) { - return new CakeTime($time, $timezone); + return new FrozenTime($time, $timezone); }), - new TwigFunction('timezones', 'Cake\I18n\Time::listTimezones'), + new TwigFunction('timezones', 'Cake\I18n\FrozenTime::listTimezones'), ]; } } diff --git a/app/vendor/composer/autoload_classmap.php b/app/vendor/composer/autoload_classmap.php index 5a89603f5..71d8d303b 100644 --- a/app/vendor/composer/autoload_classmap.php +++ b/app/vendor/composer/autoload_classmap.php @@ -12,66 +12,79 @@ 'Mobile_Detect' => $vendorDir . '/mobiledetect/mobiledetectlib/Mobile_Detect.php', 'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php', 'PHPUnit\\Exception' => $vendorDir . '/phpunit/phpunit/src/Exception.php', + 'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ActualValueIsNotAnObjectException.php', 'PHPUnit\\Framework\\Assert' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert.php', 'PHPUnit\\Framework\\AssertionFailedError' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/AssertionFailedError.php', 'PHPUnit\\Framework\\CodeCoverageException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/CodeCoverageException.php', - 'PHPUnit\\Framework\\Constraint\\ArrayHasKey' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ArrayHasKey.php', - 'PHPUnit\\Framework\\Constraint\\ArraySubset' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ArraySubset.php', - 'PHPUnit\\Framework\\Constraint\\Attribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Attribute.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotAcceptParameterTypeException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareBoolReturnTypeException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareExactlyOneParameterException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareParameterTypeException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareParameterTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotExistException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotExistException.php', + 'PHPUnit\\Framework\\Constraint\\ArrayHasKey' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Traversable/ArrayHasKey.php', + 'PHPUnit\\Framework\\Constraint\\BinaryOperator' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/BinaryOperator.php', 'PHPUnit\\Framework\\Constraint\\Callback' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Callback.php', - 'PHPUnit\\Framework\\Constraint\\ClassHasAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ClassHasAttribute.php', - 'PHPUnit\\Framework\\Constraint\\ClassHasStaticAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ClassHasStaticAttribute.php', - 'PHPUnit\\Framework\\Constraint\\Composite' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Composite.php', + 'PHPUnit\\Framework\\Constraint\\ClassHasAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Object/ClassHasAttribute.php', + 'PHPUnit\\Framework\\Constraint\\ClassHasStaticAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Object/ClassHasStaticAttribute.php', 'PHPUnit\\Framework\\Constraint\\Constraint' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Constraint.php', - 'PHPUnit\\Framework\\Constraint\\Count' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Count.php', - 'PHPUnit\\Framework\\Constraint\\DirectoryExists' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/DirectoryExists.php', - 'PHPUnit\\Framework\\Constraint\\Exception' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Exception.php', - 'PHPUnit\\Framework\\Constraint\\ExceptionCode' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ExceptionCode.php', - 'PHPUnit\\Framework\\Constraint\\ExceptionMessage' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessage.php', - 'PHPUnit\\Framework\\Constraint\\ExceptionMessageRegularExpression' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessageRegularExpression.php', - 'PHPUnit\\Framework\\Constraint\\FileExists' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/FileExists.php', - 'PHPUnit\\Framework\\Constraint\\GreaterThan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/GreaterThan.php', + 'PHPUnit\\Framework\\Constraint\\Count' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/Count.php', + 'PHPUnit\\Framework\\Constraint\\DirectoryExists' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/DirectoryExists.php', + 'PHPUnit\\Framework\\Constraint\\Exception' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Exception/Exception.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionCode' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionCode.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionMessage' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionMessage.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionMessageRegularExpression' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionMessageRegularExpression.php', + 'PHPUnit\\Framework\\Constraint\\FileExists' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/FileExists.php', + 'PHPUnit\\Framework\\Constraint\\GreaterThan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/GreaterThan.php', 'PHPUnit\\Framework\\Constraint\\IsAnything' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsAnything.php', - 'PHPUnit\\Framework\\Constraint\\IsEmpty' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsEmpty.php', - 'PHPUnit\\Framework\\Constraint\\IsEqual' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsEqual.php', - 'PHPUnit\\Framework\\Constraint\\IsFalse' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsFalse.php', - 'PHPUnit\\Framework\\Constraint\\IsFinite' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsFinite.php', + 'PHPUnit\\Framework\\Constraint\\IsEmpty' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/IsEmpty.php', + 'PHPUnit\\Framework\\Constraint\\IsEqual' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqual.php', + 'PHPUnit\\Framework\\Constraint\\IsEqualCanonicalizing' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualCanonicalizing.php', + 'PHPUnit\\Framework\\Constraint\\IsEqualIgnoringCase' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualIgnoringCase.php', + 'PHPUnit\\Framework\\Constraint\\IsEqualWithDelta' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualWithDelta.php', + 'PHPUnit\\Framework\\Constraint\\IsFalse' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Boolean/IsFalse.php', + 'PHPUnit\\Framework\\Constraint\\IsFinite' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Math/IsFinite.php', 'PHPUnit\\Framework\\Constraint\\IsIdentical' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php', - 'PHPUnit\\Framework\\Constraint\\IsInfinite' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsInfinite.php', - 'PHPUnit\\Framework\\Constraint\\IsInstanceOf' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsInstanceOf.php', - 'PHPUnit\\Framework\\Constraint\\IsJson' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsJson.php', - 'PHPUnit\\Framework\\Constraint\\IsNan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsNan.php', - 'PHPUnit\\Framework\\Constraint\\IsNull' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsNull.php', - 'PHPUnit\\Framework\\Constraint\\IsReadable' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsReadable.php', - 'PHPUnit\\Framework\\Constraint\\IsTrue' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsTrue.php', - 'PHPUnit\\Framework\\Constraint\\IsType' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsType.php', - 'PHPUnit\\Framework\\Constraint\\IsWritable' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsWritable.php', + 'PHPUnit\\Framework\\Constraint\\IsInfinite' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Math/IsInfinite.php', + 'PHPUnit\\Framework\\Constraint\\IsInstanceOf' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Type/IsInstanceOf.php', + 'PHPUnit\\Framework\\Constraint\\IsJson' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/String/IsJson.php', + 'PHPUnit\\Framework\\Constraint\\IsNan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Math/IsNan.php', + 'PHPUnit\\Framework\\Constraint\\IsNull' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Type/IsNull.php', + 'PHPUnit\\Framework\\Constraint\\IsReadable' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/IsReadable.php', + 'PHPUnit\\Framework\\Constraint\\IsTrue' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Boolean/IsTrue.php', + 'PHPUnit\\Framework\\Constraint\\IsType' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Type/IsType.php', + 'PHPUnit\\Framework\\Constraint\\IsWritable' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/IsWritable.php', 'PHPUnit\\Framework\\Constraint\\JsonMatches' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php', 'PHPUnit\\Framework\\Constraint\\JsonMatchesErrorMessageProvider' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/JsonMatchesErrorMessageProvider.php', - 'PHPUnit\\Framework\\Constraint\\LessThan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/LessThan.php', - 'PHPUnit\\Framework\\Constraint\\LogicalAnd' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/LogicalAnd.php', - 'PHPUnit\\Framework\\Constraint\\LogicalNot' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/LogicalNot.php', - 'PHPUnit\\Framework\\Constraint\\LogicalOr' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/LogicalOr.php', - 'PHPUnit\\Framework\\Constraint\\LogicalXor' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/LogicalXor.php', - 'PHPUnit\\Framework\\Constraint\\ObjectHasAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ObjectHasAttribute.php', - 'PHPUnit\\Framework\\Constraint\\RegularExpression' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/RegularExpression.php', - 'PHPUnit\\Framework\\Constraint\\SameSize' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/SameSize.php', - 'PHPUnit\\Framework\\Constraint\\StringContains' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringContains.php', - 'PHPUnit\\Framework\\Constraint\\StringEndsWith' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringEndsWith.php', - 'PHPUnit\\Framework\\Constraint\\StringMatchesFormatDescription' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringMatchesFormatDescription.php', - 'PHPUnit\\Framework\\Constraint\\StringStartsWith' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringStartsWith.php', - 'PHPUnit\\Framework\\Constraint\\TraversableContains' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/TraversableContains.php', - 'PHPUnit\\Framework\\Constraint\\TraversableContainsEqual' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsEqual.php', - 'PHPUnit\\Framework\\Constraint\\TraversableContainsIdentical' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsIdentical.php', - 'PHPUnit\\Framework\\Constraint\\TraversableContainsOnly' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.php', + 'PHPUnit\\Framework\\Constraint\\LessThan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/LessThan.php', + 'PHPUnit\\Framework\\Constraint\\LogicalAnd' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalAnd.php', + 'PHPUnit\\Framework\\Constraint\\LogicalNot' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalNot.php', + 'PHPUnit\\Framework\\Constraint\\LogicalOr' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalOr.php', + 'PHPUnit\\Framework\\Constraint\\LogicalXor' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalXor.php', + 'PHPUnit\\Framework\\Constraint\\ObjectEquals' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectEquals.php', + 'PHPUnit\\Framework\\Constraint\\ObjectHasAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectHasAttribute.php', + 'PHPUnit\\Framework\\Constraint\\Operator' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/Operator.php', + 'PHPUnit\\Framework\\Constraint\\RegularExpression' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/String/RegularExpression.php', + 'PHPUnit\\Framework\\Constraint\\SameSize' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/SameSize.php', + 'PHPUnit\\Framework\\Constraint\\StringContains' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/String/StringContains.php', + 'PHPUnit\\Framework\\Constraint\\StringEndsWith' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/String/StringEndsWith.php', + 'PHPUnit\\Framework\\Constraint\\StringMatchesFormatDescription' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/String/StringMatchesFormatDescription.php', + 'PHPUnit\\Framework\\Constraint\\StringStartsWith' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/String/StringStartsWith.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContains' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContains.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContainsEqual' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsEqual.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContainsIdentical' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsIdentical.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContainsOnly' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsOnly.php', + 'PHPUnit\\Framework\\Constraint\\UnaryOperator' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/UnaryOperator.php', 'PHPUnit\\Framework\\CoveredCodeNotExecutedException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/CoveredCodeNotExecutedException.php', 'PHPUnit\\Framework\\DataProviderTestSuite' => $vendorDir . '/phpunit/phpunit/src/Framework/DataProviderTestSuite.php', + 'PHPUnit\\Framework\\Error' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/Error.php', + 'PHPUnit\\Framework\\ErrorTestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/ErrorTestCase.php', 'PHPUnit\\Framework\\Error\\Deprecated' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Deprecated.php', 'PHPUnit\\Framework\\Error\\Error' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Error.php', 'PHPUnit\\Framework\\Error\\Notice' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Notice.php', 'PHPUnit\\Framework\\Error\\Warning' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Warning.php', 'PHPUnit\\Framework\\Exception' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/Exception.php', 'PHPUnit\\Framework\\ExceptionWrapper' => $vendorDir . '/phpunit/phpunit/src/Framework/ExceptionWrapper.php', + 'PHPUnit\\Framework\\ExecutionOrderDependency' => $vendorDir . '/phpunit/phpunit/src/Framework/ExecutionOrderDependency.php', 'PHPUnit\\Framework\\ExpectationFailedException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ExpectationFailedException.php', 'PHPUnit\\Framework\\IncompleteTest' => $vendorDir . '/phpunit/phpunit/src/Framework/IncompleteTest.php', 'PHPUnit\\Framework\\IncompleteTestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/IncompleteTestCase.php', @@ -86,20 +99,31 @@ 'PHPUnit\\Framework\\MockObject\\Builder\\Identity' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Builder/Identity.php', 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationMocker' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationMocker.php', 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationStubber' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationStubber.php', - 'PHPUnit\\Framework\\MockObject\\Builder\\Match_' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Builder/Match_.php', 'PHPUnit\\Framework\\MockObject\\Builder\\MethodNameMatch' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Builder/MethodNameMatch.php', 'PHPUnit\\Framework\\MockObject\\Builder\\ParametersMatch' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Builder/ParametersMatch.php', 'PHPUnit\\Framework\\MockObject\\Builder\\Stub' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Builder/Stub.php', + 'PHPUnit\\Framework\\MockObject\\CannotUseAddMethodsException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/CannotUseAddMethodsException.php', + 'PHPUnit\\Framework\\MockObject\\CannotUseOnlyMethodsException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/CannotUseOnlyMethodsException.php', + 'PHPUnit\\Framework\\MockObject\\ClassAlreadyExistsException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/ClassAlreadyExistsException.php', + 'PHPUnit\\Framework\\MockObject\\ClassIsFinalException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/ClassIsFinalException.php', 'PHPUnit\\Framework\\MockObject\\ConfigurableMethod' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/ConfigurableMethod.php', 'PHPUnit\\Framework\\MockObject\\ConfigurableMethodsAlreadyInitializedException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php', + 'PHPUnit\\Framework\\MockObject\\DuplicateMethodException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/DuplicateMethodException.php', 'PHPUnit\\Framework\\MockObject\\Exception' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/Exception.php', 'PHPUnit\\Framework\\MockObject\\Generator' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Generator.php', 'PHPUnit\\Framework\\MockObject\\IncompatibleReturnValueException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/IncompatibleReturnValueException.php', + 'PHPUnit\\Framework\\MockObject\\InvalidMethodNameException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/InvalidMethodNameException.php', 'PHPUnit\\Framework\\MockObject\\Invocation' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Invocation.php', 'PHPUnit\\Framework\\MockObject\\InvocationHandler' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/InvocationHandler.php', + 'PHPUnit\\Framework\\MockObject\\MatchBuilderNotFoundException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/MatchBuilderNotFoundException.php', 'PHPUnit\\Framework\\MockObject\\Matcher' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Matcher.php', + 'PHPUnit\\Framework\\MockObject\\MatcherAlreadyRegisteredException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php', 'PHPUnit\\Framework\\MockObject\\Method' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Api/Method.php', + 'PHPUnit\\Framework\\MockObject\\MethodCannotBeConfiguredException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php', + 'PHPUnit\\Framework\\MockObject\\MethodNameAlreadyConfiguredException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MethodNameConstraint' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/MethodNameConstraint.php', + 'PHPUnit\\Framework\\MockObject\\MethodNameNotConfiguredException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodNameNotConfiguredException.php', + 'PHPUnit\\Framework\\MockObject\\MethodParametersAlreadyConfiguredException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MockBuilder' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/MockBuilder.php', 'PHPUnit\\Framework\\MockObject\\MockClass' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/MockClass.php', 'PHPUnit\\Framework\\MockObject\\MockMethod' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/MockMethod.php', @@ -108,6 +132,9 @@ 'PHPUnit\\Framework\\MockObject\\MockTrait' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/MockTrait.php', 'PHPUnit\\Framework\\MockObject\\MockType' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/MockType.php', 'PHPUnit\\Framework\\MockObject\\MockedCloneMethod' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Api/MockedCloneMethod.php', + 'PHPUnit\\Framework\\MockObject\\OriginalConstructorInvocationRequiredException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/OriginalConstructorInvocationRequiredException.php', + 'PHPUnit\\Framework\\MockObject\\ReflectionException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/ReflectionException.php', + 'PHPUnit\\Framework\\MockObject\\ReturnValueNotConfiguredException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\Rule\\AnyInvokedCount' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/AnyInvokedCount.php', 'PHPUnit\\Framework\\MockObject\\Rule\\AnyParameters' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/AnyParameters.php', 'PHPUnit\\Framework\\MockObject\\Rule\\ConsecutiveParameters' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/ConsecutiveParameters.php', @@ -121,6 +148,7 @@ 'PHPUnit\\Framework\\MockObject\\Rule\\Parameters' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/Parameters.php', 'PHPUnit\\Framework\\MockObject\\Rule\\ParametersRule' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/ParametersRule.php', 'PHPUnit\\Framework\\MockObject\\RuntimeException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/RuntimeException.php', + 'PHPUnit\\Framework\\MockObject\\SoapExtensionNotAvailableException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/SoapExtensionNotAvailableException.php', 'PHPUnit\\Framework\\MockObject\\Stub' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ConsecutiveCalls' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub/ConsecutiveCalls.php', 'PHPUnit\\Framework\\MockObject\\Stub\\Exception' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub/Exception.php', @@ -131,11 +159,15 @@ 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnStub' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnStub.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnValueMap' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnValueMap.php', 'PHPUnit\\Framework\\MockObject\\Stub\\Stub' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub/Stub.php', + 'PHPUnit\\Framework\\MockObject\\UnknownClassException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownClassException.php', + 'PHPUnit\\Framework\\MockObject\\UnknownTraitException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownTraitException.php', + 'PHPUnit\\Framework\\MockObject\\UnknownTypeException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownTypeException.php', 'PHPUnit\\Framework\\MockObject\\UnmockedCloneMethod' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Api/UnmockedCloneMethod.php', 'PHPUnit\\Framework\\MockObject\\Verifiable' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Verifiable.php', 'PHPUnit\\Framework\\NoChildTestSuiteException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/NoChildTestSuiteException.php', 'PHPUnit\\Framework\\OutputError' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/OutputError.php', 'PHPUnit\\Framework\\PHPTAssertionFailedError' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/PHPTAssertionFailedError.php', + 'PHPUnit\\Framework\\Reorderable' => $vendorDir . '/phpunit/phpunit/src/Framework/Reorderable.php', 'PHPUnit\\Framework\\RiskyTestError' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/RiskyTestError.php', 'PHPUnit\\Framework\\SelfDescribing' => $vendorDir . '/phpunit/phpunit/src/Framework/SelfDescribing.php', 'PHPUnit\\Framework\\SkippedTest' => $vendorDir . '/phpunit/phpunit/src/Framework/SkippedTest.php', @@ -170,6 +202,8 @@ 'PHPUnit\\Runner\\BeforeTestHook' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/BeforeTestHook.php', 'PHPUnit\\Runner\\DefaultTestResultCache' => $vendorDir . '/phpunit/phpunit/src/Runner/DefaultTestResultCache.php', 'PHPUnit\\Runner\\Exception' => $vendorDir . '/phpunit/phpunit/src/Runner/Exception.php', + 'PHPUnit\\Runner\\Extension\\ExtensionHandler' => $vendorDir . '/phpunit/phpunit/src/Runner/Extension/ExtensionHandler.php', + 'PHPUnit\\Runner\\Extension\\PharLoader' => $vendorDir . '/phpunit/phpunit/src/Runner/Extension/PharLoader.php', 'PHPUnit\\Runner\\Filter\\ExcludeGroupFilterIterator' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/ExcludeGroupFilterIterator.php', 'PHPUnit\\Runner\\Filter\\Factory' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Factory.php', 'PHPUnit\\Runner\\Filter\\GroupFilterIterator' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/GroupFilterIterator.php', @@ -186,23 +220,110 @@ 'PHPUnit\\Runner\\TestSuiteLoader' => $vendorDir . '/phpunit/phpunit/src/Runner/TestSuiteLoader.php', 'PHPUnit\\Runner\\TestSuiteSorter' => $vendorDir . '/phpunit/phpunit/src/Runner/TestSuiteSorter.php', 'PHPUnit\\Runner\\Version' => $vendorDir . '/phpunit/phpunit/src/Runner/Version.php', + 'PHPUnit\\TextUI\\CliArguments\\Builder' => $vendorDir . '/phpunit/phpunit/src/TextUI/CliArguments/Builder.php', + 'PHPUnit\\TextUI\\CliArguments\\Configuration' => $vendorDir . '/phpunit/phpunit/src/TextUI/CliArguments/Configuration.php', + 'PHPUnit\\TextUI\\CliArguments\\Exception' => $vendorDir . '/phpunit/phpunit/src/TextUI/CliArguments/Exception.php', + 'PHPUnit\\TextUI\\CliArguments\\Mapper' => $vendorDir . '/phpunit/phpunit/src/TextUI/CliArguments/Mapper.php', 'PHPUnit\\TextUI\\Command' => $vendorDir . '/phpunit/phpunit/src/TextUI/Command.php', - 'PHPUnit\\TextUI\\Exception' => $vendorDir . '/phpunit/phpunit/src/TextUI/Exception.php', + 'PHPUnit\\TextUI\\DefaultResultPrinter' => $vendorDir . '/phpunit/phpunit/src/TextUI/DefaultResultPrinter.php', + 'PHPUnit\\TextUI\\Exception' => $vendorDir . '/phpunit/phpunit/src/TextUI/Exception/Exception.php', 'PHPUnit\\TextUI\\Help' => $vendorDir . '/phpunit/phpunit/src/TextUI/Help.php', + 'PHPUnit\\TextUI\\ReflectionException' => $vendorDir . '/phpunit/phpunit/src/TextUI/Exception/ReflectionException.php', 'PHPUnit\\TextUI\\ResultPrinter' => $vendorDir . '/phpunit/phpunit/src/TextUI/ResultPrinter.php', + 'PHPUnit\\TextUI\\RuntimeException' => $vendorDir . '/phpunit/phpunit/src/TextUI/Exception/RuntimeException.php', + 'PHPUnit\\TextUI\\TestDirectoryNotFoundException' => $vendorDir . '/phpunit/phpunit/src/TextUI/Exception/TestDirectoryNotFoundException.php', + 'PHPUnit\\TextUI\\TestFileNotFoundException' => $vendorDir . '/phpunit/phpunit/src/TextUI/Exception/TestFileNotFoundException.php', 'PHPUnit\\TextUI\\TestRunner' => $vendorDir . '/phpunit/phpunit/src/TextUI/TestRunner.php', + 'PHPUnit\\TextUI\\TestSuiteMapper' => $vendorDir . '/phpunit/phpunit/src/TextUI/TestSuiteMapper.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\CodeCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\FilterMapper' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\Directory' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Clover' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Cobertura' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Crap4j' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Html' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Php' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Php.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Text' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Xml' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Configuration' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Configuration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Constant' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Constant.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/ConstantCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ConvertLogTypes' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCloverToReport' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCrap4jToReport' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageHtmlToReport' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoveragePhpToReport' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageTextToReport' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageXmlToReport' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Directory' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/Directory.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Exception' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Exception.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Extension' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/Extension.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\File' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/File.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/FileCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Generator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Generator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Group' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/Group.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/GroupCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/GroupCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Groups' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/Groups.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IniSetting' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSetting.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSettingCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IntroduceCoverageElement' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Loader' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Loader.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\LogToReportMigration' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Junit' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Junit.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Logging' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Logging.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TeamCity' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TeamCity.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Html' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Html.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Text' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Xml' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Xml.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Text' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Migration' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/Migration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilder' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationBuilder.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilderException' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationBuilderException.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationException' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationException.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Migrator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromFilterWhitelistToCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromRootToCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistDirectoriesToCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistDirectoriesToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistExcludesToCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\PHPUnit' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/PHPUnit.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Php' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Php.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\PhpHandler' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/PhpHandler.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCacheTokensAttribute' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveEmptyFilter' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveLogTypes' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectory' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectory.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestFile' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFile.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFileCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuite' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuite.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\UpdateSchemaLocationTo93' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Variable' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Variable.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/VariableCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php', 'PHPUnit\\Util\\Annotation\\DocBlock' => $vendorDir . '/phpunit/phpunit/src/Util/Annotation/DocBlock.php', 'PHPUnit\\Util\\Annotation\\Registry' => $vendorDir . '/phpunit/phpunit/src/Util/Annotation/Registry.php', 'PHPUnit\\Util\\Blacklist' => $vendorDir . '/phpunit/phpunit/src/Util/Blacklist.php', 'PHPUnit\\Util\\Color' => $vendorDir . '/phpunit/phpunit/src/Util/Color.php', - 'PHPUnit\\Util\\Configuration' => $vendorDir . '/phpunit/phpunit/src/Util/Configuration.php', - 'PHPUnit\\Util\\ConfigurationGenerator' => $vendorDir . '/phpunit/phpunit/src/Util/ConfigurationGenerator.php', 'PHPUnit\\Util\\ErrorHandler' => $vendorDir . '/phpunit/phpunit/src/Util/ErrorHandler.php', 'PHPUnit\\Util\\Exception' => $vendorDir . '/phpunit/phpunit/src/Util/Exception.php', + 'PHPUnit\\Util\\ExcludeList' => $vendorDir . '/phpunit/phpunit/src/Util/ExcludeList.php', 'PHPUnit\\Util\\FileLoader' => $vendorDir . '/phpunit/phpunit/src/Util/FileLoader.php', 'PHPUnit\\Util\\Filesystem' => $vendorDir . '/phpunit/phpunit/src/Util/Filesystem.php', 'PHPUnit\\Util\\Filter' => $vendorDir . '/phpunit/phpunit/src/Util/Filter.php', - 'PHPUnit\\Util\\Getopt' => $vendorDir . '/phpunit/phpunit/src/Util/Getopt.php', 'PHPUnit\\Util\\GlobalState' => $vendorDir . '/phpunit/phpunit/src/Util/GlobalState.php', 'PHPUnit\\Util\\InvalidDataSetException' => $vendorDir . '/phpunit/phpunit/src/Util/InvalidDataSetException.php', 'PHPUnit\\Util\\Json' => $vendorDir . '/phpunit/phpunit/src/Util/Json.php', @@ -227,184 +348,16 @@ 'PHPUnit\\Util\\XdebugFilterScriptGenerator' => $vendorDir . '/phpunit/phpunit/src/Util/XdebugFilterScriptGenerator.php', 'PHPUnit\\Util\\Xml' => $vendorDir . '/phpunit/phpunit/src/Util/Xml.php', 'PHPUnit\\Util\\XmlTestListRenderer' => $vendorDir . '/phpunit/phpunit/src/Util/XmlTestListRenderer.php', - 'PHP_Token' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', - 'PHP_TokenWithScope' => $vendorDir . '/phpunit/php-token-stream/src/TokenWithScope.php', - 'PHP_TokenWithScopeAndVisibility' => $vendorDir . '/phpunit/php-token-stream/src/TokenWithScopeAndVisibility.php', - 'PHP_Token_ABSTRACT' => $vendorDir . '/phpunit/php-token-stream/src/Abstract.php', - 'PHP_Token_AMPERSAND' => $vendorDir . '/phpunit/php-token-stream/src/Ampersand.php', - 'PHP_Token_AND_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/AndEqual.php', - 'PHP_Token_ARRAY' => $vendorDir . '/phpunit/php-token-stream/src/Array.php', - 'PHP_Token_ARRAY_CAST' => $vendorDir . '/phpunit/php-token-stream/src/ArrayCast.php', - 'PHP_Token_AS' => $vendorDir . '/phpunit/php-token-stream/src/As.php', - 'PHP_Token_AT' => $vendorDir . '/phpunit/php-token-stream/src/At.php', - 'PHP_Token_BACKTICK' => $vendorDir . '/phpunit/php-token-stream/src/Backtick.php', - 'PHP_Token_BAD_CHARACTER' => $vendorDir . '/phpunit/php-token-stream/src/BadCharacter.php', - 'PHP_Token_BOOLEAN_AND' => $vendorDir . '/phpunit/php-token-stream/src/BooleanAnd.php', - 'PHP_Token_BOOLEAN_OR' => $vendorDir . '/phpunit/php-token-stream/src/BooleanOr.php', - 'PHP_Token_BOOL_CAST' => $vendorDir . '/phpunit/php-token-stream/src/BoolCast.php', - 'PHP_Token_BREAK' => $vendorDir . '/phpunit/php-token-stream/src/break.php', - 'PHP_Token_CALLABLE' => $vendorDir . '/phpunit/php-token-stream/src/Callable.php', - 'PHP_Token_CARET' => $vendorDir . '/phpunit/php-token-stream/src/Caret.php', - 'PHP_Token_CASE' => $vendorDir . '/phpunit/php-token-stream/src/Case.php', - 'PHP_Token_CATCH' => $vendorDir . '/phpunit/php-token-stream/src/Catch.php', - 'PHP_Token_CHARACTER' => $vendorDir . '/phpunit/php-token-stream/src/Character.php', - 'PHP_Token_CLASS' => $vendorDir . '/phpunit/php-token-stream/src/Class.php', - 'PHP_Token_CLASS_C' => $vendorDir . '/phpunit/php-token-stream/src/ClassC.php', - 'PHP_Token_CLASS_NAME_CONSTANT' => $vendorDir . '/phpunit/php-token-stream/src/ClassNameConstant.php', - 'PHP_Token_CLONE' => $vendorDir . '/phpunit/php-token-stream/src/Clone.php', - 'PHP_Token_CLOSE_BRACKET' => $vendorDir . '/phpunit/php-token-stream/src/CloseBracket.php', - 'PHP_Token_CLOSE_CURLY' => $vendorDir . '/phpunit/php-token-stream/src/CloseCurly.php', - 'PHP_Token_CLOSE_SQUARE' => $vendorDir . '/phpunit/php-token-stream/src/CloseSquare.php', - 'PHP_Token_CLOSE_TAG' => $vendorDir . '/phpunit/php-token-stream/src/CloseTag.php', - 'PHP_Token_COALESCE' => $vendorDir . '/phpunit/php-token-stream/src/Coalesce.php', - 'PHP_Token_COALESCE_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/CoalesceEqual.php', - 'PHP_Token_COLON' => $vendorDir . '/phpunit/php-token-stream/src/Colon.php', - 'PHP_Token_COMMA' => $vendorDir . '/phpunit/php-token-stream/src/Comma.php', - 'PHP_Token_COMMENT' => $vendorDir . '/phpunit/php-token-stream/src/Comment.php', - 'PHP_Token_CONCAT_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/ConcatEqual.php', - 'PHP_Token_CONST' => $vendorDir . '/phpunit/php-token-stream/src/Const.php', - 'PHP_Token_CONSTANT_ENCAPSED_STRING' => $vendorDir . '/phpunit/php-token-stream/src/ConstantEncapsedString.php', - 'PHP_Token_CONTINUE' => $vendorDir . '/phpunit/php-token-stream/src/Continue.php', - 'PHP_Token_CURLY_OPEN' => $vendorDir . '/phpunit/php-token-stream/src/CurlyOpen.php', - 'PHP_Token_DEC' => $vendorDir . '/phpunit/php-token-stream/src/Dec.php', - 'PHP_Token_DECLARE' => $vendorDir . '/phpunit/php-token-stream/src/Declare.php', - 'PHP_Token_DEFAULT' => $vendorDir . '/phpunit/php-token-stream/src/Default.php', - 'PHP_Token_DIR' => $vendorDir . '/phpunit/php-token-stream/src/Dir.php', - 'PHP_Token_DIV' => $vendorDir . '/phpunit/php-token-stream/src/Div.php', - 'PHP_Token_DIV_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/DivEqual.php', - 'PHP_Token_DNUMBER' => $vendorDir . '/phpunit/php-token-stream/src/DNumber.php', - 'PHP_Token_DO' => $vendorDir . '/phpunit/php-token-stream/src/Do.php', - 'PHP_Token_DOC_COMMENT' => $vendorDir . '/phpunit/php-token-stream/src/DocComment.php', - 'PHP_Token_DOLLAR' => $vendorDir . '/phpunit/php-token-stream/src/Dollar.php', - 'PHP_Token_DOLLAR_OPEN_CURLY_BRACES' => $vendorDir . '/phpunit/php-token-stream/src/DollarOpenCurlyBraces.php', - 'PHP_Token_DOT' => $vendorDir . '/phpunit/php-token-stream/src/Dot.php', - 'PHP_Token_DOUBLE_ARROW' => $vendorDir . '/phpunit/php-token-stream/src/DoubleArrow.php', - 'PHP_Token_DOUBLE_CAST' => $vendorDir . '/phpunit/php-token-stream/src/DoubleCast.php', - 'PHP_Token_DOUBLE_COLON' => $vendorDir . '/phpunit/php-token-stream/src/DoubleColon.php', - 'PHP_Token_DOUBLE_QUOTES' => $vendorDir . '/phpunit/php-token-stream/src/DoubleQuotes.php', - 'PHP_Token_ECHO' => $vendorDir . '/phpunit/php-token-stream/src/Echo.php', - 'PHP_Token_ELLIPSIS' => $vendorDir . '/phpunit/php-token-stream/src/Ellipsis.php', - 'PHP_Token_ELSE' => $vendorDir . '/phpunit/php-token-stream/src/Else.php', - 'PHP_Token_ELSEIF' => $vendorDir . '/phpunit/php-token-stream/src/Elseif.php', - 'PHP_Token_EMPTY' => $vendorDir . '/phpunit/php-token-stream/src/Empty.php', - 'PHP_Token_ENCAPSED_AND_WHITESPACE' => $vendorDir . '/phpunit/php-token-stream/src/EncapsedAndWhitespace.php', - 'PHP_Token_ENDDECLARE' => $vendorDir . '/phpunit/php-token-stream/src/Enddeclare.php', - 'PHP_Token_ENDFOR' => $vendorDir . '/phpunit/php-token-stream/src/Endfor.php', - 'PHP_Token_ENDFOREACH' => $vendorDir . '/phpunit/php-token-stream/src/Endforeach.php', - 'PHP_Token_ENDIF' => $vendorDir . '/phpunit/php-token-stream/src/Endif.php', - 'PHP_Token_ENDSWITCH' => $vendorDir . '/phpunit/php-token-stream/src/Endswitch.php', - 'PHP_Token_ENDWHILE' => $vendorDir . '/phpunit/php-token-stream/src/Endwhile.php', - 'PHP_Token_END_HEREDOC' => $vendorDir . '/phpunit/php-token-stream/src/EndHeredoc.php', - 'PHP_Token_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Equal.php', - 'PHP_Token_EVAL' => $vendorDir . '/phpunit/php-token-stream/src/Eval.php', - 'PHP_Token_EXCLAMATION_MARK' => $vendorDir . '/phpunit/php-token-stream/src/ExclamationMark.php', - 'PHP_Token_EXIT' => $vendorDir . '/phpunit/php-token-stream/src/Exit.php', - 'PHP_Token_EXTENDS' => $vendorDir . '/phpunit/php-token-stream/src/Extends.php', - 'PHP_Token_FILE' => $vendorDir . '/phpunit/php-token-stream/src/File.php', - 'PHP_Token_FINAL' => $vendorDir . '/phpunit/php-token-stream/src/Final.php', - 'PHP_Token_FINALLY' => $vendorDir . '/phpunit/php-token-stream/src/Finally.php', - 'PHP_Token_FN' => $vendorDir . '/phpunit/php-token-stream/src/Fn.php', - 'PHP_Token_FOR' => $vendorDir . '/phpunit/php-token-stream/src/For.php', - 'PHP_Token_FOREACH' => $vendorDir . '/phpunit/php-token-stream/src/Foreach.php', - 'PHP_Token_FUNCTION' => $vendorDir . '/phpunit/php-token-stream/src/Function.php', - 'PHP_Token_FUNC_C' => $vendorDir . '/phpunit/php-token-stream/src/FuncC.php', - 'PHP_Token_GLOBAL' => $vendorDir . '/phpunit/php-token-stream/src/Global.php', - 'PHP_Token_GOTO' => $vendorDir . '/phpunit/php-token-stream/src/Goto.php', - 'PHP_Token_GT' => $vendorDir . '/phpunit/php-token-stream/src/Gt.php', - 'PHP_Token_HALT_COMPILER' => $vendorDir . '/phpunit/php-token-stream/src/HaltCompiler.php', - 'PHP_Token_IF' => $vendorDir . '/phpunit/php-token-stream/src/If.php', - 'PHP_Token_IMPLEMENTS' => $vendorDir . '/phpunit/php-token-stream/src/Implements.php', - 'PHP_Token_INC' => $vendorDir . '/phpunit/php-token-stream/src/Inc.php', - 'PHP_Token_INCLUDE' => $vendorDir . '/phpunit/php-token-stream/src/Include.php', - 'PHP_Token_INCLUDE_ONCE' => $vendorDir . '/phpunit/php-token-stream/src/IncludeOnce.php', - 'PHP_Token_INLINE_HTML' => $vendorDir . '/phpunit/php-token-stream/src/InlineHtml.php', - 'PHP_Token_INSTANCEOF' => $vendorDir . '/phpunit/php-token-stream/src/Instanceof.php', - 'PHP_Token_INSTEADOF' => $vendorDir . '/phpunit/php-token-stream/src/Insteadof.php', - 'PHP_Token_INTERFACE' => $vendorDir . '/phpunit/php-token-stream/src/Interface.php', - 'PHP_Token_INT_CAST' => $vendorDir . '/phpunit/php-token-stream/src/IntCast.php', - 'PHP_Token_ISSET' => $vendorDir . '/phpunit/php-token-stream/src/Isset.php', - 'PHP_Token_IS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/IsEqual.php', - 'PHP_Token_IS_GREATER_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/IsGreaterOrEqual.php', - 'PHP_Token_IS_IDENTICAL' => $vendorDir . '/phpunit/php-token-stream/src/IsIdentical.php', - 'PHP_Token_IS_NOT_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/IsNotEqual.php', - 'PHP_Token_IS_NOT_IDENTICAL' => $vendorDir . '/phpunit/php-token-stream/src/IsNotIdentical.php', - 'PHP_Token_IS_SMALLER_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/IsSmallerOrEqual.php', - 'PHP_Token_Includes' => $vendorDir . '/phpunit/php-token-stream/src/Includes.php', - 'PHP_Token_LINE' => $vendorDir . '/phpunit/php-token-stream/src/Line.php', - 'PHP_Token_LIST' => $vendorDir . '/phpunit/php-token-stream/src/List.php', - 'PHP_Token_LNUMBER' => $vendorDir . '/phpunit/php-token-stream/src/Lnumber.php', - 'PHP_Token_LOGICAL_AND' => $vendorDir . '/phpunit/php-token-stream/src/LogicalAnd.php', - 'PHP_Token_LOGICAL_OR' => $vendorDir . '/phpunit/php-token-stream/src/LogicalOr.php', - 'PHP_Token_LOGICAL_XOR' => $vendorDir . '/phpunit/php-token-stream/src/LogicalXor.php', - 'PHP_Token_LT' => $vendorDir . '/phpunit/php-token-stream/src/Lt.php', - 'PHP_Token_METHOD_C' => $vendorDir . '/phpunit/php-token-stream/src/MethodC.php', - 'PHP_Token_MINUS' => $vendorDir . '/phpunit/php-token-stream/src/Minus.php', - 'PHP_Token_MINUS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/MinusEqual.php', - 'PHP_Token_MOD_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/ModEqual.php', - 'PHP_Token_MULT' => $vendorDir . '/phpunit/php-token-stream/src/Mult.php', - 'PHP_Token_MUL_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/MulEqual.php', - 'PHP_Token_NAMESPACE' => $vendorDir . '/phpunit/php-token-stream/src/Namespace.php', - 'PHP_Token_NAME_FULLY_QUALIFIED' => $vendorDir . '/phpunit/php-token-stream/src/NameFullyQualified.php', - 'PHP_Token_NAME_QUALIFIED' => $vendorDir . '/phpunit/php-token-stream/src/NameQualified.php', - 'PHP_Token_NAME_RELATIVE' => $vendorDir . '/phpunit/php-token-stream/src/NameRelative.php', - 'PHP_Token_NEW' => $vendorDir . '/phpunit/php-token-stream/src/New.php', - 'PHP_Token_NS_C' => $vendorDir . '/phpunit/php-token-stream/src/NsC.php', - 'PHP_Token_NS_SEPARATOR' => $vendorDir . '/phpunit/php-token-stream/src/NsSeparator.php', - 'PHP_Token_NUM_STRING' => $vendorDir . '/phpunit/php-token-stream/src/NumString.php', - 'PHP_Token_OBJECT_CAST' => $vendorDir . '/phpunit/php-token-stream/src/ObjectCast.php', - 'PHP_Token_OBJECT_OPERATOR' => $vendorDir . '/phpunit/php-token-stream/src/ObjectOperator.php', - 'PHP_Token_OPEN_BRACKET' => $vendorDir . '/phpunit/php-token-stream/src/OpenBracket.php', - 'PHP_Token_OPEN_CURLY' => $vendorDir . '/phpunit/php-token-stream/src/OpenCurly.php', - 'PHP_Token_OPEN_SQUARE' => $vendorDir . '/phpunit/php-token-stream/src/OpenSquare.php', - 'PHP_Token_OPEN_TAG' => $vendorDir . '/phpunit/php-token-stream/src/OpenTag.php', - 'PHP_Token_OPEN_TAG_WITH_ECHO' => $vendorDir . '/phpunit/php-token-stream/src/OpenTagWithEcho.php', - 'PHP_Token_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/OrEqual.php', - 'PHP_Token_PAAMAYIM_NEKUDOTAYIM' => $vendorDir . '/phpunit/php-token-stream/src/PaamayimNekudotayim.php', - 'PHP_Token_PERCENT' => $vendorDir . '/phpunit/php-token-stream/src/Percent.php', - 'PHP_Token_PIPE' => $vendorDir . '/phpunit/php-token-stream/src/Pipe.php', - 'PHP_Token_PLUS' => $vendorDir . '/phpunit/php-token-stream/src/Plus.php', - 'PHP_Token_PLUS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/PlusEqual.php', - 'PHP_Token_POW' => $vendorDir . '/phpunit/php-token-stream/src/Pow.php', - 'PHP_Token_POW_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/PowEqual.php', - 'PHP_Token_PRINT' => $vendorDir . '/phpunit/php-token-stream/src/Print.php', - 'PHP_Token_PRIVATE' => $vendorDir . '/phpunit/php-token-stream/src/Private.php', - 'PHP_Token_PROTECTED' => $vendorDir . '/phpunit/php-token-stream/src/Protected.php', - 'PHP_Token_PUBLIC' => $vendorDir . '/phpunit/php-token-stream/src/Public.php', - 'PHP_Token_QUESTION_MARK' => $vendorDir . '/phpunit/php-token-stream/src/QuestionMark.php', - 'PHP_Token_REQUIRE' => $vendorDir . '/phpunit/php-token-stream/src/Require.php', - 'PHP_Token_REQUIRE_ONCE' => $vendorDir . '/phpunit/php-token-stream/src/RequireOnce.php', - 'PHP_Token_RETURN' => $vendorDir . '/phpunit/php-token-stream/src/Return.php', - 'PHP_Token_SEMICOLON' => $vendorDir . '/phpunit/php-token-stream/src/Semicolon.php', - 'PHP_Token_SL' => $vendorDir . '/phpunit/php-token-stream/src/Sl.php', - 'PHP_Token_SL_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/SlEqual.php', - 'PHP_Token_SPACESHIP' => $vendorDir . '/phpunit/php-token-stream/src/Spaceship.php', - 'PHP_Token_SR' => $vendorDir . '/phpunit/php-token-stream/src/Sr.php', - 'PHP_Token_SR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/SrEqual.php', - 'PHP_Token_START_HEREDOC' => $vendorDir . '/phpunit/php-token-stream/src/StartHeredoc.php', - 'PHP_Token_STATIC' => $vendorDir . '/phpunit/php-token-stream/src/Static.php', - 'PHP_Token_STRING' => $vendorDir . '/phpunit/php-token-stream/src/String.php', - 'PHP_Token_STRING_CAST' => $vendorDir . '/phpunit/php-token-stream/src/StringCast.php', - 'PHP_Token_STRING_VARNAME' => $vendorDir . '/phpunit/php-token-stream/src/StringVarname.php', - 'PHP_Token_SWITCH' => $vendorDir . '/phpunit/php-token-stream/src/Switch.php', - 'PHP_Token_Stream' => $vendorDir . '/phpunit/php-token-stream/src/Stream.php', - 'PHP_Token_Stream_CachingFactory' => $vendorDir . '/phpunit/php-token-stream/src/CachingFactory.php', - 'PHP_Token_THROW' => $vendorDir . '/phpunit/php-token-stream/src/Throw.php', - 'PHP_Token_TILDE' => $vendorDir . '/phpunit/php-token-stream/src/Tilde.php', - 'PHP_Token_TRAIT' => $vendorDir . '/phpunit/php-token-stream/src/Trait.php', - 'PHP_Token_TRAIT_C' => $vendorDir . '/phpunit/php-token-stream/src/TraitC.php', - 'PHP_Token_TRY' => $vendorDir . '/phpunit/php-token-stream/src/Try.php', - 'PHP_Token_UNSET' => $vendorDir . '/phpunit/php-token-stream/src/Unset.php', - 'PHP_Token_UNSET_CAST' => $vendorDir . '/phpunit/php-token-stream/src/UnsetCast.php', - 'PHP_Token_USE' => $vendorDir . '/phpunit/php-token-stream/src/Use.php', - 'PHP_Token_USE_FUNCTION' => $vendorDir . '/phpunit/php-token-stream/src/UseFunction.php', - 'PHP_Token_Util' => $vendorDir . '/phpunit/php-token-stream/src/Util.php', - 'PHP_Token_VAR' => $vendorDir . '/phpunit/php-token-stream/src/Var.php', - 'PHP_Token_VARIABLE' => $vendorDir . '/phpunit/php-token-stream/src/Variable.php', - 'PHP_Token_WHILE' => $vendorDir . '/phpunit/php-token-stream/src/While.php', - 'PHP_Token_WHITESPACE' => $vendorDir . '/phpunit/php-token-stream/src/Whitespace.php', - 'PHP_Token_XOR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/XorEqual.php', - 'PHP_Token_YIELD' => $vendorDir . '/phpunit/php-token-stream/src/Yield.php', - 'PHP_Token_YIELD_FROM' => $vendorDir . '/phpunit/php-token-stream/src/YieldFrom.php', + 'PHPUnit\\Util\\Xml\\Exception' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/Exception.php', + 'PHPUnit\\Util\\Xml\\FailedSchemaDetectionResult' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/FailedSchemaDetectionResult.php', + 'PHPUnit\\Util\\Xml\\Loader' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/Loader.php', + 'PHPUnit\\Util\\Xml\\SchemaDetectionResult' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/SchemaDetectionResult.php', + 'PHPUnit\\Util\\Xml\\SchemaDetector' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/SchemaDetector.php', + 'PHPUnit\\Util\\Xml\\SchemaFinder' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/SchemaFinder.php', + 'PHPUnit\\Util\\Xml\\SnapshotNodeList' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/SnapshotNodeList.php', + 'PHPUnit\\Util\\Xml\\SuccessfulSchemaDetectionResult' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/SuccessfulSchemaDetectionResult.php', + 'PHPUnit\\Util\\Xml\\ValidationResult' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/ValidationResult.php', + 'PHPUnit\\Util\\Xml\\Validator' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/Validator.php', 'PharIo\\Manifest\\Application' => $vendorDir . '/phar-io/manifest/src/values/Application.php', 'PharIo\\Manifest\\ApplicationName' => $vendorDir . '/phar-io/manifest/src/values/ApplicationName.php', 'PharIo\\Manifest\\Author' => $vendorDir . '/phar-io/manifest/src/values/Author.php', @@ -475,22 +428,50 @@ 'PharIo\\Version\\VersionConstraintValue' => $vendorDir . '/phar-io/version/src/VersionConstraintValue.php', 'PharIo\\Version\\VersionNumber' => $vendorDir . '/phar-io/version/src/VersionNumber.php', 'ReturnTypeWillChange' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php', + 'SebastianBergmann\\CliParser\\AmbiguousOptionException' => $vendorDir . '/sebastian/cli-parser/src/exceptions/AmbiguousOptionException.php', + 'SebastianBergmann\\CliParser\\Exception' => $vendorDir . '/sebastian/cli-parser/src/exceptions/Exception.php', + 'SebastianBergmann\\CliParser\\OptionDoesNotAllowArgumentException' => $vendorDir . '/sebastian/cli-parser/src/exceptions/OptionDoesNotAllowArgumentException.php', + 'SebastianBergmann\\CliParser\\Parser' => $vendorDir . '/sebastian/cli-parser/src/Parser.php', + 'SebastianBergmann\\CliParser\\RequiredOptionArgumentMissingException' => $vendorDir . '/sebastian/cli-parser/src/exceptions/RequiredOptionArgumentMissingException.php', + 'SebastianBergmann\\CliParser\\UnknownOptionException' => $vendorDir . '/sebastian/cli-parser/src/exceptions/UnknownOptionException.php', + 'SebastianBergmann\\CodeCoverage\\BranchAndPathCoverageNotSupportedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/BranchAndPathCoverageNotSupportedException.php', 'SebastianBergmann\\CodeCoverage\\CodeCoverage' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage.php', - 'SebastianBergmann\\CodeCoverage\\CoveredCodeNotExecutedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php', + 'SebastianBergmann\\CodeCoverage\\CrapIndex' => $vendorDir . '/phpunit/php-code-coverage/src/CrapIndex.php', + 'SebastianBergmann\\CodeCoverage\\DeadCodeDetectionNotSupportedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/DeadCodeDetectionNotSupportedException.php', + 'SebastianBergmann\\CodeCoverage\\Directory' => $vendorDir . '/phpunit/php-code-coverage/src/Directory.php', + 'SebastianBergmann\\CodeCoverage\\DirectoryCouldNotBeCreatedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/DirectoryCouldNotBeCreatedException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Driver' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/Driver.php', - 'SebastianBergmann\\CodeCoverage\\Driver\\PCOV' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/PCOV.php', - 'SebastianBergmann\\CodeCoverage\\Driver\\PHPDBG' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/PHPDBG.php', - 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/Xdebug.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\PathExistsButIsNotDirectoryException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/PathExistsButIsNotDirectoryException.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\PcovDriver' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/PcovDriver.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\PcovNotAvailableException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/PcovNotAvailableException.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgDriver' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/PhpdbgDriver.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgNotAvailableException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/PhpdbgNotAvailableException.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\Selector' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/Selector.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\WriteOperationFailedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/WriteOperationFailedException.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\WrongXdebugVersionException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/WrongXdebugVersionException.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2Driver' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/Xdebug2Driver.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2NotEnabledException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/Xdebug2NotEnabledException.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3Driver' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/Xdebug3Driver.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3NotEnabledException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/Xdebug3NotEnabledException.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\XdebugNotAvailableException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/XdebugNotAvailableException.php', 'SebastianBergmann\\CodeCoverage\\Exception' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/Exception.php', 'SebastianBergmann\\CodeCoverage\\Filter' => $vendorDir . '/phpunit/php-code-coverage/src/Filter.php', 'SebastianBergmann\\CodeCoverage\\InvalidArgumentException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/InvalidArgumentException.php', - 'SebastianBergmann\\CodeCoverage\\MissingCoversAnnotationException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/MissingCoversAnnotationException.php', + 'SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverAvailableException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverAvailableException.php', + 'SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverWithPathCoverageSupportAvailableException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php', 'SebastianBergmann\\CodeCoverage\\Node\\AbstractNode' => $vendorDir . '/phpunit/php-code-coverage/src/Node/AbstractNode.php', 'SebastianBergmann\\CodeCoverage\\Node\\Builder' => $vendorDir . '/phpunit/php-code-coverage/src/Node/Builder.php', 'SebastianBergmann\\CodeCoverage\\Node\\Directory' => $vendorDir . '/phpunit/php-code-coverage/src/Node/Directory.php', 'SebastianBergmann\\CodeCoverage\\Node\\File' => $vendorDir . '/phpunit/php-code-coverage/src/Node/File.php', 'SebastianBergmann\\CodeCoverage\\Node\\Iterator' => $vendorDir . '/phpunit/php-code-coverage/src/Node/Iterator.php', + 'SebastianBergmann\\CodeCoverage\\ParserException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/ParserException.php', + 'SebastianBergmann\\CodeCoverage\\Percentage' => $vendorDir . '/phpunit/php-code-coverage/src/Percentage.php', + 'SebastianBergmann\\CodeCoverage\\ProcessedCodeCoverageData' => $vendorDir . '/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php', + 'SebastianBergmann\\CodeCoverage\\RawCodeCoverageData' => $vendorDir . '/phpunit/php-code-coverage/src/RawCodeCoverageData.php', + 'SebastianBergmann\\CodeCoverage\\ReflectionException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/ReflectionException.php', + 'SebastianBergmann\\CodeCoverage\\ReportAlreadyFinalizedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/ReportAlreadyFinalizedException.php', 'SebastianBergmann\\CodeCoverage\\Report\\Clover' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Clover.php', + 'SebastianBergmann\\CodeCoverage\\Report\\Cobertura' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Cobertura.php', 'SebastianBergmann\\CodeCoverage\\Report\\Crap4j' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Crap4j.php', 'SebastianBergmann\\CodeCoverage\\Report\\Html\\Dashboard' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.php', 'SebastianBergmann\\CodeCoverage\\Report\\Html\\Directory' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php', @@ -512,26 +493,63 @@ 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Tests' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Tests.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Totals' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Totals.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Unit' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Unit.php', - 'SebastianBergmann\\CodeCoverage\\RuntimeException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/RuntimeException.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysisCacheNotConfiguredException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/StaticAnalysisCacheNotConfiguredException.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\Cache' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/Cache.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CacheWarmer' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/CacheWarmer.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingCoveredFileAnalyser' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/CachingCoveredFileAnalyser.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingUncoveredFileAnalyser' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/CachingUncoveredFileAnalyser.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CodeUnitFindingVisitor' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/CodeUnitFindingVisitor.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CoveredFileAnalyser' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/CoveredFileAnalyser.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ExecutableLinesFindingVisitor' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\IgnoredLinesFindingVisitor' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/IgnoredLinesFindingVisitor.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingCoveredFileAnalyser' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/ParsingCoveredFileAnalyser.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingUncoveredFileAnalyser' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/ParsingUncoveredFileAnalyser.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\UncoveredFileAnalyser' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/UncoveredFileAnalyser.php', + 'SebastianBergmann\\CodeCoverage\\TestIdMissingException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/TestIdMissingException.php', 'SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php', - 'SebastianBergmann\\CodeCoverage\\Util' => $vendorDir . '/phpunit/php-code-coverage/src/Util.php', 'SebastianBergmann\\CodeCoverage\\Version' => $vendorDir . '/phpunit/php-code-coverage/src/Version.php', + 'SebastianBergmann\\CodeCoverage\\XmlException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/XmlException.php', 'SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => $vendorDir . '/sebastian/code-unit-reverse-lookup/src/Wizard.php', + 'SebastianBergmann\\CodeUnit\\ClassMethodUnit' => $vendorDir . '/sebastian/code-unit/src/ClassMethodUnit.php', + 'SebastianBergmann\\CodeUnit\\ClassUnit' => $vendorDir . '/sebastian/code-unit/src/ClassUnit.php', + 'SebastianBergmann\\CodeUnit\\CodeUnit' => $vendorDir . '/sebastian/code-unit/src/CodeUnit.php', + 'SebastianBergmann\\CodeUnit\\CodeUnitCollection' => $vendorDir . '/sebastian/code-unit/src/CodeUnitCollection.php', + 'SebastianBergmann\\CodeUnit\\CodeUnitCollectionIterator' => $vendorDir . '/sebastian/code-unit/src/CodeUnitCollectionIterator.php', + 'SebastianBergmann\\CodeUnit\\Exception' => $vendorDir . '/sebastian/code-unit/src/exceptions/Exception.php', + 'SebastianBergmann\\CodeUnit\\FunctionUnit' => $vendorDir . '/sebastian/code-unit/src/FunctionUnit.php', + 'SebastianBergmann\\CodeUnit\\InterfaceMethodUnit' => $vendorDir . '/sebastian/code-unit/src/InterfaceMethodUnit.php', + 'SebastianBergmann\\CodeUnit\\InterfaceUnit' => $vendorDir . '/sebastian/code-unit/src/InterfaceUnit.php', + 'SebastianBergmann\\CodeUnit\\InvalidCodeUnitException' => $vendorDir . '/sebastian/code-unit/src/exceptions/InvalidCodeUnitException.php', + 'SebastianBergmann\\CodeUnit\\Mapper' => $vendorDir . '/sebastian/code-unit/src/Mapper.php', + 'SebastianBergmann\\CodeUnit\\NoTraitException' => $vendorDir . '/sebastian/code-unit/src/exceptions/NoTraitException.php', + 'SebastianBergmann\\CodeUnit\\ReflectionException' => $vendorDir . '/sebastian/code-unit/src/exceptions/ReflectionException.php', + 'SebastianBergmann\\CodeUnit\\TraitMethodUnit' => $vendorDir . '/sebastian/code-unit/src/TraitMethodUnit.php', + 'SebastianBergmann\\CodeUnit\\TraitUnit' => $vendorDir . '/sebastian/code-unit/src/TraitUnit.php', 'SebastianBergmann\\Comparator\\ArrayComparator' => $vendorDir . '/sebastian/comparator/src/ArrayComparator.php', 'SebastianBergmann\\Comparator\\Comparator' => $vendorDir . '/sebastian/comparator/src/Comparator.php', 'SebastianBergmann\\Comparator\\ComparisonFailure' => $vendorDir . '/sebastian/comparator/src/ComparisonFailure.php', 'SebastianBergmann\\Comparator\\DOMNodeComparator' => $vendorDir . '/sebastian/comparator/src/DOMNodeComparator.php', 'SebastianBergmann\\Comparator\\DateTimeComparator' => $vendorDir . '/sebastian/comparator/src/DateTimeComparator.php', 'SebastianBergmann\\Comparator\\DoubleComparator' => $vendorDir . '/sebastian/comparator/src/DoubleComparator.php', + 'SebastianBergmann\\Comparator\\Exception' => $vendorDir . '/sebastian/comparator/src/exceptions/Exception.php', 'SebastianBergmann\\Comparator\\ExceptionComparator' => $vendorDir . '/sebastian/comparator/src/ExceptionComparator.php', 'SebastianBergmann\\Comparator\\Factory' => $vendorDir . '/sebastian/comparator/src/Factory.php', 'SebastianBergmann\\Comparator\\MockObjectComparator' => $vendorDir . '/sebastian/comparator/src/MockObjectComparator.php', 'SebastianBergmann\\Comparator\\NumericComparator' => $vendorDir . '/sebastian/comparator/src/NumericComparator.php', 'SebastianBergmann\\Comparator\\ObjectComparator' => $vendorDir . '/sebastian/comparator/src/ObjectComparator.php', 'SebastianBergmann\\Comparator\\ResourceComparator' => $vendorDir . '/sebastian/comparator/src/ResourceComparator.php', + 'SebastianBergmann\\Comparator\\RuntimeException' => $vendorDir . '/sebastian/comparator/src/exceptions/RuntimeException.php', 'SebastianBergmann\\Comparator\\ScalarComparator' => $vendorDir . '/sebastian/comparator/src/ScalarComparator.php', 'SebastianBergmann\\Comparator\\SplObjectStorageComparator' => $vendorDir . '/sebastian/comparator/src/SplObjectStorageComparator.php', 'SebastianBergmann\\Comparator\\TypeComparator' => $vendorDir . '/sebastian/comparator/src/TypeComparator.php', + 'SebastianBergmann\\Complexity\\Calculator' => $vendorDir . '/sebastian/complexity/src/Calculator.php', + 'SebastianBergmann\\Complexity\\Complexity' => $vendorDir . '/sebastian/complexity/src/Complexity/Complexity.php', + 'SebastianBergmann\\Complexity\\ComplexityCalculatingVisitor' => $vendorDir . '/sebastian/complexity/src/Visitor/ComplexityCalculatingVisitor.php', + 'SebastianBergmann\\Complexity\\ComplexityCollection' => $vendorDir . '/sebastian/complexity/src/Complexity/ComplexityCollection.php', + 'SebastianBergmann\\Complexity\\ComplexityCollectionIterator' => $vendorDir . '/sebastian/complexity/src/Complexity/ComplexityCollectionIterator.php', + 'SebastianBergmann\\Complexity\\CyclomaticComplexityCalculatingVisitor' => $vendorDir . '/sebastian/complexity/src/Visitor/CyclomaticComplexityCalculatingVisitor.php', + 'SebastianBergmann\\Complexity\\Exception' => $vendorDir . '/sebastian/complexity/src/Exception/Exception.php', + 'SebastianBergmann\\Complexity\\RuntimeException' => $vendorDir . '/sebastian/complexity/src/Exception/RuntimeException.php', 'SebastianBergmann\\Diff\\Chunk' => $vendorDir . '/sebastian/diff/src/Chunk.php', 'SebastianBergmann\\Diff\\ConfigurationException' => $vendorDir . '/sebastian/diff/src/Exception/ConfigurationException.php', 'SebastianBergmann\\Diff\\Diff' => $vendorDir . '/sebastian/diff/src/Diff.php', @@ -555,12 +573,23 @@ 'SebastianBergmann\\FileIterator\\Facade' => $vendorDir . '/phpunit/php-file-iterator/src/Facade.php', 'SebastianBergmann\\FileIterator\\Factory' => $vendorDir . '/phpunit/php-file-iterator/src/Factory.php', 'SebastianBergmann\\FileIterator\\Iterator' => $vendorDir . '/phpunit/php-file-iterator/src/Iterator.php', - 'SebastianBergmann\\GlobalState\\Blacklist' => $vendorDir . '/sebastian/global-state/src/Blacklist.php', 'SebastianBergmann\\GlobalState\\CodeExporter' => $vendorDir . '/sebastian/global-state/src/CodeExporter.php', 'SebastianBergmann\\GlobalState\\Exception' => $vendorDir . '/sebastian/global-state/src/exceptions/Exception.php', + 'SebastianBergmann\\GlobalState\\ExcludeList' => $vendorDir . '/sebastian/global-state/src/ExcludeList.php', 'SebastianBergmann\\GlobalState\\Restorer' => $vendorDir . '/sebastian/global-state/src/Restorer.php', 'SebastianBergmann\\GlobalState\\RuntimeException' => $vendorDir . '/sebastian/global-state/src/exceptions/RuntimeException.php', 'SebastianBergmann\\GlobalState\\Snapshot' => $vendorDir . '/sebastian/global-state/src/Snapshot.php', + 'SebastianBergmann\\Invoker\\Exception' => $vendorDir . '/phpunit/php-invoker/src/exceptions/Exception.php', + 'SebastianBergmann\\Invoker\\Invoker' => $vendorDir . '/phpunit/php-invoker/src/Invoker.php', + 'SebastianBergmann\\Invoker\\ProcessControlExtensionNotLoadedException' => $vendorDir . '/phpunit/php-invoker/src/exceptions/ProcessControlExtensionNotLoadedException.php', + 'SebastianBergmann\\Invoker\\TimeoutException' => $vendorDir . '/phpunit/php-invoker/src/exceptions/TimeoutException.php', + 'SebastianBergmann\\LinesOfCode\\Counter' => $vendorDir . '/sebastian/lines-of-code/src/Counter.php', + 'SebastianBergmann\\LinesOfCode\\Exception' => $vendorDir . '/sebastian/lines-of-code/src/Exception/Exception.php', + 'SebastianBergmann\\LinesOfCode\\IllogicalValuesException' => $vendorDir . '/sebastian/lines-of-code/src/Exception/IllogicalValuesException.php', + 'SebastianBergmann\\LinesOfCode\\LineCountingVisitor' => $vendorDir . '/sebastian/lines-of-code/src/LineCountingVisitor.php', + 'SebastianBergmann\\LinesOfCode\\LinesOfCode' => $vendorDir . '/sebastian/lines-of-code/src/LinesOfCode.php', + 'SebastianBergmann\\LinesOfCode\\NegativeValueException' => $vendorDir . '/sebastian/lines-of-code/src/Exception/NegativeValueException.php', + 'SebastianBergmann\\LinesOfCode\\RuntimeException' => $vendorDir . '/sebastian/lines-of-code/src/Exception/RuntimeException.php', 'SebastianBergmann\\ObjectEnumerator\\Enumerator' => $vendorDir . '/sebastian/object-enumerator/src/Enumerator.php', 'SebastianBergmann\\ObjectEnumerator\\Exception' => $vendorDir . '/sebastian/object-enumerator/src/Exception.php', 'SebastianBergmann\\ObjectEnumerator\\InvalidArgumentException' => $vendorDir . '/sebastian/object-enumerator/src/InvalidArgumentException.php', @@ -571,25 +600,37 @@ 'SebastianBergmann\\RecursionContext\\Exception' => $vendorDir . '/sebastian/recursion-context/src/Exception.php', 'SebastianBergmann\\RecursionContext\\InvalidArgumentException' => $vendorDir . '/sebastian/recursion-context/src/InvalidArgumentException.php', 'SebastianBergmann\\ResourceOperations\\ResourceOperations' => $vendorDir . '/sebastian/resource-operations/src/ResourceOperations.php', - 'SebastianBergmann\\Timer\\Exception' => $vendorDir . '/phpunit/php-timer/src/Exception.php', - 'SebastianBergmann\\Timer\\RuntimeException' => $vendorDir . '/phpunit/php-timer/src/RuntimeException.php', + 'SebastianBergmann\\Template\\Exception' => $vendorDir . '/phpunit/php-text-template/src/exceptions/Exception.php', + 'SebastianBergmann\\Template\\InvalidArgumentException' => $vendorDir . '/phpunit/php-text-template/src/exceptions/InvalidArgumentException.php', + 'SebastianBergmann\\Template\\RuntimeException' => $vendorDir . '/phpunit/php-text-template/src/exceptions/RuntimeException.php', + 'SebastianBergmann\\Template\\Template' => $vendorDir . '/phpunit/php-text-template/src/Template.php', + 'SebastianBergmann\\Timer\\Duration' => $vendorDir . '/phpunit/php-timer/src/Duration.php', + 'SebastianBergmann\\Timer\\Exception' => $vendorDir . '/phpunit/php-timer/src/exceptions/Exception.php', + 'SebastianBergmann\\Timer\\NoActiveTimerException' => $vendorDir . '/phpunit/php-timer/src/exceptions/NoActiveTimerException.php', + 'SebastianBergmann\\Timer\\ResourceUsageFormatter' => $vendorDir . '/phpunit/php-timer/src/ResourceUsageFormatter.php', + 'SebastianBergmann\\Timer\\TimeSinceStartOfRequestNotAvailableException' => $vendorDir . '/phpunit/php-timer/src/exceptions/TimeSinceStartOfRequestNotAvailableException.php', 'SebastianBergmann\\Timer\\Timer' => $vendorDir . '/phpunit/php-timer/src/Timer.php', 'SebastianBergmann\\Type\\CallableType' => $vendorDir . '/sebastian/type/src/CallableType.php', 'SebastianBergmann\\Type\\Exception' => $vendorDir . '/sebastian/type/src/exception/Exception.php', + 'SebastianBergmann\\Type\\FalseType' => $vendorDir . '/sebastian/type/src/FalseType.php', 'SebastianBergmann\\Type\\GenericObjectType' => $vendorDir . '/sebastian/type/src/GenericObjectType.php', 'SebastianBergmann\\Type\\IterableType' => $vendorDir . '/sebastian/type/src/IterableType.php', + 'SebastianBergmann\\Type\\LogicException' => $vendorDir . '/sebastian/type/src/exception/LogicException.php', + 'SebastianBergmann\\Type\\MixedType' => $vendorDir . '/sebastian/type/src/MixedType.php', 'SebastianBergmann\\Type\\NullType' => $vendorDir . '/sebastian/type/src/NullType.php', 'SebastianBergmann\\Type\\ObjectType' => $vendorDir . '/sebastian/type/src/ObjectType.php', + 'SebastianBergmann\\Type\\ReflectionMapper' => $vendorDir . '/sebastian/type/src/ReflectionMapper.php', 'SebastianBergmann\\Type\\RuntimeException' => $vendorDir . '/sebastian/type/src/exception/RuntimeException.php', 'SebastianBergmann\\Type\\SimpleType' => $vendorDir . '/sebastian/type/src/SimpleType.php', + 'SebastianBergmann\\Type\\StaticType' => $vendorDir . '/sebastian/type/src/StaticType.php', 'SebastianBergmann\\Type\\Type' => $vendorDir . '/sebastian/type/src/Type.php', 'SebastianBergmann\\Type\\TypeName' => $vendorDir . '/sebastian/type/src/TypeName.php', + 'SebastianBergmann\\Type\\UnionType' => $vendorDir . '/sebastian/type/src/UnionType.php', 'SebastianBergmann\\Type\\UnknownType' => $vendorDir . '/sebastian/type/src/UnknownType.php', 'SebastianBergmann\\Type\\VoidType' => $vendorDir . '/sebastian/type/src/VoidType.php', 'SebastianBergmann\\Version' => $vendorDir . '/sebastian/version/src/Version.php', 'SqlFormatter' => $vendorDir . '/jdorn/sql-formatter/lib/SqlFormatter.php', 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', - 'Text_Template' => $vendorDir . '/phpunit/php-text-template/src/Template.php', 'TheSeer\\Tokenizer\\Exception' => $vendorDir . '/theseer/tokenizer/src/Exception.php', 'TheSeer\\Tokenizer\\NamespaceUri' => $vendorDir . '/theseer/tokenizer/src/NamespaceUri.php', 'TheSeer\\Tokenizer\\NamespaceUriException' => $vendorDir . '/theseer/tokenizer/src/NamespaceUriException.php', diff --git a/app/vendor/composer/autoload_files.php b/app/vendor/composer/autoload_files.php index 00304d13e..8570dfe64 100644 --- a/app/vendor/composer/autoload_files.php +++ b/app/vendor/composer/autoload_files.php @@ -7,10 +7,17 @@ return array( '9b38cf48e83f5d8f60375221cd213eee' => $vendorDir . '/phpstan/phpstan/bootstrap.php', + 'ec07570ca5a812141189b1fa81503674' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert/Functions.php', 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', + '23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php', + '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php', + 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', + '0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php', + 'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php', + 'ad155f8f1cf0d418fe49e248db8c661b' => $vendorDir . '/react/promise/src/functions_include.php', '07d7f1a47144818725fd8d91a907ac57' => $vendorDir . '/laminas/laminas-diactoros/src/functions/create_uploaded_file.php', 'da94ac5d3ca7d2dbab84ce561ce72bfd' => $vendorDir . '/laminas/laminas-diactoros/src/functions/marshal_headers_from_sapi.php', '3d97c8dcdfba8cb85d3b34f116bb248b' => $vendorDir . '/laminas/laminas-diactoros/src/functions/marshal_method_from_sapi.php', @@ -27,18 +34,14 @@ 'cc8e14526dc240491e17a838cb78508c' => $vendorDir . '/laminas/laminas-diactoros/src/functions/normalize_server.legacy.php', '786bf90caabc9e09b6ad4cc5ca8f0e30' => $vendorDir . '/laminas/laminas-diactoros/src/functions/normalize_uploaded_files.legacy.php', '751a5a3f463e4be759be31748b61737c' => $vendorDir . '/laminas/laminas-diactoros/src/functions/parse_cookie_header.legacy.php', - '23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php', '34122c0574b76bf21c9a8db62b5b9cf3' => $vendorDir . '/cakephp/chronos/src/carbon_compat.php', - '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php', - 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', - '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', - '0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php', - 'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php', '7e9bd612cc444b3eed788ebbe46263a0' => $vendorDir . '/laminas/laminas-zendframework-bridge/src/autoload.php', 'c720f792236cd163ece8049879166850' => $vendorDir . '/cakephp/cakephp/src/Core/functions.php', 'ede59e3a405fb689cd1cebb7bb1db3fb' => $vendorDir . '/cakephp/cakephp/src/Collection/functions.php', '90236b492da7ca2983a2ad6e33e4152e' => $vendorDir . '/cakephp/cakephp/src/I18n/functions.php', + '2cb76c05856dfb60ada40ef54138d49a' => $vendorDir . '/cakephp/cakephp/src/Routing/functions.php', 'b1fc73705e1bec51cd2b20a32cf1c60a' => $vendorDir . '/cakephp/cakephp/src/Utility/bootstrap.php', + '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', '6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', 'bf9f5270ae66ac6fa0290b4bf47867b7' => $vendorDir . '/adodb/adodb-php/adodb.inc.php', '801c31d8ed748cfa537fa45402288c95' => $vendorDir . '/psy/psysh/src/functions.php', diff --git a/app/vendor/composer/autoload_psr4.php b/app/vendor/composer/autoload_psr4.php index 872052d2e..8dc35eadb 100644 --- a/app/vendor/composer/autoload_psr4.php +++ b/app/vendor/composer/autoload_psr4.php @@ -25,8 +25,10 @@ 'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'), 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), 'Symfony\\Component\\Config\\' => array($vendorDir . '/symfony/config'), + 'SlevomatCodingStandard\\' => array($vendorDir . '/slevomat/coding-standard/SlevomatCodingStandard'), 'Seld\\PharUtils\\' => array($vendorDir . '/seld/phar-utils/src'), 'Seld\\JsonLint\\' => array($vendorDir . '/seld/jsonlint/src/Seld/JsonLint'), + 'React\\Promise\\' => array($vendorDir . '/react/promise/src'), 'Psy\\' => array($vendorDir . '/psy/psysh/src'), 'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), @@ -37,8 +39,11 @@ 'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src/Prophecy'), 'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'), 'Phinx\\' => array($vendorDir . '/robmorgan/phinx/src/Phinx'), + 'PackageVersions\\' => array($vendorDir . '/composer/package-versions-deprecated/src/PackageVersions'), + 'PHPStan\\PhpDocParser\\' => array($vendorDir . '/phpstan/phpdoc-parser/src'), 'Migrations\\' => array($vendorDir . '/cakephp/migrations/src'), 'M1\\Env\\' => array($vendorDir . '/m1/env/src'), + 'League\\Container\\' => array($vendorDir . '/league/container/src'), 'Laminas\\ZendFrameworkBridge\\' => array($vendorDir . '/laminas/laminas-zendframework-bridge/src'), 'Laminas\\HttpHandlerRunner\\' => array($vendorDir . '/laminas/laminas-httphandlerrunner/src'), 'Laminas\\Diactoros\\' => array($vendorDir . '/laminas/laminas-diactoros/src'), @@ -46,15 +51,17 @@ 'Jasny\\Twig\\' => array($vendorDir . '/jasny/twig-extensions/src'), 'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'), 'Doctrine\\Deprecations\\' => array($vendorDir . '/doctrine/deprecations/lib/Doctrine/Deprecations'), - 'Doctrine\\DBAL\\' => array($vendorDir . '/doctrine/dbal/lib/Doctrine/DBAL'), + 'Doctrine\\DBAL\\' => array($vendorDir . '/doctrine/dbal/src'), 'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'), 'Doctrine\\Common\\' => array($vendorDir . '/doctrine/event-manager/lib/Doctrine/Common'), 'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'), 'DebugKit\\Test\\Fixture\\' => array($vendorDir . '/cakephp/debug_kit/tests/Fixture'), 'DebugKit\\' => array($vendorDir . '/cakephp/debug_kit/src'), + 'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => array($vendorDir . '/dealerdirect/phpcodesniffer-composer-installer/src'), 'Composer\\XdebugHandler\\' => array($vendorDir . '/composer/xdebug-handler/src'), 'Composer\\Spdx\\' => array($vendorDir . '/composer/spdx-licenses/src'), 'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'), + 'Composer\\MetadataMinifier\\' => array($vendorDir . '/composer/metadata-minifier/src'), 'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'), 'Composer\\' => array($vendorDir . '/composer/composer/src/Composer'), 'Cake\\TwigView\\' => array($vendorDir . '/cakephp/twig-view/src'), @@ -63,8 +70,8 @@ 'Cake\\Chronos\\' => array($vendorDir . '/cakephp/chronos/src'), 'Cake\\' => array($vendorDir . '/cakephp/cakephp/src'), 'CakePHP\\' => array($vendorDir . '/cakephp/cakephp-codesniffer/CakePHP'), + 'Brick\\VarExporter\\' => array($vendorDir . '/brick/varexporter/src'), 'Bake\\' => array($vendorDir . '/cakephp/bake/src'), - 'Aura\\Intl\\' => array($vendorDir . '/aura/intl/src'), 'App\\Test\\' => array($baseDir . '/tests'), 'App\\' => array($baseDir . '/src'), ); diff --git a/app/vendor/composer/autoload_static.php b/app/vendor/composer/autoload_static.php index 91761359d..1928ae3b2 100644 --- a/app/vendor/composer/autoload_static.php +++ b/app/vendor/composer/autoload_static.php @@ -8,10 +8,17 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 { public static $files = array ( '9b38cf48e83f5d8f60375221cd213eee' => __DIR__ . '/..' . '/phpstan/phpstan/bootstrap.php', + 'ec07570ca5a812141189b1fa81503674' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert/Functions.php', 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', + '23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php', + '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php', + 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', + '0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php', + 'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php', + 'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php', '07d7f1a47144818725fd8d91a907ac57' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/create_uploaded_file.php', 'da94ac5d3ca7d2dbab84ce561ce72bfd' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/marshal_headers_from_sapi.php', '3d97c8dcdfba8cb85d3b34f116bb248b' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/marshal_method_from_sapi.php', @@ -28,18 +35,14 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'cc8e14526dc240491e17a838cb78508c' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/normalize_server.legacy.php', '786bf90caabc9e09b6ad4cc5ca8f0e30' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/normalize_uploaded_files.legacy.php', '751a5a3f463e4be759be31748b61737c' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/parse_cookie_header.legacy.php', - '23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php', '34122c0574b76bf21c9a8db62b5b9cf3' => __DIR__ . '/..' . '/cakephp/chronos/src/carbon_compat.php', - '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php', - 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', - '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', - '0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php', - 'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php', '7e9bd612cc444b3eed788ebbe46263a0' => __DIR__ . '/..' . '/laminas/laminas-zendframework-bridge/src/autoload.php', 'c720f792236cd163ece8049879166850' => __DIR__ . '/..' . '/cakephp/cakephp/src/Core/functions.php', 'ede59e3a405fb689cd1cebb7bb1db3fb' => __DIR__ . '/..' . '/cakephp/cakephp/src/Collection/functions.php', '90236b492da7ca2983a2ad6e33e4152e' => __DIR__ . '/..' . '/cakephp/cakephp/src/I18n/functions.php', + '2cb76c05856dfb60ada40ef54138d49a' => __DIR__ . '/..' . '/cakephp/cakephp/src/Routing/functions.php', 'b1fc73705e1bec51cd2b20a32cf1c60a' => __DIR__ . '/..' . '/cakephp/cakephp/src/Utility/bootstrap.php', + '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', '6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', 'bf9f5270ae66ac6fa0290b4bf47867b7' => __DIR__ . '/..' . '/adodb/adodb-php/adodb.inc.php', '801c31d8ed748cfa537fa45402288c95' => __DIR__ . '/..' . '/psy/psysh/src/functions.php', @@ -76,9 +79,14 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'Symfony\\Component\\Filesystem\\' => 29, 'Symfony\\Component\\Console\\' => 26, 'Symfony\\Component\\Config\\' => 25, + 'SlevomatCodingStandard\\' => 23, 'Seld\\PharUtils\\' => 15, 'Seld\\JsonLint\\' => 14, ), + 'R' => + array ( + 'React\\Promise\\' => 14, + ), 'P' => array ( 'Psy\\' => 4, @@ -91,6 +99,8 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'Prophecy\\' => 9, 'PhpParser\\' => 10, 'Phinx\\' => 6, + 'PackageVersions\\' => 16, + 'PHPStan\\PhpDocParser\\' => 21, ), 'M' => array ( @@ -99,6 +109,7 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 ), 'L' => array ( + 'League\\Container\\' => 17, 'Laminas\\ZendFrameworkBridge\\' => 28, 'Laminas\\HttpHandlerRunner\\' => 26, 'Laminas\\Diactoros\\' => 18, @@ -118,12 +129,14 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'DeepCopy\\' => 9, 'DebugKit\\Test\\Fixture\\' => 22, 'DebugKit\\' => 9, + 'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => 55, ), 'C' => array ( 'Composer\\XdebugHandler\\' => 23, 'Composer\\Spdx\\' => 14, 'Composer\\Semver\\' => 16, + 'Composer\\MetadataMinifier\\' => 26, 'Composer\\CaBundle\\' => 18, 'Composer\\' => 9, 'Cake\\TwigView\\' => 14, @@ -135,11 +148,11 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 ), 'B' => array ( + 'Brick\\VarExporter\\' => 18, 'Bake\\' => 5, ), 'A' => array ( - 'Aura\\Intl\\' => 10, 'App\\Test\\' => 9, 'App\\' => 4, ), @@ -224,6 +237,10 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 array ( 0 => __DIR__ . '/..' . '/symfony/config', ), + 'SlevomatCodingStandard\\' => + array ( + 0 => __DIR__ . '/..' . '/slevomat/coding-standard/SlevomatCodingStandard', + ), 'Seld\\PharUtils\\' => array ( 0 => __DIR__ . '/..' . '/seld/phar-utils/src', @@ -232,6 +249,10 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 array ( 0 => __DIR__ . '/..' . '/seld/jsonlint/src/Seld/JsonLint', ), + 'React\\Promise\\' => + array ( + 0 => __DIR__ . '/..' . '/react/promise/src', + ), 'Psy\\' => array ( 0 => __DIR__ . '/..' . '/psy/psysh/src', @@ -274,6 +295,14 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 array ( 0 => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx', ), + 'PackageVersions\\' => + array ( + 0 => __DIR__ . '/..' . '/composer/package-versions-deprecated/src/PackageVersions', + ), + 'PHPStan\\PhpDocParser\\' => + array ( + 0 => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src', + ), 'Migrations\\' => array ( 0 => __DIR__ . '/..' . '/cakephp/migrations/src', @@ -282,6 +311,10 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 array ( 0 => __DIR__ . '/..' . '/m1/env/src', ), + 'League\\Container\\' => + array ( + 0 => __DIR__ . '/..' . '/league/container/src', + ), 'Laminas\\ZendFrameworkBridge\\' => array ( 0 => __DIR__ . '/..' . '/laminas/laminas-zendframework-bridge/src', @@ -312,7 +345,7 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 ), 'Doctrine\\DBAL\\' => array ( - 0 => __DIR__ . '/..' . '/doctrine/dbal/lib/Doctrine/DBAL', + 0 => __DIR__ . '/..' . '/doctrine/dbal/src', ), 'Doctrine\\Common\\Cache\\' => array ( @@ -334,6 +367,10 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 array ( 0 => __DIR__ . '/..' . '/cakephp/debug_kit/src', ), + 'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => + array ( + 0 => __DIR__ . '/..' . '/dealerdirect/phpcodesniffer-composer-installer/src', + ), 'Composer\\XdebugHandler\\' => array ( 0 => __DIR__ . '/..' . '/composer/xdebug-handler/src', @@ -346,6 +383,10 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 array ( 0 => __DIR__ . '/..' . '/composer/semver/src', ), + 'Composer\\MetadataMinifier\\' => + array ( + 0 => __DIR__ . '/..' . '/composer/metadata-minifier/src', + ), 'Composer\\CaBundle\\' => array ( 0 => __DIR__ . '/..' . '/composer/ca-bundle/src', @@ -378,13 +419,13 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 array ( 0 => __DIR__ . '/..' . '/cakephp/cakephp-codesniffer/CakePHP', ), - 'Bake\\' => + 'Brick\\VarExporter\\' => array ( - 0 => __DIR__ . '/..' . '/cakephp/bake/src', + 0 => __DIR__ . '/..' . '/brick/varexporter/src', ), - 'Aura\\Intl\\' => + 'Bake\\' => array ( - 0 => __DIR__ . '/..' . '/aura/intl/src', + 0 => __DIR__ . '/..' . '/cakephp/bake/src', ), 'App\\Test\\' => array ( @@ -421,66 +462,79 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'Mobile_Detect' => __DIR__ . '/..' . '/mobiledetect/mobiledetectlib/Mobile_Detect.php', 'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php', 'PHPUnit\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Exception.php', + 'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ActualValueIsNotAnObjectException.php', 'PHPUnit\\Framework\\Assert' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert.php', 'PHPUnit\\Framework\\AssertionFailedError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/AssertionFailedError.php', 'PHPUnit\\Framework\\CodeCoverageException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/CodeCoverageException.php', - 'PHPUnit\\Framework\\Constraint\\ArrayHasKey' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ArrayHasKey.php', - 'PHPUnit\\Framework\\Constraint\\ArraySubset' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ArraySubset.php', - 'PHPUnit\\Framework\\Constraint\\Attribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Attribute.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotAcceptParameterTypeException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareBoolReturnTypeException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareExactlyOneParameterException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareParameterTypeException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareParameterTypeException.php', + 'PHPUnit\\Framework\\ComparisonMethodDoesNotExistException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotExistException.php', + 'PHPUnit\\Framework\\Constraint\\ArrayHasKey' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Traversable/ArrayHasKey.php', + 'PHPUnit\\Framework\\Constraint\\BinaryOperator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/BinaryOperator.php', 'PHPUnit\\Framework\\Constraint\\Callback' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Callback.php', - 'PHPUnit\\Framework\\Constraint\\ClassHasAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ClassHasAttribute.php', - 'PHPUnit\\Framework\\Constraint\\ClassHasStaticAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ClassHasStaticAttribute.php', - 'PHPUnit\\Framework\\Constraint\\Composite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Composite.php', + 'PHPUnit\\Framework\\Constraint\\ClassHasAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Object/ClassHasAttribute.php', + 'PHPUnit\\Framework\\Constraint\\ClassHasStaticAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Object/ClassHasStaticAttribute.php', 'PHPUnit\\Framework\\Constraint\\Constraint' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Constraint.php', - 'PHPUnit\\Framework\\Constraint\\Count' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Count.php', - 'PHPUnit\\Framework\\Constraint\\DirectoryExists' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/DirectoryExists.php', - 'PHPUnit\\Framework\\Constraint\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Exception.php', - 'PHPUnit\\Framework\\Constraint\\ExceptionCode' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ExceptionCode.php', - 'PHPUnit\\Framework\\Constraint\\ExceptionMessage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessage.php', - 'PHPUnit\\Framework\\Constraint\\ExceptionMessageRegularExpression' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessageRegularExpression.php', - 'PHPUnit\\Framework\\Constraint\\FileExists' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/FileExists.php', - 'PHPUnit\\Framework\\Constraint\\GreaterThan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/GreaterThan.php', + 'PHPUnit\\Framework\\Constraint\\Count' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/Count.php', + 'PHPUnit\\Framework\\Constraint\\DirectoryExists' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/DirectoryExists.php', + 'PHPUnit\\Framework\\Constraint\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Exception/Exception.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionCode' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionCode.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionMessage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionMessage.php', + 'PHPUnit\\Framework\\Constraint\\ExceptionMessageRegularExpression' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionMessageRegularExpression.php', + 'PHPUnit\\Framework\\Constraint\\FileExists' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/FileExists.php', + 'PHPUnit\\Framework\\Constraint\\GreaterThan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/GreaterThan.php', 'PHPUnit\\Framework\\Constraint\\IsAnything' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsAnything.php', - 'PHPUnit\\Framework\\Constraint\\IsEmpty' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsEmpty.php', - 'PHPUnit\\Framework\\Constraint\\IsEqual' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsEqual.php', - 'PHPUnit\\Framework\\Constraint\\IsFalse' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsFalse.php', - 'PHPUnit\\Framework\\Constraint\\IsFinite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsFinite.php', + 'PHPUnit\\Framework\\Constraint\\IsEmpty' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/IsEmpty.php', + 'PHPUnit\\Framework\\Constraint\\IsEqual' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqual.php', + 'PHPUnit\\Framework\\Constraint\\IsEqualCanonicalizing' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualCanonicalizing.php', + 'PHPUnit\\Framework\\Constraint\\IsEqualIgnoringCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualIgnoringCase.php', + 'PHPUnit\\Framework\\Constraint\\IsEqualWithDelta' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualWithDelta.php', + 'PHPUnit\\Framework\\Constraint\\IsFalse' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Boolean/IsFalse.php', + 'PHPUnit\\Framework\\Constraint\\IsFinite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Math/IsFinite.php', 'PHPUnit\\Framework\\Constraint\\IsIdentical' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php', - 'PHPUnit\\Framework\\Constraint\\IsInfinite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsInfinite.php', - 'PHPUnit\\Framework\\Constraint\\IsInstanceOf' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsInstanceOf.php', - 'PHPUnit\\Framework\\Constraint\\IsJson' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsJson.php', - 'PHPUnit\\Framework\\Constraint\\IsNan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsNan.php', - 'PHPUnit\\Framework\\Constraint\\IsNull' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsNull.php', - 'PHPUnit\\Framework\\Constraint\\IsReadable' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsReadable.php', - 'PHPUnit\\Framework\\Constraint\\IsTrue' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsTrue.php', - 'PHPUnit\\Framework\\Constraint\\IsType' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsType.php', - 'PHPUnit\\Framework\\Constraint\\IsWritable' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsWritable.php', + 'PHPUnit\\Framework\\Constraint\\IsInfinite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Math/IsInfinite.php', + 'PHPUnit\\Framework\\Constraint\\IsInstanceOf' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Type/IsInstanceOf.php', + 'PHPUnit\\Framework\\Constraint\\IsJson' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/String/IsJson.php', + 'PHPUnit\\Framework\\Constraint\\IsNan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Math/IsNan.php', + 'PHPUnit\\Framework\\Constraint\\IsNull' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Type/IsNull.php', + 'PHPUnit\\Framework\\Constraint\\IsReadable' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/IsReadable.php', + 'PHPUnit\\Framework\\Constraint\\IsTrue' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Boolean/IsTrue.php', + 'PHPUnit\\Framework\\Constraint\\IsType' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Type/IsType.php', + 'PHPUnit\\Framework\\Constraint\\IsWritable' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/IsWritable.php', 'PHPUnit\\Framework\\Constraint\\JsonMatches' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php', 'PHPUnit\\Framework\\Constraint\\JsonMatchesErrorMessageProvider' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/JsonMatchesErrorMessageProvider.php', - 'PHPUnit\\Framework\\Constraint\\LessThan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/LessThan.php', - 'PHPUnit\\Framework\\Constraint\\LogicalAnd' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/LogicalAnd.php', - 'PHPUnit\\Framework\\Constraint\\LogicalNot' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/LogicalNot.php', - 'PHPUnit\\Framework\\Constraint\\LogicalOr' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/LogicalOr.php', - 'PHPUnit\\Framework\\Constraint\\LogicalXor' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/LogicalXor.php', - 'PHPUnit\\Framework\\Constraint\\ObjectHasAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ObjectHasAttribute.php', - 'PHPUnit\\Framework\\Constraint\\RegularExpression' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/RegularExpression.php', - 'PHPUnit\\Framework\\Constraint\\SameSize' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/SameSize.php', - 'PHPUnit\\Framework\\Constraint\\StringContains' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringContains.php', - 'PHPUnit\\Framework\\Constraint\\StringEndsWith' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringEndsWith.php', - 'PHPUnit\\Framework\\Constraint\\StringMatchesFormatDescription' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringMatchesFormatDescription.php', - 'PHPUnit\\Framework\\Constraint\\StringStartsWith' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringStartsWith.php', - 'PHPUnit\\Framework\\Constraint\\TraversableContains' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/TraversableContains.php', - 'PHPUnit\\Framework\\Constraint\\TraversableContainsEqual' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsEqual.php', - 'PHPUnit\\Framework\\Constraint\\TraversableContainsIdentical' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsIdentical.php', - 'PHPUnit\\Framework\\Constraint\\TraversableContainsOnly' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.php', + 'PHPUnit\\Framework\\Constraint\\LessThan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/LessThan.php', + 'PHPUnit\\Framework\\Constraint\\LogicalAnd' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalAnd.php', + 'PHPUnit\\Framework\\Constraint\\LogicalNot' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalNot.php', + 'PHPUnit\\Framework\\Constraint\\LogicalOr' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalOr.php', + 'PHPUnit\\Framework\\Constraint\\LogicalXor' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalXor.php', + 'PHPUnit\\Framework\\Constraint\\ObjectEquals' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectEquals.php', + 'PHPUnit\\Framework\\Constraint\\ObjectHasAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectHasAttribute.php', + 'PHPUnit\\Framework\\Constraint\\Operator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/Operator.php', + 'PHPUnit\\Framework\\Constraint\\RegularExpression' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/String/RegularExpression.php', + 'PHPUnit\\Framework\\Constraint\\SameSize' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/SameSize.php', + 'PHPUnit\\Framework\\Constraint\\StringContains' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/String/StringContains.php', + 'PHPUnit\\Framework\\Constraint\\StringEndsWith' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/String/StringEndsWith.php', + 'PHPUnit\\Framework\\Constraint\\StringMatchesFormatDescription' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/String/StringMatchesFormatDescription.php', + 'PHPUnit\\Framework\\Constraint\\StringStartsWith' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/String/StringStartsWith.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContains' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContains.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContainsEqual' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsEqual.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContainsIdentical' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsIdentical.php', + 'PHPUnit\\Framework\\Constraint\\TraversableContainsOnly' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsOnly.php', + 'PHPUnit\\Framework\\Constraint\\UnaryOperator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/UnaryOperator.php', 'PHPUnit\\Framework\\CoveredCodeNotExecutedException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/CoveredCodeNotExecutedException.php', 'PHPUnit\\Framework\\DataProviderTestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/DataProviderTestSuite.php', + 'PHPUnit\\Framework\\Error' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/Error.php', + 'PHPUnit\\Framework\\ErrorTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/ErrorTestCase.php', 'PHPUnit\\Framework\\Error\\Deprecated' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Deprecated.php', 'PHPUnit\\Framework\\Error\\Error' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Error.php', 'PHPUnit\\Framework\\Error\\Notice' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Notice.php', 'PHPUnit\\Framework\\Error\\Warning' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Warning.php', 'PHPUnit\\Framework\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/Exception.php', 'PHPUnit\\Framework\\ExceptionWrapper' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/ExceptionWrapper.php', + 'PHPUnit\\Framework\\ExecutionOrderDependency' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/ExecutionOrderDependency.php', 'PHPUnit\\Framework\\ExpectationFailedException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ExpectationFailedException.php', 'PHPUnit\\Framework\\IncompleteTest' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/IncompleteTest.php', 'PHPUnit\\Framework\\IncompleteTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/IncompleteTestCase.php', @@ -495,20 +549,31 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'PHPUnit\\Framework\\MockObject\\Builder\\Identity' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Builder/Identity.php', 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationMocker' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationMocker.php', 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationStubber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationStubber.php', - 'PHPUnit\\Framework\\MockObject\\Builder\\Match_' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Builder/Match_.php', 'PHPUnit\\Framework\\MockObject\\Builder\\MethodNameMatch' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Builder/MethodNameMatch.php', 'PHPUnit\\Framework\\MockObject\\Builder\\ParametersMatch' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Builder/ParametersMatch.php', 'PHPUnit\\Framework\\MockObject\\Builder\\Stub' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Builder/Stub.php', + 'PHPUnit\\Framework\\MockObject\\CannotUseAddMethodsException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/CannotUseAddMethodsException.php', + 'PHPUnit\\Framework\\MockObject\\CannotUseOnlyMethodsException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/CannotUseOnlyMethodsException.php', + 'PHPUnit\\Framework\\MockObject\\ClassAlreadyExistsException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/ClassAlreadyExistsException.php', + 'PHPUnit\\Framework\\MockObject\\ClassIsFinalException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/ClassIsFinalException.php', 'PHPUnit\\Framework\\MockObject\\ConfigurableMethod' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/ConfigurableMethod.php', 'PHPUnit\\Framework\\MockObject\\ConfigurableMethodsAlreadyInitializedException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php', + 'PHPUnit\\Framework\\MockObject\\DuplicateMethodException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/DuplicateMethodException.php', 'PHPUnit\\Framework\\MockObject\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/Exception.php', 'PHPUnit\\Framework\\MockObject\\Generator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Generator.php', 'PHPUnit\\Framework\\MockObject\\IncompatibleReturnValueException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/IncompatibleReturnValueException.php', + 'PHPUnit\\Framework\\MockObject\\InvalidMethodNameException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/InvalidMethodNameException.php', 'PHPUnit\\Framework\\MockObject\\Invocation' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Invocation.php', 'PHPUnit\\Framework\\MockObject\\InvocationHandler' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/InvocationHandler.php', + 'PHPUnit\\Framework\\MockObject\\MatchBuilderNotFoundException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/MatchBuilderNotFoundException.php', 'PHPUnit\\Framework\\MockObject\\Matcher' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Matcher.php', + 'PHPUnit\\Framework\\MockObject\\MatcherAlreadyRegisteredException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php', 'PHPUnit\\Framework\\MockObject\\Method' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Api/Method.php', + 'PHPUnit\\Framework\\MockObject\\MethodCannotBeConfiguredException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php', + 'PHPUnit\\Framework\\MockObject\\MethodNameAlreadyConfiguredException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MethodNameConstraint' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/MethodNameConstraint.php', + 'PHPUnit\\Framework\\MockObject\\MethodNameNotConfiguredException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodNameNotConfiguredException.php', + 'PHPUnit\\Framework\\MockObject\\MethodParametersAlreadyConfiguredException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MockBuilder' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/MockBuilder.php', 'PHPUnit\\Framework\\MockObject\\MockClass' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/MockClass.php', 'PHPUnit\\Framework\\MockObject\\MockMethod' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/MockMethod.php', @@ -517,6 +582,9 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'PHPUnit\\Framework\\MockObject\\MockTrait' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/MockTrait.php', 'PHPUnit\\Framework\\MockObject\\MockType' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/MockType.php', 'PHPUnit\\Framework\\MockObject\\MockedCloneMethod' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Api/MockedCloneMethod.php', + 'PHPUnit\\Framework\\MockObject\\OriginalConstructorInvocationRequiredException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/OriginalConstructorInvocationRequiredException.php', + 'PHPUnit\\Framework\\MockObject\\ReflectionException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/ReflectionException.php', + 'PHPUnit\\Framework\\MockObject\\ReturnValueNotConfiguredException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\Rule\\AnyInvokedCount' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/AnyInvokedCount.php', 'PHPUnit\\Framework\\MockObject\\Rule\\AnyParameters' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/AnyParameters.php', 'PHPUnit\\Framework\\MockObject\\Rule\\ConsecutiveParameters' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/ConsecutiveParameters.php', @@ -530,6 +598,7 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'PHPUnit\\Framework\\MockObject\\Rule\\Parameters' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/Parameters.php', 'PHPUnit\\Framework\\MockObject\\Rule\\ParametersRule' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/ParametersRule.php', 'PHPUnit\\Framework\\MockObject\\RuntimeException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/RuntimeException.php', + 'PHPUnit\\Framework\\MockObject\\SoapExtensionNotAvailableException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/SoapExtensionNotAvailableException.php', 'PHPUnit\\Framework\\MockObject\\Stub' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ConsecutiveCalls' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub/ConsecutiveCalls.php', 'PHPUnit\\Framework\\MockObject\\Stub\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub/Exception.php', @@ -540,11 +609,15 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnStub' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnStub.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnValueMap' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnValueMap.php', 'PHPUnit\\Framework\\MockObject\\Stub\\Stub' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub/Stub.php', + 'PHPUnit\\Framework\\MockObject\\UnknownClassException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownClassException.php', + 'PHPUnit\\Framework\\MockObject\\UnknownTraitException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownTraitException.php', + 'PHPUnit\\Framework\\MockObject\\UnknownTypeException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownTypeException.php', 'PHPUnit\\Framework\\MockObject\\UnmockedCloneMethod' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Api/UnmockedCloneMethod.php', 'PHPUnit\\Framework\\MockObject\\Verifiable' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Verifiable.php', 'PHPUnit\\Framework\\NoChildTestSuiteException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/NoChildTestSuiteException.php', 'PHPUnit\\Framework\\OutputError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/OutputError.php', 'PHPUnit\\Framework\\PHPTAssertionFailedError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/PHPTAssertionFailedError.php', + 'PHPUnit\\Framework\\Reorderable' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Reorderable.php', 'PHPUnit\\Framework\\RiskyTestError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/RiskyTestError.php', 'PHPUnit\\Framework\\SelfDescribing' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SelfDescribing.php', 'PHPUnit\\Framework\\SkippedTest' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SkippedTest.php', @@ -579,6 +652,8 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'PHPUnit\\Runner\\BeforeTestHook' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/BeforeTestHook.php', 'PHPUnit\\Runner\\DefaultTestResultCache' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/DefaultTestResultCache.php', 'PHPUnit\\Runner\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Exception.php', + 'PHPUnit\\Runner\\Extension\\ExtensionHandler' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Extension/ExtensionHandler.php', + 'PHPUnit\\Runner\\Extension\\PharLoader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Extension/PharLoader.php', 'PHPUnit\\Runner\\Filter\\ExcludeGroupFilterIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/ExcludeGroupFilterIterator.php', 'PHPUnit\\Runner\\Filter\\Factory' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Factory.php', 'PHPUnit\\Runner\\Filter\\GroupFilterIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/GroupFilterIterator.php', @@ -595,23 +670,110 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'PHPUnit\\Runner\\TestSuiteLoader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/TestSuiteLoader.php', 'PHPUnit\\Runner\\TestSuiteSorter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/TestSuiteSorter.php', 'PHPUnit\\Runner\\Version' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Version.php', + 'PHPUnit\\TextUI\\CliArguments\\Builder' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/CliArguments/Builder.php', + 'PHPUnit\\TextUI\\CliArguments\\Configuration' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/CliArguments/Configuration.php', + 'PHPUnit\\TextUI\\CliArguments\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/CliArguments/Exception.php', + 'PHPUnit\\TextUI\\CliArguments\\Mapper' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/CliArguments/Mapper.php', 'PHPUnit\\TextUI\\Command' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Command.php', - 'PHPUnit\\TextUI\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Exception.php', + 'PHPUnit\\TextUI\\DefaultResultPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/DefaultResultPrinter.php', + 'PHPUnit\\TextUI\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Exception/Exception.php', 'PHPUnit\\TextUI\\Help' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Help.php', + 'PHPUnit\\TextUI\\ReflectionException' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Exception/ReflectionException.php', 'PHPUnit\\TextUI\\ResultPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/ResultPrinter.php', + 'PHPUnit\\TextUI\\RuntimeException' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Exception/RuntimeException.php', + 'PHPUnit\\TextUI\\TestDirectoryNotFoundException' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Exception/TestDirectoryNotFoundException.php', + 'PHPUnit\\TextUI\\TestFileNotFoundException' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Exception/TestFileNotFoundException.php', 'PHPUnit\\TextUI\\TestRunner' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/TestRunner.php', + 'PHPUnit\\TextUI\\TestSuiteMapper' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/TestSuiteMapper.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\CodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\FilterMapper' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\Directory' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Clover' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Cobertura' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Crap4j' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Html' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Php' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Php.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Text' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Xml' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Configuration' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Configuration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Constant' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Constant.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/ConstantCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ConvertLogTypes' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCloverToReport' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCrap4jToReport' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageHtmlToReport' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoveragePhpToReport' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageTextToReport' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageXmlToReport' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Directory' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/Directory.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Exception.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Extension' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/Extension.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\File' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/File.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/FileCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Generator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Generator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Group' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/Group.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/GroupCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/GroupCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Groups' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/Groups.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IniSetting' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSetting.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSettingCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\IntroduceCoverageElement' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Loader' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Loader.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\LogToReportMigration' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Junit' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Junit.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Logging' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Logging.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TeamCity' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TeamCity.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Html' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Html.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Text' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Xml' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Xml.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Text' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Text.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Migration' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/Migration.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilder' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationBuilder.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilderException' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationBuilderException.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationException' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationException.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Migrator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromFilterWhitelistToCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromRootToCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistDirectoriesToCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistDirectoriesToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistExcludesToCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\PHPUnit' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/PHPUnit.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Php' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Php.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\PhpHandler' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/PhpHandler.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCacheTokensAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveEmptyFilter' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveLogTypes' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectory' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectory.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestFile' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFile.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFileCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuite.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\UpdateSchemaLocationTo93' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\Variable' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Variable.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/VariableCollection.php', + 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php', 'PHPUnit\\Util\\Annotation\\DocBlock' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Annotation/DocBlock.php', 'PHPUnit\\Util\\Annotation\\Registry' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Annotation/Registry.php', 'PHPUnit\\Util\\Blacklist' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Blacklist.php', 'PHPUnit\\Util\\Color' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Color.php', - 'PHPUnit\\Util\\Configuration' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Configuration.php', - 'PHPUnit\\Util\\ConfigurationGenerator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/ConfigurationGenerator.php', 'PHPUnit\\Util\\ErrorHandler' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/ErrorHandler.php', 'PHPUnit\\Util\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Exception.php', + 'PHPUnit\\Util\\ExcludeList' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/ExcludeList.php', 'PHPUnit\\Util\\FileLoader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/FileLoader.php', 'PHPUnit\\Util\\Filesystem' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Filesystem.php', 'PHPUnit\\Util\\Filter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Filter.php', - 'PHPUnit\\Util\\Getopt' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Getopt.php', 'PHPUnit\\Util\\GlobalState' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/GlobalState.php', 'PHPUnit\\Util\\InvalidDataSetException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/InvalidDataSetException.php', 'PHPUnit\\Util\\Json' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Json.php', @@ -636,184 +798,16 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'PHPUnit\\Util\\XdebugFilterScriptGenerator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/XdebugFilterScriptGenerator.php', 'PHPUnit\\Util\\Xml' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml.php', 'PHPUnit\\Util\\XmlTestListRenderer' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/XmlTestListRenderer.php', - 'PHP_Token' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', - 'PHP_TokenWithScope' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/TokenWithScope.php', - 'PHP_TokenWithScopeAndVisibility' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/TokenWithScopeAndVisibility.php', - 'PHP_Token_ABSTRACT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Abstract.php', - 'PHP_Token_AMPERSAND' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Ampersand.php', - 'PHP_Token_AND_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/AndEqual.php', - 'PHP_Token_ARRAY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Array.php', - 'PHP_Token_ARRAY_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/ArrayCast.php', - 'PHP_Token_AS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/As.php', - 'PHP_Token_AT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/At.php', - 'PHP_Token_BACKTICK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Backtick.php', - 'PHP_Token_BAD_CHARACTER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/BadCharacter.php', - 'PHP_Token_BOOLEAN_AND' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/BooleanAnd.php', - 'PHP_Token_BOOLEAN_OR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/BooleanOr.php', - 'PHP_Token_BOOL_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/BoolCast.php', - 'PHP_Token_BREAK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/break.php', - 'PHP_Token_CALLABLE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Callable.php', - 'PHP_Token_CARET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Caret.php', - 'PHP_Token_CASE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Case.php', - 'PHP_Token_CATCH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Catch.php', - 'PHP_Token_CHARACTER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Character.php', - 'PHP_Token_CLASS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Class.php', - 'PHP_Token_CLASS_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/ClassC.php', - 'PHP_Token_CLASS_NAME_CONSTANT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/ClassNameConstant.php', - 'PHP_Token_CLONE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Clone.php', - 'PHP_Token_CLOSE_BRACKET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/CloseBracket.php', - 'PHP_Token_CLOSE_CURLY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/CloseCurly.php', - 'PHP_Token_CLOSE_SQUARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/CloseSquare.php', - 'PHP_Token_CLOSE_TAG' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/CloseTag.php', - 'PHP_Token_COALESCE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Coalesce.php', - 'PHP_Token_COALESCE_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/CoalesceEqual.php', - 'PHP_Token_COLON' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Colon.php', - 'PHP_Token_COMMA' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Comma.php', - 'PHP_Token_COMMENT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Comment.php', - 'PHP_Token_CONCAT_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/ConcatEqual.php', - 'PHP_Token_CONST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Const.php', - 'PHP_Token_CONSTANT_ENCAPSED_STRING' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/ConstantEncapsedString.php', - 'PHP_Token_CONTINUE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Continue.php', - 'PHP_Token_CURLY_OPEN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/CurlyOpen.php', - 'PHP_Token_DEC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Dec.php', - 'PHP_Token_DECLARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Declare.php', - 'PHP_Token_DEFAULT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Default.php', - 'PHP_Token_DIR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Dir.php', - 'PHP_Token_DIV' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Div.php', - 'PHP_Token_DIV_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/DivEqual.php', - 'PHP_Token_DNUMBER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/DNumber.php', - 'PHP_Token_DO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Do.php', - 'PHP_Token_DOC_COMMENT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/DocComment.php', - 'PHP_Token_DOLLAR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Dollar.php', - 'PHP_Token_DOLLAR_OPEN_CURLY_BRACES' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/DollarOpenCurlyBraces.php', - 'PHP_Token_DOT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Dot.php', - 'PHP_Token_DOUBLE_ARROW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/DoubleArrow.php', - 'PHP_Token_DOUBLE_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/DoubleCast.php', - 'PHP_Token_DOUBLE_COLON' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/DoubleColon.php', - 'PHP_Token_DOUBLE_QUOTES' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/DoubleQuotes.php', - 'PHP_Token_ECHO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Echo.php', - 'PHP_Token_ELLIPSIS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Ellipsis.php', - 'PHP_Token_ELSE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Else.php', - 'PHP_Token_ELSEIF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Elseif.php', - 'PHP_Token_EMPTY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Empty.php', - 'PHP_Token_ENCAPSED_AND_WHITESPACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/EncapsedAndWhitespace.php', - 'PHP_Token_ENDDECLARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Enddeclare.php', - 'PHP_Token_ENDFOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Endfor.php', - 'PHP_Token_ENDFOREACH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Endforeach.php', - 'PHP_Token_ENDIF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Endif.php', - 'PHP_Token_ENDSWITCH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Endswitch.php', - 'PHP_Token_ENDWHILE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Endwhile.php', - 'PHP_Token_END_HEREDOC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/EndHeredoc.php', - 'PHP_Token_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Equal.php', - 'PHP_Token_EVAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Eval.php', - 'PHP_Token_EXCLAMATION_MARK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/ExclamationMark.php', - 'PHP_Token_EXIT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Exit.php', - 'PHP_Token_EXTENDS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Extends.php', - 'PHP_Token_FILE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/File.php', - 'PHP_Token_FINAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Final.php', - 'PHP_Token_FINALLY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Finally.php', - 'PHP_Token_FN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Fn.php', - 'PHP_Token_FOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/For.php', - 'PHP_Token_FOREACH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Foreach.php', - 'PHP_Token_FUNCTION' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Function.php', - 'PHP_Token_FUNC_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/FuncC.php', - 'PHP_Token_GLOBAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Global.php', - 'PHP_Token_GOTO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Goto.php', - 'PHP_Token_GT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Gt.php', - 'PHP_Token_HALT_COMPILER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/HaltCompiler.php', - 'PHP_Token_IF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/If.php', - 'PHP_Token_IMPLEMENTS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Implements.php', - 'PHP_Token_INC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Inc.php', - 'PHP_Token_INCLUDE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Include.php', - 'PHP_Token_INCLUDE_ONCE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/IncludeOnce.php', - 'PHP_Token_INLINE_HTML' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/InlineHtml.php', - 'PHP_Token_INSTANCEOF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Instanceof.php', - 'PHP_Token_INSTEADOF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Insteadof.php', - 'PHP_Token_INTERFACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Interface.php', - 'PHP_Token_INT_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/IntCast.php', - 'PHP_Token_ISSET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Isset.php', - 'PHP_Token_IS_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/IsEqual.php', - 'PHP_Token_IS_GREATER_OR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/IsGreaterOrEqual.php', - 'PHP_Token_IS_IDENTICAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/IsIdentical.php', - 'PHP_Token_IS_NOT_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/IsNotEqual.php', - 'PHP_Token_IS_NOT_IDENTICAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/IsNotIdentical.php', - 'PHP_Token_IS_SMALLER_OR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/IsSmallerOrEqual.php', - 'PHP_Token_Includes' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Includes.php', - 'PHP_Token_LINE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Line.php', - 'PHP_Token_LIST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/List.php', - 'PHP_Token_LNUMBER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Lnumber.php', - 'PHP_Token_LOGICAL_AND' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/LogicalAnd.php', - 'PHP_Token_LOGICAL_OR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/LogicalOr.php', - 'PHP_Token_LOGICAL_XOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/LogicalXor.php', - 'PHP_Token_LT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Lt.php', - 'PHP_Token_METHOD_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/MethodC.php', - 'PHP_Token_MINUS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Minus.php', - 'PHP_Token_MINUS_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/MinusEqual.php', - 'PHP_Token_MOD_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/ModEqual.php', - 'PHP_Token_MULT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Mult.php', - 'PHP_Token_MUL_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/MulEqual.php', - 'PHP_Token_NAMESPACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Namespace.php', - 'PHP_Token_NAME_FULLY_QUALIFIED' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/NameFullyQualified.php', - 'PHP_Token_NAME_QUALIFIED' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/NameQualified.php', - 'PHP_Token_NAME_RELATIVE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/NameRelative.php', - 'PHP_Token_NEW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/New.php', - 'PHP_Token_NS_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/NsC.php', - 'PHP_Token_NS_SEPARATOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/NsSeparator.php', - 'PHP_Token_NUM_STRING' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/NumString.php', - 'PHP_Token_OBJECT_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/ObjectCast.php', - 'PHP_Token_OBJECT_OPERATOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/ObjectOperator.php', - 'PHP_Token_OPEN_BRACKET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/OpenBracket.php', - 'PHP_Token_OPEN_CURLY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/OpenCurly.php', - 'PHP_Token_OPEN_SQUARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/OpenSquare.php', - 'PHP_Token_OPEN_TAG' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/OpenTag.php', - 'PHP_Token_OPEN_TAG_WITH_ECHO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/OpenTagWithEcho.php', - 'PHP_Token_OR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/OrEqual.php', - 'PHP_Token_PAAMAYIM_NEKUDOTAYIM' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/PaamayimNekudotayim.php', - 'PHP_Token_PERCENT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Percent.php', - 'PHP_Token_PIPE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Pipe.php', - 'PHP_Token_PLUS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Plus.php', - 'PHP_Token_PLUS_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/PlusEqual.php', - 'PHP_Token_POW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Pow.php', - 'PHP_Token_POW_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/PowEqual.php', - 'PHP_Token_PRINT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Print.php', - 'PHP_Token_PRIVATE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Private.php', - 'PHP_Token_PROTECTED' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Protected.php', - 'PHP_Token_PUBLIC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Public.php', - 'PHP_Token_QUESTION_MARK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/QuestionMark.php', - 'PHP_Token_REQUIRE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Require.php', - 'PHP_Token_REQUIRE_ONCE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/RequireOnce.php', - 'PHP_Token_RETURN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Return.php', - 'PHP_Token_SEMICOLON' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Semicolon.php', - 'PHP_Token_SL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Sl.php', - 'PHP_Token_SL_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/SlEqual.php', - 'PHP_Token_SPACESHIP' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Spaceship.php', - 'PHP_Token_SR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Sr.php', - 'PHP_Token_SR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/SrEqual.php', - 'PHP_Token_START_HEREDOC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/StartHeredoc.php', - 'PHP_Token_STATIC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Static.php', - 'PHP_Token_STRING' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/String.php', - 'PHP_Token_STRING_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/StringCast.php', - 'PHP_Token_STRING_VARNAME' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/StringVarname.php', - 'PHP_Token_SWITCH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Switch.php', - 'PHP_Token_Stream' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Stream.php', - 'PHP_Token_Stream_CachingFactory' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/CachingFactory.php', - 'PHP_Token_THROW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Throw.php', - 'PHP_Token_TILDE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Tilde.php', - 'PHP_Token_TRAIT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Trait.php', - 'PHP_Token_TRAIT_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/TraitC.php', - 'PHP_Token_TRY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Try.php', - 'PHP_Token_UNSET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Unset.php', - 'PHP_Token_UNSET_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/UnsetCast.php', - 'PHP_Token_USE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Use.php', - 'PHP_Token_USE_FUNCTION' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/UseFunction.php', - 'PHP_Token_Util' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Util.php', - 'PHP_Token_VAR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Var.php', - 'PHP_Token_VARIABLE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Variable.php', - 'PHP_Token_WHILE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/While.php', - 'PHP_Token_WHITESPACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Whitespace.php', - 'PHP_Token_XOR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/XorEqual.php', - 'PHP_Token_YIELD' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Yield.php', - 'PHP_Token_YIELD_FROM' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/YieldFrom.php', + 'PHPUnit\\Util\\Xml\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/Exception.php', + 'PHPUnit\\Util\\Xml\\FailedSchemaDetectionResult' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/FailedSchemaDetectionResult.php', + 'PHPUnit\\Util\\Xml\\Loader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/Loader.php', + 'PHPUnit\\Util\\Xml\\SchemaDetectionResult' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/SchemaDetectionResult.php', + 'PHPUnit\\Util\\Xml\\SchemaDetector' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/SchemaDetector.php', + 'PHPUnit\\Util\\Xml\\SchemaFinder' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/SchemaFinder.php', + 'PHPUnit\\Util\\Xml\\SnapshotNodeList' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/SnapshotNodeList.php', + 'PHPUnit\\Util\\Xml\\SuccessfulSchemaDetectionResult' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/SuccessfulSchemaDetectionResult.php', + 'PHPUnit\\Util\\Xml\\ValidationResult' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/ValidationResult.php', + 'PHPUnit\\Util\\Xml\\Validator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/Validator.php', 'PharIo\\Manifest\\Application' => __DIR__ . '/..' . '/phar-io/manifest/src/values/Application.php', 'PharIo\\Manifest\\ApplicationName' => __DIR__ . '/..' . '/phar-io/manifest/src/values/ApplicationName.php', 'PharIo\\Manifest\\Author' => __DIR__ . '/..' . '/phar-io/manifest/src/values/Author.php', @@ -884,22 +878,50 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'PharIo\\Version\\VersionConstraintValue' => __DIR__ . '/..' . '/phar-io/version/src/VersionConstraintValue.php', 'PharIo\\Version\\VersionNumber' => __DIR__ . '/..' . '/phar-io/version/src/VersionNumber.php', 'ReturnTypeWillChange' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php', + 'SebastianBergmann\\CliParser\\AmbiguousOptionException' => __DIR__ . '/..' . '/sebastian/cli-parser/src/exceptions/AmbiguousOptionException.php', + 'SebastianBergmann\\CliParser\\Exception' => __DIR__ . '/..' . '/sebastian/cli-parser/src/exceptions/Exception.php', + 'SebastianBergmann\\CliParser\\OptionDoesNotAllowArgumentException' => __DIR__ . '/..' . '/sebastian/cli-parser/src/exceptions/OptionDoesNotAllowArgumentException.php', + 'SebastianBergmann\\CliParser\\Parser' => __DIR__ . '/..' . '/sebastian/cli-parser/src/Parser.php', + 'SebastianBergmann\\CliParser\\RequiredOptionArgumentMissingException' => __DIR__ . '/..' . '/sebastian/cli-parser/src/exceptions/RequiredOptionArgumentMissingException.php', + 'SebastianBergmann\\CliParser\\UnknownOptionException' => __DIR__ . '/..' . '/sebastian/cli-parser/src/exceptions/UnknownOptionException.php', + 'SebastianBergmann\\CodeCoverage\\BranchAndPathCoverageNotSupportedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/BranchAndPathCoverageNotSupportedException.php', 'SebastianBergmann\\CodeCoverage\\CodeCoverage' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage.php', - 'SebastianBergmann\\CodeCoverage\\CoveredCodeNotExecutedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php', + 'SebastianBergmann\\CodeCoverage\\CrapIndex' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CrapIndex.php', + 'SebastianBergmann\\CodeCoverage\\DeadCodeDetectionNotSupportedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/DeadCodeDetectionNotSupportedException.php', + 'SebastianBergmann\\CodeCoverage\\Directory' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Directory.php', + 'SebastianBergmann\\CodeCoverage\\DirectoryCouldNotBeCreatedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/DirectoryCouldNotBeCreatedException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Driver' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/Driver.php', - 'SebastianBergmann\\CodeCoverage\\Driver\\PCOV' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/PCOV.php', - 'SebastianBergmann\\CodeCoverage\\Driver\\PHPDBG' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/PHPDBG.php', - 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/Xdebug.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\PathExistsButIsNotDirectoryException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/PathExistsButIsNotDirectoryException.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\PcovDriver' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/PcovDriver.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\PcovNotAvailableException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/PcovNotAvailableException.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgDriver' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/PhpdbgDriver.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgNotAvailableException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/PhpdbgNotAvailableException.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\Selector' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/Selector.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\WriteOperationFailedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/WriteOperationFailedException.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\WrongXdebugVersionException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/WrongXdebugVersionException.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2Driver' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/Xdebug2Driver.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2NotEnabledException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/Xdebug2NotEnabledException.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3Driver' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/Xdebug3Driver.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3NotEnabledException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/Xdebug3NotEnabledException.php', + 'SebastianBergmann\\CodeCoverage\\Driver\\XdebugNotAvailableException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/XdebugNotAvailableException.php', 'SebastianBergmann\\CodeCoverage\\Exception' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/Exception.php', 'SebastianBergmann\\CodeCoverage\\Filter' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Filter.php', 'SebastianBergmann\\CodeCoverage\\InvalidArgumentException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/InvalidArgumentException.php', - 'SebastianBergmann\\CodeCoverage\\MissingCoversAnnotationException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/MissingCoversAnnotationException.php', + 'SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverAvailableException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverAvailableException.php', + 'SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverWithPathCoverageSupportAvailableException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php', 'SebastianBergmann\\CodeCoverage\\Node\\AbstractNode' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/AbstractNode.php', 'SebastianBergmann\\CodeCoverage\\Node\\Builder' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/Builder.php', 'SebastianBergmann\\CodeCoverage\\Node\\Directory' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/Directory.php', 'SebastianBergmann\\CodeCoverage\\Node\\File' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/File.php', 'SebastianBergmann\\CodeCoverage\\Node\\Iterator' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/Iterator.php', + 'SebastianBergmann\\CodeCoverage\\ParserException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/ParserException.php', + 'SebastianBergmann\\CodeCoverage\\Percentage' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Percentage.php', + 'SebastianBergmann\\CodeCoverage\\ProcessedCodeCoverageData' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php', + 'SebastianBergmann\\CodeCoverage\\RawCodeCoverageData' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/RawCodeCoverageData.php', + 'SebastianBergmann\\CodeCoverage\\ReflectionException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/ReflectionException.php', + 'SebastianBergmann\\CodeCoverage\\ReportAlreadyFinalizedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/ReportAlreadyFinalizedException.php', 'SebastianBergmann\\CodeCoverage\\Report\\Clover' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Clover.php', + 'SebastianBergmann\\CodeCoverage\\Report\\Cobertura' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Cobertura.php', 'SebastianBergmann\\CodeCoverage\\Report\\Crap4j' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Crap4j.php', 'SebastianBergmann\\CodeCoverage\\Report\\Html\\Dashboard' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.php', 'SebastianBergmann\\CodeCoverage\\Report\\Html\\Directory' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php', @@ -921,26 +943,63 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Tests' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Tests.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Totals' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Totals.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Unit' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Unit.php', - 'SebastianBergmann\\CodeCoverage\\RuntimeException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/RuntimeException.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysisCacheNotConfiguredException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/StaticAnalysisCacheNotConfiguredException.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\Cache' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/Cache.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CacheWarmer' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/CacheWarmer.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingCoveredFileAnalyser' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/CachingCoveredFileAnalyser.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingUncoveredFileAnalyser' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/CachingUncoveredFileAnalyser.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CodeUnitFindingVisitor' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/CodeUnitFindingVisitor.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CoveredFileAnalyser' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/CoveredFileAnalyser.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ExecutableLinesFindingVisitor' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\IgnoredLinesFindingVisitor' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/IgnoredLinesFindingVisitor.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingCoveredFileAnalyser' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/ParsingCoveredFileAnalyser.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingUncoveredFileAnalyser' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/ParsingUncoveredFileAnalyser.php', + 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\UncoveredFileAnalyser' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/UncoveredFileAnalyser.php', + 'SebastianBergmann\\CodeCoverage\\TestIdMissingException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/TestIdMissingException.php', 'SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php', - 'SebastianBergmann\\CodeCoverage\\Util' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Util.php', 'SebastianBergmann\\CodeCoverage\\Version' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Version.php', + 'SebastianBergmann\\CodeCoverage\\XmlException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/XmlException.php', 'SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => __DIR__ . '/..' . '/sebastian/code-unit-reverse-lookup/src/Wizard.php', + 'SebastianBergmann\\CodeUnit\\ClassMethodUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/ClassMethodUnit.php', + 'SebastianBergmann\\CodeUnit\\ClassUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/ClassUnit.php', + 'SebastianBergmann\\CodeUnit\\CodeUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/CodeUnit.php', + 'SebastianBergmann\\CodeUnit\\CodeUnitCollection' => __DIR__ . '/..' . '/sebastian/code-unit/src/CodeUnitCollection.php', + 'SebastianBergmann\\CodeUnit\\CodeUnitCollectionIterator' => __DIR__ . '/..' . '/sebastian/code-unit/src/CodeUnitCollectionIterator.php', + 'SebastianBergmann\\CodeUnit\\Exception' => __DIR__ . '/..' . '/sebastian/code-unit/src/exceptions/Exception.php', + 'SebastianBergmann\\CodeUnit\\FunctionUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/FunctionUnit.php', + 'SebastianBergmann\\CodeUnit\\InterfaceMethodUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/InterfaceMethodUnit.php', + 'SebastianBergmann\\CodeUnit\\InterfaceUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/InterfaceUnit.php', + 'SebastianBergmann\\CodeUnit\\InvalidCodeUnitException' => __DIR__ . '/..' . '/sebastian/code-unit/src/exceptions/InvalidCodeUnitException.php', + 'SebastianBergmann\\CodeUnit\\Mapper' => __DIR__ . '/..' . '/sebastian/code-unit/src/Mapper.php', + 'SebastianBergmann\\CodeUnit\\NoTraitException' => __DIR__ . '/..' . '/sebastian/code-unit/src/exceptions/NoTraitException.php', + 'SebastianBergmann\\CodeUnit\\ReflectionException' => __DIR__ . '/..' . '/sebastian/code-unit/src/exceptions/ReflectionException.php', + 'SebastianBergmann\\CodeUnit\\TraitMethodUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/TraitMethodUnit.php', + 'SebastianBergmann\\CodeUnit\\TraitUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/TraitUnit.php', 'SebastianBergmann\\Comparator\\ArrayComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ArrayComparator.php', 'SebastianBergmann\\Comparator\\Comparator' => __DIR__ . '/..' . '/sebastian/comparator/src/Comparator.php', 'SebastianBergmann\\Comparator\\ComparisonFailure' => __DIR__ . '/..' . '/sebastian/comparator/src/ComparisonFailure.php', 'SebastianBergmann\\Comparator\\DOMNodeComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/DOMNodeComparator.php', 'SebastianBergmann\\Comparator\\DateTimeComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/DateTimeComparator.php', 'SebastianBergmann\\Comparator\\DoubleComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/DoubleComparator.php', + 'SebastianBergmann\\Comparator\\Exception' => __DIR__ . '/..' . '/sebastian/comparator/src/exceptions/Exception.php', 'SebastianBergmann\\Comparator\\ExceptionComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ExceptionComparator.php', 'SebastianBergmann\\Comparator\\Factory' => __DIR__ . '/..' . '/sebastian/comparator/src/Factory.php', 'SebastianBergmann\\Comparator\\MockObjectComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/MockObjectComparator.php', 'SebastianBergmann\\Comparator\\NumericComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/NumericComparator.php', 'SebastianBergmann\\Comparator\\ObjectComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ObjectComparator.php', 'SebastianBergmann\\Comparator\\ResourceComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ResourceComparator.php', + 'SebastianBergmann\\Comparator\\RuntimeException' => __DIR__ . '/..' . '/sebastian/comparator/src/exceptions/RuntimeException.php', 'SebastianBergmann\\Comparator\\ScalarComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ScalarComparator.php', 'SebastianBergmann\\Comparator\\SplObjectStorageComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/SplObjectStorageComparator.php', 'SebastianBergmann\\Comparator\\TypeComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/TypeComparator.php', + 'SebastianBergmann\\Complexity\\Calculator' => __DIR__ . '/..' . '/sebastian/complexity/src/Calculator.php', + 'SebastianBergmann\\Complexity\\Complexity' => __DIR__ . '/..' . '/sebastian/complexity/src/Complexity/Complexity.php', + 'SebastianBergmann\\Complexity\\ComplexityCalculatingVisitor' => __DIR__ . '/..' . '/sebastian/complexity/src/Visitor/ComplexityCalculatingVisitor.php', + 'SebastianBergmann\\Complexity\\ComplexityCollection' => __DIR__ . '/..' . '/sebastian/complexity/src/Complexity/ComplexityCollection.php', + 'SebastianBergmann\\Complexity\\ComplexityCollectionIterator' => __DIR__ . '/..' . '/sebastian/complexity/src/Complexity/ComplexityCollectionIterator.php', + 'SebastianBergmann\\Complexity\\CyclomaticComplexityCalculatingVisitor' => __DIR__ . '/..' . '/sebastian/complexity/src/Visitor/CyclomaticComplexityCalculatingVisitor.php', + 'SebastianBergmann\\Complexity\\Exception' => __DIR__ . '/..' . '/sebastian/complexity/src/Exception/Exception.php', + 'SebastianBergmann\\Complexity\\RuntimeException' => __DIR__ . '/..' . '/sebastian/complexity/src/Exception/RuntimeException.php', 'SebastianBergmann\\Diff\\Chunk' => __DIR__ . '/..' . '/sebastian/diff/src/Chunk.php', 'SebastianBergmann\\Diff\\ConfigurationException' => __DIR__ . '/..' . '/sebastian/diff/src/Exception/ConfigurationException.php', 'SebastianBergmann\\Diff\\Diff' => __DIR__ . '/..' . '/sebastian/diff/src/Diff.php', @@ -964,12 +1023,23 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'SebastianBergmann\\FileIterator\\Facade' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Facade.php', 'SebastianBergmann\\FileIterator\\Factory' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Factory.php', 'SebastianBergmann\\FileIterator\\Iterator' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Iterator.php', - 'SebastianBergmann\\GlobalState\\Blacklist' => __DIR__ . '/..' . '/sebastian/global-state/src/Blacklist.php', 'SebastianBergmann\\GlobalState\\CodeExporter' => __DIR__ . '/..' . '/sebastian/global-state/src/CodeExporter.php', 'SebastianBergmann\\GlobalState\\Exception' => __DIR__ . '/..' . '/sebastian/global-state/src/exceptions/Exception.php', + 'SebastianBergmann\\GlobalState\\ExcludeList' => __DIR__ . '/..' . '/sebastian/global-state/src/ExcludeList.php', 'SebastianBergmann\\GlobalState\\Restorer' => __DIR__ . '/..' . '/sebastian/global-state/src/Restorer.php', 'SebastianBergmann\\GlobalState\\RuntimeException' => __DIR__ . '/..' . '/sebastian/global-state/src/exceptions/RuntimeException.php', 'SebastianBergmann\\GlobalState\\Snapshot' => __DIR__ . '/..' . '/sebastian/global-state/src/Snapshot.php', + 'SebastianBergmann\\Invoker\\Exception' => __DIR__ . '/..' . '/phpunit/php-invoker/src/exceptions/Exception.php', + 'SebastianBergmann\\Invoker\\Invoker' => __DIR__ . '/..' . '/phpunit/php-invoker/src/Invoker.php', + 'SebastianBergmann\\Invoker\\ProcessControlExtensionNotLoadedException' => __DIR__ . '/..' . '/phpunit/php-invoker/src/exceptions/ProcessControlExtensionNotLoadedException.php', + 'SebastianBergmann\\Invoker\\TimeoutException' => __DIR__ . '/..' . '/phpunit/php-invoker/src/exceptions/TimeoutException.php', + 'SebastianBergmann\\LinesOfCode\\Counter' => __DIR__ . '/..' . '/sebastian/lines-of-code/src/Counter.php', + 'SebastianBergmann\\LinesOfCode\\Exception' => __DIR__ . '/..' . '/sebastian/lines-of-code/src/Exception/Exception.php', + 'SebastianBergmann\\LinesOfCode\\IllogicalValuesException' => __DIR__ . '/..' . '/sebastian/lines-of-code/src/Exception/IllogicalValuesException.php', + 'SebastianBergmann\\LinesOfCode\\LineCountingVisitor' => __DIR__ . '/..' . '/sebastian/lines-of-code/src/LineCountingVisitor.php', + 'SebastianBergmann\\LinesOfCode\\LinesOfCode' => __DIR__ . '/..' . '/sebastian/lines-of-code/src/LinesOfCode.php', + 'SebastianBergmann\\LinesOfCode\\NegativeValueException' => __DIR__ . '/..' . '/sebastian/lines-of-code/src/Exception/NegativeValueException.php', + 'SebastianBergmann\\LinesOfCode\\RuntimeException' => __DIR__ . '/..' . '/sebastian/lines-of-code/src/Exception/RuntimeException.php', 'SebastianBergmann\\ObjectEnumerator\\Enumerator' => __DIR__ . '/..' . '/sebastian/object-enumerator/src/Enumerator.php', 'SebastianBergmann\\ObjectEnumerator\\Exception' => __DIR__ . '/..' . '/sebastian/object-enumerator/src/Exception.php', 'SebastianBergmann\\ObjectEnumerator\\InvalidArgumentException' => __DIR__ . '/..' . '/sebastian/object-enumerator/src/InvalidArgumentException.php', @@ -980,25 +1050,37 @@ class ComposerStaticInit8d81387c26c532d7a48feda4036c56c7 'SebastianBergmann\\RecursionContext\\Exception' => __DIR__ . '/..' . '/sebastian/recursion-context/src/Exception.php', 'SebastianBergmann\\RecursionContext\\InvalidArgumentException' => __DIR__ . '/..' . '/sebastian/recursion-context/src/InvalidArgumentException.php', 'SebastianBergmann\\ResourceOperations\\ResourceOperations' => __DIR__ . '/..' . '/sebastian/resource-operations/src/ResourceOperations.php', - 'SebastianBergmann\\Timer\\Exception' => __DIR__ . '/..' . '/phpunit/php-timer/src/Exception.php', - 'SebastianBergmann\\Timer\\RuntimeException' => __DIR__ . '/..' . '/phpunit/php-timer/src/RuntimeException.php', + 'SebastianBergmann\\Template\\Exception' => __DIR__ . '/..' . '/phpunit/php-text-template/src/exceptions/Exception.php', + 'SebastianBergmann\\Template\\InvalidArgumentException' => __DIR__ . '/..' . '/phpunit/php-text-template/src/exceptions/InvalidArgumentException.php', + 'SebastianBergmann\\Template\\RuntimeException' => __DIR__ . '/..' . '/phpunit/php-text-template/src/exceptions/RuntimeException.php', + 'SebastianBergmann\\Template\\Template' => __DIR__ . '/..' . '/phpunit/php-text-template/src/Template.php', + 'SebastianBergmann\\Timer\\Duration' => __DIR__ . '/..' . '/phpunit/php-timer/src/Duration.php', + 'SebastianBergmann\\Timer\\Exception' => __DIR__ . '/..' . '/phpunit/php-timer/src/exceptions/Exception.php', + 'SebastianBergmann\\Timer\\NoActiveTimerException' => __DIR__ . '/..' . '/phpunit/php-timer/src/exceptions/NoActiveTimerException.php', + 'SebastianBergmann\\Timer\\ResourceUsageFormatter' => __DIR__ . '/..' . '/phpunit/php-timer/src/ResourceUsageFormatter.php', + 'SebastianBergmann\\Timer\\TimeSinceStartOfRequestNotAvailableException' => __DIR__ . '/..' . '/phpunit/php-timer/src/exceptions/TimeSinceStartOfRequestNotAvailableException.php', 'SebastianBergmann\\Timer\\Timer' => __DIR__ . '/..' . '/phpunit/php-timer/src/Timer.php', 'SebastianBergmann\\Type\\CallableType' => __DIR__ . '/..' . '/sebastian/type/src/CallableType.php', 'SebastianBergmann\\Type\\Exception' => __DIR__ . '/..' . '/sebastian/type/src/exception/Exception.php', + 'SebastianBergmann\\Type\\FalseType' => __DIR__ . '/..' . '/sebastian/type/src/FalseType.php', 'SebastianBergmann\\Type\\GenericObjectType' => __DIR__ . '/..' . '/sebastian/type/src/GenericObjectType.php', 'SebastianBergmann\\Type\\IterableType' => __DIR__ . '/..' . '/sebastian/type/src/IterableType.php', + 'SebastianBergmann\\Type\\LogicException' => __DIR__ . '/..' . '/sebastian/type/src/exception/LogicException.php', + 'SebastianBergmann\\Type\\MixedType' => __DIR__ . '/..' . '/sebastian/type/src/MixedType.php', 'SebastianBergmann\\Type\\NullType' => __DIR__ . '/..' . '/sebastian/type/src/NullType.php', 'SebastianBergmann\\Type\\ObjectType' => __DIR__ . '/..' . '/sebastian/type/src/ObjectType.php', + 'SebastianBergmann\\Type\\ReflectionMapper' => __DIR__ . '/..' . '/sebastian/type/src/ReflectionMapper.php', 'SebastianBergmann\\Type\\RuntimeException' => __DIR__ . '/..' . '/sebastian/type/src/exception/RuntimeException.php', 'SebastianBergmann\\Type\\SimpleType' => __DIR__ . '/..' . '/sebastian/type/src/SimpleType.php', + 'SebastianBergmann\\Type\\StaticType' => __DIR__ . '/..' . '/sebastian/type/src/StaticType.php', 'SebastianBergmann\\Type\\Type' => __DIR__ . '/..' . '/sebastian/type/src/Type.php', 'SebastianBergmann\\Type\\TypeName' => __DIR__ . '/..' . '/sebastian/type/src/TypeName.php', + 'SebastianBergmann\\Type\\UnionType' => __DIR__ . '/..' . '/sebastian/type/src/UnionType.php', 'SebastianBergmann\\Type\\UnknownType' => __DIR__ . '/..' . '/sebastian/type/src/UnknownType.php', 'SebastianBergmann\\Type\\VoidType' => __DIR__ . '/..' . '/sebastian/type/src/VoidType.php', 'SebastianBergmann\\Version' => __DIR__ . '/..' . '/sebastian/version/src/Version.php', 'SqlFormatter' => __DIR__ . '/..' . '/jdorn/sql-formatter/lib/SqlFormatter.php', 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', - 'Text_Template' => __DIR__ . '/..' . '/phpunit/php-text-template/src/Template.php', 'TheSeer\\Tokenizer\\Exception' => __DIR__ . '/..' . '/theseer/tokenizer/src/Exception.php', 'TheSeer\\Tokenizer\\NamespaceUri' => __DIR__ . '/..' . '/theseer/tokenizer/src/NamespaceUri.php', 'TheSeer\\Tokenizer\\NamespaceUriException' => __DIR__ . '/..' . '/theseer/tokenizer/src/NamespaceUriException.php', diff --git a/app/vendor/composer/ca-bundle/composer.json b/app/vendor/composer/ca-bundle/composer.json index 5213e9763..ed6a1b364 100644 --- a/app/vendor/composer/ca-bundle/composer.json +++ b/app/vendor/composer/ca-bundle/composer.json @@ -30,7 +30,7 @@ "symfony/phpunit-bridge": "^4.2 || ^5", "phpstan/phpstan": "^0.12.55", "psr/log": "^1.0", - "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" + "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" }, "autoload": { "psr-4": { diff --git a/app/vendor/composer/ca-bundle/res/cacert.pem b/app/vendor/composer/ca-bundle/res/cacert.pem index 264923b3e..456003b9e 100644 --- a/app/vendor/composer/ca-bundle/res/cacert.pem +++ b/app/vendor/composer/ca-bundle/res/cacert.pem @@ -1,7 +1,7 @@ ## ## Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Tue May 25 03:12:05 2021 GMT +## Certificate data from Mozilla as of: Mon Jul 5 21:35:54 2021 GMT ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates @@ -14,7 +14,9 @@ ## Just configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version 1.28. -## SHA256: e292bd4e2d500c86df45b830d89417be5c42ee670408f1d2c454c63d8a782865 +## SHA256: c8f6733d1ff4e6a4769c182971a1234f95ae079247a9c439a13423fe8ba5c24f +## +## Modified to remove expiring DST Root CA X3: 2021-09-25. ## @@ -156,38 +158,6 @@ Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z 12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== -----END CERTIFICATE----- -QuoVadis Root CA -================ ------BEGIN CERTIFICATE----- -MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE -ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 -eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz -MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp -cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD -EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk -J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL -F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL -YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen -AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w -PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y -ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 -MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj -YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs -ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh -Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW -Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu -BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw -FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 -tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo -fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul -LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x -gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi -5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi -5nrQNiOKSnQ2+Q== ------END CERTIFICATE----- - QuoVadis Root CA 2 ================== -----BEGIN CERTIFICATE----- @@ -275,26 +245,6 @@ s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ FL39vmwLAw== -----END CERTIFICATE----- -Sonera Class 2 Root CA -====================== ------BEGIN CERTIFICATE----- -MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG -U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw -NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh -IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 -/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT -dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG -f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P -tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH -nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT -XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt -0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI -cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph -Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx -EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH -llpwrN9M ------END CERTIFICATE----- - XRamp Global CA Root ==================== -----BEGIN CERTIFICATE----- @@ -433,26 +383,6 @@ mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K -----END CERTIFICATE----- -DST Root CA X3 -============== ------BEGIN CERTIFICATE----- -MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK -ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X -DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 -cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT -rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 -UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy -xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d -utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T -AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ -MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug -dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE -GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw -RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS -fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ ------END CERTIFICATE----- - SwissSign Gold CA - G2 ====================== -----BEGIN CERTIFICATE----- @@ -1194,27 +1124,6 @@ OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== -----END CERTIFICATE----- -Trustis FPS Root CA -=================== ------BEGIN CERTIFICATE----- -MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG -EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 -IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV -BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ -RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk -H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa -cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt -o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA -AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd -BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c -GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC -yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P -8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV -l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl -iB6XzCGcKQENZetX2fNXlrtIzYE= ------END CERTIFICATE----- - Buypass Class 2 Root CA ======================= -----BEGIN CERTIFICATE----- @@ -3136,3 +3045,112 @@ gLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZk vLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+ CAezNIm8BZ/3Hobui3A= -----END CERTIFICATE----- + +GLOBALTRUST 2020 +================ +-----BEGIN CERTIFICATE----- +MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQVQx +IzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVT +VCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYxMDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAh +BgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAy +MDIwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWi +D59bRatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9ZYybNpyrO +VPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3QWPKzv9pj2gOlTblzLmM +CcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPwyJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCm +fecqQjuCgGOlYx8ZzHyyZqjC0203b+J+BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKA +A1GqtH6qRNdDYfOiaxaJSaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9OR +JitHHmkHr96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj04KlG +DfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9MedKZssCz3AwyIDMvU +clOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIwq7ejMZdnrY8XD2zHc+0klGvIg5rQ +mjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1Ud +IwQYMBaAFNwuH9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA +VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJCXtzoRlgHNQIw +4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd6IwPS3BD0IL/qMy/pJTAvoe9 +iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS +8cE54+X1+NZK3TTN+2/BT+MAi1bikvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2 +HcqtbepBEX4tdJP7wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxS +vTOBTI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6CMUO+1918 +oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn4rnvyOL2NSl6dPrFf4IF +YqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+IaFvowdlxfv1k7/9nR4hYJS8+hge9+6jl +gqispdNpQ80xiEmEU5LAsTkbOYMBMMTyqfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== +-----END CERTIFICATE----- + +ANF Secure Server Root CA +========================= +-----BEGIN CERTIFICATE----- +MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNVBAUTCUc2MzI4 +NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lv +bjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNVBAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3Qg +Q0EwHhcNMTkwOTA0MTAwMDM4WhcNMzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEw +MQswCQYDVQQGEwJFUzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQw +EgYDVQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9vdCBDQTCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCjcqQZAZ2cC4Ffc0m6p6zz +BE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9qyGFOtibBTI3/TO80sh9l2Ll49a2pcbnv +T1gdpd50IJeh7WhM3pIXS7yr/2WanvtH2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcv +B2VSAKduyK9o7PQUlrZXH1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXse +zx76W0OLzc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyRp1RM +VwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQzW7i1o0TJrH93PB0j +7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/SiOL9V8BY9KHcyi1Swr1+KuCLH5z +JTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJnLNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe +8TZBAQIvfXOn3kLMTOmJDVb3n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVO +Hj1tyRRM4y5Bu8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj +o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAOBgNVHQ8BAf8E +BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEATh65isagmD9uw2nAalxJ +UqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzx +j6ptBZNscsdW699QIyjlRRA96Gejrw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDt +dD+4E5UGUcjohybKpFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM +5gf0vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjqOknkJjCb +5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ/zo1PqVUSlJZS2Db7v54 +EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ92zg/LFis6ELhDtjTO0wugumDLmsx2d1H +hk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI+PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGy +g77FGr8H6lnco4g175x2MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3 +r5+qPeoott7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= +-----END CERTIFICATE----- + +Certum EC-384 CA +================ +-----BEGIN CERTIFICATE----- +MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQswCQYDVQQGEwJQ +TDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2 +MDcyNDU0WhcNNDMwMzI2MDcyNDU0WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERh +dGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx +GTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATEKI6rGFtq +vm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7TmFy8as10CW4kjPMIRBSqn +iBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68KjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFI0GZnQkdjrzife81r1HfS+8EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNo +ADBlAjADVS2m5hjEfO/JUG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0 +QoSZ/6vnnvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= +-----END CERTIFICATE----- + +Certum Trusted Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6MQswCQYDVQQG +EwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0g +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0Ew +HhcNMTgwMzE2MTIxMDEzWhcNNDMwMzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMY +QXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZn0EGze2jusDbCSzBfN8p +fktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/qp1x4EaTByIVcJdPTsuclzxFUl6s1wB52 +HO8AU5853BSlLCIls3Jy/I2z5T4IHhQqNwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2 +fJmItdUDmj0VDT06qKhF8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGt +g/BKEiJ3HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGamqi4 +NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi7VdNIuJGmj8PkTQk +fVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSFytKAQd8FqKPVhJBPC/PgP5sZ0jeJ +P/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0PqafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSY +njYJdmZm/Bo/6khUHL4wvYBQv3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHK +HRzQ+8S1h9E6Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 +vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQADggIBAEii1QAL +LtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4WxmB82M+w85bj/UvXgF2Ez8s +ALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvozMrnadyHncI013nR03e4qllY/p0m+jiGPp2K +h2RX5Rc64vmNueMzeMGQ2Ljdt4NR5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8 +CYyqOhNf6DR5UMEQGfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA +4kZf5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq0Uc9Nneo +WWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7DP78v3DSk+yshzWePS/Tj +6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTMqJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmT +OPQD8rv7gmsHINFSH5pkAnuYZttcTVoP0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZck +bxJF0WddCajJFdr60qZfE2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb +-----END CERTIFICATE----- diff --git a/app/vendor/composer/composer/.gitattributes b/app/vendor/composer/composer/.gitattributes index 51b431136..2ee43db45 100644 --- a/app/vendor/composer/composer/.gitattributes +++ b/app/vendor/composer/composer/.gitattributes @@ -2,7 +2,7 @@ * text=auto eol=lf # These files are always considered text and should use LF. -# See core.whitespace @ http://git-scm.com/docs/git-config for whitespace flags. +# See core.whitespace @ https://git-scm.com/docs/git-config for whitespace flags. *.php text eol=lf whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4 diff=php *.json text eol=lf whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4 *.test text eol=lf whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4 @@ -15,3 +15,4 @@ .travis.yml export-ignore appveyor.yml export-ignore phpunit.xml.dist export-ignore +/phpstan/ export-ignore diff --git a/app/vendor/composer/composer/.gitignore b/app/vendor/composer/composer/.gitignore index f9d78df20..0b1fb77c0 100644 --- a/app/vendor/composer/composer/.gitignore +++ b/app/vendor/composer/composer/.gitignore @@ -4,8 +4,9 @@ /composer.phar /vendor /nbproject +.phpunit.result.cache phpunit.xml .vagrant Vagrantfile .idea -.php_cs.cache \ No newline at end of file +.php_cs.cache diff --git a/app/vendor/composer/composer/CHANGELOG.md b/app/vendor/composer/composer/CHANGELOG.md index bc6a92fc8..983bc68a3 100644 --- a/app/vendor/composer/composer/CHANGELOG.md +++ b/app/vendor/composer/composer/CHANGELOG.md @@ -1,3 +1,339 @@ +### [2.1.9] 2021-10-05 + + * Security: Fixed command injection vulnerability on Windows (GHSA-frqg-7g38-6gcf / CVE-2021-41116) + * Fixed classmap parsing with a new class parser which does not rely on regexes anymore (#10107) + * Fixed inline git credentials showing up in output in some conditions (#10115) + * Fixed support for running updates while offline as long as the cache contains enough information (#10116) + * Fixed `show --all foo/bar` which as of 2.0.0 was not showing all versions anymore but only the installed one (#10095) + * Fixed VCS repos ignoring some versions silently when the API rate limit is reached (#10132) + * Fixed CA bundle to remove the expired Let's Encrypt root CA + +### [2.1.8] 2021-09-15 + + * Fixed regression in 2.1.7 when parsing classmaps in files containing invalid Unicode (#10102) + +### [2.1.7] 2021-09-14 + + * Added many type annotations internally, which may have an effect on CI/static analysis for people using Composer as a dependency. This work will continue in following releases + * Fixed regression in 2.1.6 when parsing classmaps with empty heredocs (#10067) + * Fixed regression in 2.1.6 where list command was not showing plugin commands (#10075) + * Fixed issue handling package updates where the package type changed (#10076) + * Fixed docker being detected as WSL when run inside WSL (#10094) + +### [2.1.6] 2021-08-19 + + * Updated internal PHAR signatures to be SHA512 instead of SHA1 + * Fixed uncaught exception handler regression (#10022) + * Fixed more PHP 8.1 deprecation warnings (#10036, #10038, #10061) + * Fixed corrupted zips in the cache from blocking installs until a cache clear, the bad archives are now deleted automatically on first failure (#10028) + * Fixed URL sanitizer handling of new github tokens (#10048) + * Fixed issue finding classes with very long heredocs in classmap autoload (#10050) + * Fixed proc_open being required for simple installs from zip, as well as diagnose (#9253) + * Fixed path repository bug causing symlinks to be left behind after a package is uninstalled (#10023) + * Fixed issue in 7-zip support on windows with certain archives (#10058) + * Fixed bootstrapping process to avoid loading the composer.json and plugins until necessary, speeding things up slightly (#10064) + * Fixed lib-openssl detection on FreeBSD (#10046) + * Fixed support for `ircs://` protocol for support.irc composer.json entries + +### [2.1.5] 2021-07-23 + + * Fixed `create-project` creating a `php:` directory in the directory it was executed in (#10020, #10021) + * Fixed curl downloader to respect default_socket_timeout if it is bigger than our default 300s (#10018) + +### [2.1.4] 2021-07-22 + + * Fixed PHP 8.1 deprecation warnings (#10008) + * Fixed support for working within UNC/WSL paths on Windows (#9993) + * Fixed 7-zip support to also be looked up on Linux/macOS as 7z or 7zz (#9951) + * Fixed repositories' `only`/`exclude` properties to avoid matching names as sub-strings of full package names (#10001) + * Fixed open_basedir regression from #9855 + * Fixed schema errors being reported incorrectly in some conditions (#9986) + * Fixed `archive` command not working with async archive extraction + * Fixed `init` command being able to generate an invalid composer.json (#9986) + +### [2.1.3] 2021-06-09 + + * Add "symlink" option for "bin-compat" config to force symlinking even on WSL/Windows (#9959) + * Fixed source binaries not being made executable when symlinks cannot be used (#9961) + * Fixed more deletion edge cases (#9955, #9956) + * Fixed `dump-autoload` command not dispatching scripts anymore, regressed in 2.1.2 (#9954) + +### [2.1.2] 2021-06-07 + + * Added `--dev` to `dump-autoload` command to allow force-dumping dev autoload rules even if dev requirements are not present (#9946) + * Fixed `--no-scripts` disabling events for plugins too instead of only disabling script handlers, using `--no-plugins` is the way to disable plugins (#9942) + * Fixed handling of deletions during package installs on some filesystems (#9945, #9947) + * Fixed undefined array access when using "@php " in a script handler (#9943) + * Fixed usage of InstalledVersions when loaded from composer/composer installed as a dependency and runtime Composer is v1 (#9937) + +### [2.1.1] 2021-06-04 + + * Fixed regression in autoload generation when --no-scripts is used (#9935) + * Fixed `outdated` color legend to have the right color in the right place (#9939) + * Fixed PCRE bug causing a previously valid pattern to fail to match (#9941) + * Fixed JsonFile::validateSchema regression when used as a library to validate custom schema files (#9938) + +### [2.1.0] 2021-06-03 + + * Fixed PHP 8.1 deprecation warning (#9932) + * Fixed env var handling when variables_order includes E and symfony/console 3.3.15+ is in use (#9930) + +### [2.1.0-RC1] 2021-06-02 + + * Bumped `composer-runtime-api` and `composer-plugin-api` to `2.1.0` + * UX Change: The default install method for packages is now always dist/zip, even for dev packages, added `--prefer-install=auto` if you want the old behavior (#9603) + * UX Change: Packages from `path` repositories which are symlinked in the vendor dir will always be updated in partial updates to avoid mistakes when the original composer.json changes but the symlinked package is not explicitly updated (#9765) + * Added `reinstall` command that takes one or more package names, including wildcard (`*`) support, and removes then reinstalls them in the exact same version they had (#9915) + * Added support for parallel package installs on Windows via [7-Zip](https://www.7-zip.org/) if it is installed (#9875) + * Added detection of invalid composer.lock files that do not fullfil the composer.json requirements to `validate` command (#9899) + * Added `InstalledVersions::getInstalledPackagesByType(string $type)` to retrieve installed plugins for example, [read more](https://getcomposer.org/doc/07-runtime.md#knowing-which-packages-of-a-given-type-are-installed) (#9699) + * Added `InstalledVersions::getInstalledPath(string $packageName)` to retrieve the install path of a given package, [read more](https://getcomposer.org/doc/07-runtime.md#knowing-the-path-in-which-a-package-is-installed) (#9699) + * Added flag to `InstalledVersions::isInstalled()` to allow excluding dev requirements from that check (#9682) + * Added support for PHP 8.1 enums in autoloader / classmap generation (#9670) + * Added support for using `@php binary-name foo` in scripts to refer to a binary without using its full path, but forcing to use the same PHP version as Composer used (#9726) + * Added `--format=json` support to the `fund` command (#9678) + * Added `--format=json` support to the `search` command (#9747) + * Added `COMPOSER_DEV_MODE` env var definition within the run-script command for compatibility (#9793) + * Added async uninstall of packages (#9618) + * Added color legend to `outdated` and `show --latest` commands (#9716) + * Added `secure-svn-domains` config option to mark secure svn:// hostnames and suppress warnings without disabling secure-http (#9872) + * Added `gitlab-protocol` config option to allow forcing `git` or `http` URLs for all gitlab repos loaded inline, instead of the default of git for private and http for public (#9401) + * Added generation of autoload rules in `init` command (#9829) + * Added source/dist validation in `validate` command + * Added automatic detection of WSL when generating binaries and use `bin-compat:full` implicitly (#9855) + * Added automatic detection of the --no-dev state for `dump-autoload` based on the last install run (#9714) + * Added warning/prompt to `require` command if requiring a package that already exists in require-dev or vice versa (#9542) + * Added information about package conflicts in the `why`/`why-not` commands (#9693) + * Removed version argument from `why` command as it was not needed (#9729) + * Fixed `why-not` command to always require a specific version as it is useless without (#9729) + * Fixed cache dir on macOS to follow OS guidelines, it is now in ~/Library/Caches/composer (#9898) + * Fixed composer.json JSON schema to avoid having name/description required by default (#9912) + * Fixed support for running inside WSL paths from a Windows PHP/Composer (#9861) + * Fixed InstalledVersions to include the original doc blocks when installed from a Composer phar file + * Fixed `require` command to use `*` as constraint for extensions bundled with PHP instead of duplicating the PHP constraint (#9483) + * Fixed `search` output to be aligned and avoid wrapped long lines to be more readable (#9455) + * Error output improvements for many cases (#9876, #9837, #9928, and some smaller improvements) + +### [2.0.14] 2021-05-21 + + * Updated composer/xdebug-handler to 2.0 which adds supports for Xdebug 3 + * Fixed handling of inline-update-constraints with references or stability flags (#9847) + * Fixed async processes erroring in an unclear way when they failed to start (#9808) + * Fixed support for the upcoming Symfony 6.0 release when Composer is installed as a library (#9896) + * Fixed progress output missing newlines on PowerShell, and disable progress output by default when CI env var is present (#9621) + * Fixed support for Vagrant/VirtualBox filesystem slowness when installing binaries from packages (#9627) + * Fixed type annotations for the InstalledVersions class + * Deprecated InstalledVersions::getRawData in favor of InstalledVersions::getAllRawData (#9816) + +### [2.0.13] 2021-04-27 + + * Security: Fixed command injection vulnerability in HgDriver/HgDownloader and hardened other VCS drivers and downloaders (GHSA-h5h8-pc6h-jvvx / CVE-2021-29472) + * Fixed install step at the end of the init command to take new dependencies into account correctly + * Fixed `update --lock` listing updates which were not really happening (#9812) + * Fixed support for --no-dev combined with --locked in outdated and show commands (#9788) + +### [2.0.12] 2021-04-01 + + * Fixed support for new GitHub OAuth token format (#9757) + * Fixed support for Vagrant/VirtualBox filesystem slowness by adding short sleeps in some places (#9627) + * Fixed unclear error reporting when a package is in the lock file but not in the remote repositories (#9750) + * Fixed processes silently ignoring the CWD when it does not exist + * Fixed new Windows bin handling to avoid proxying phar files (#9742) + * Fixed issue extracting archives into paths that already exist, fixing problems with some custom installers (composer/installers#479) + * Fixed support for branch names starting with master/trunk/default (#9739) + * Fixed self-update to preserve phar file permissions on Windows (#9733) + * Fixed detection of hg version when localized (#9753) + * Fixed git execution failures to also include the stdout output (#9720) + +### [2.0.11] 2021-02-24 + + * Reverted "Fixed runtime autoloader registration (for plugins and script handlers) to prefer the project dependencies over the bundled Composer ones" as it caused more problems than expected + +### [2.0.10] 2021-02-23 + + * Added COMPOSER_MAX_PARALLEL_HTTP to let people set a lower amount of parallel requests if needed + * Fixed autoloader registration when plugins are loaded, which may impact plugins relying on this bug (if you use `symfony/flex` make sure you upgrade it to 1.12.2+ to fix `dump-env` issues) + * Fixed `exec` command suppressing output in some circumstances + * Fixed Windows/cmd.exe support for script handlers defined as `path/to/foo`, which are now rewritten internally to `path\to\foo` when needed + * Fixed bin handling on Windows for PHP scripts, to more closely match symlinks and allow `@php vendor/bin/foo` to work cross-platform + * Fixed Git for Windows/Git Bash not being detected correctly as an interactive shell (regression since 2.0.7) + * Fixed regression handling some private Bitbucket repository clones + * Fixed Ctrl-C/SIGINT handling during downloads to correctly abort as soon as possible + * Fixed runtime autoloader registration (for plugins and script handlers) to prefer the project dependencies over the bundled Composer ones + * Fixed numeric default branches being aliased as 9999999-dev internally. This alias now only applies to default branches being non-numeric (e.g. `dev-main`) + * Fixed support for older lib-sodium versions + * Fixed various minor issues + +### [2.0.9] 2021-01-27 + + * Added warning if the curl extension is not enabled as it significantly degrades performance + * Fixed InstalledVersions to report all packages when several vendor dirs are present in the same runtime + * Fixed download speed when downloading large files + * Fixed `archive` and path repo copies mishandling some .gitignore paths + * Fixed root package classes not being available to the plugins/scripts during the initial install + * Fixed cache writes to be atomic and better support multiple Composer processes running in parallel + * Fixed preg jit issues when `config` or `require` modifies large composer.json files + * Fixed compatibility with envs having open_basedir restrictions + * Fixed exclude-from-classmap causing regex issues when having too many paths + * Fixed compatibility issue with Symfony 4/5 + * Several small performance and debug output improvements + +### [2.0.8] 2020-12-03 + + * Fixed packages with aliases not matching conflicts which match the alias + * Fixed invalid reports of uncommitted changes when using non-default remotes in vendor dir + * Fixed curl error handling edge cases + * Fixed cached git repositories becoming stale by having a `git gc` applied to them periodically + * Fixed issue initializing plugins when using dev packages + * Fixed update --lock / mirrors failing to update in some edge cases + * Fixed partial update with --with-dependencies failing in some edge cases with some nonsensical error + +### [2.0.7] 2020-11-13 + + * Fixed detection of TTY mode, made input non-interactive automatically if STDIN is not a TTY + * Fixed root aliases not being present in lock file if not required by anything else + * Fixed `remove` command requiring a lock file to be present + * Fixed `Composer\InstalledVersions` to always contain up to date data during installation + * Fixed `status` command breaking on slow networks + * Fixed order of POST_PACKAGE_* events to occur together once all installations of a package batch are done + +### [2.0.6] 2020-11-07 + + * Fixed regression in 2.0.5 dealing with custom installers which do not pass absolute paths + +### [2.0.5] 2020-11-06 + + * Disabled platform-check verification of extensions by default (now defaulting `php-only`), set platform-check to `true` if you want a complete check + * Improved platform-check handling of issue reporting + * Fixed platform-check to only check non-dev requires even if require-dev dependencies are installed + * Fixed issues dealing with custom installers which return trailing slashes in getInstallPath (ideally avoid doing this as there might be other issues left) + * Fixed issues when curl functions are disabled + * Fixed gitlab-domains/github-domains to make sure if they are overridden the default value remains present + * Fixed issues removing/upgrading packages from path repositories on Windows + * Fixed regression in 2.0.4 when handling of git@bitbucket.org URLs in vcs repositories + * Fixed issue running create-project in current directory on Windows + +### [2.0.4] 2020-10-30 + + * Fixed `check-platform-req` command not being clear on what packages are checked, and added a --lock flag to explicitly check the locked packages + * Fixed `config` & `create-project` adding of repositories to make sure they are prepended as order is much more important in Composer 2, also added a --append flag to `config` to restore the old behavior in the unlikely case this is needed + * Fixed curl downloader failing on old PHP releases or when using self-signed SSL certificates + * Fixed Bitbucket API authentication issue + +### [2.0.3] 2020-10-28 + + * Fixed bug in `outdated` command where dev packages with branch-aliases where always shown as being outdated + * Fixed issue in lock file interoperability with composer 1.x when using `dev-master as xxx` aliases + * Fixed new `--locked` option being missing from `outdated` command, for checking outdated packages directly from the lock file + * Fixed a few debug/error reporting strings + +### [2.0.2] 2020-10-25 + + * Fixed regression handling `composer show -s` in projects where no version can be guessed from VCS + * Fixed regression handling partial updates/`require` when a lock file was missing + * Fixed interop issue with plugins that need to update dist URLs of packages, [see docs](https://getcomposer.org/doc/articles/plugins.md#plugin-modifies-downloads) if you need this + +### [2.0.1] 2020-10-24 + + * Fixed crash on PHP8 + +### [2.0.0] 2020-10-24 + + * Fixed proxy handling issues when combined with our new curl-based downloader + * Fixed solver bug resulting in endless loops in some cases + * Fixed solver output being extremely long due to learnt rules + * Fixed solver bug with multi literals + * Fixed a couple minor regressions + +### [2.0.0-RC2] 2020-10-14 + + * Breaking: Removed `OperationInterface::getReason` as the data was not accurate + * Added automatic removal of packages which are not required anymore whenever an update is done, this will purge packages previously left over by partial updates and `require`/`remove` + * Added shorthand aliases `-w` for `--with-dependencies` and `-W` for `--with-all-dependencies` on `update`/`require`/`remove` commands + * Added `COMPOSER_DEBUG_EVENTS=1` env var support for plugin authors to figure out which events are triggered when + * Added `setCustomCacheKey` to `PreFileDownloadEvent` and fixed a cache bug for integrations changing the processed url of package archives + * Added `Composer\Util\SyncHelper` for plugin authors to deal with async Promises more easily + * Added `$composer->getLoop()->getHttpDownloader()` to get access to the main HttpDownloader instance in plugins + * Added a non-zero exit code (2) and warning to `remove` command when a package to be removed could not be removed + * Added `--apcu-autoloader-prefix` (or `--apcu-prefix` for `dump-autoload` command) flag to let people use apcu autoloading in a deterministic output way if that is needed + * Fixed version guesser to look at remote branches as well as local ones + * Lots of minor bug fixes and improvements + +### [2.0.0-RC1] 2020-09-10 + + * Added more advanced filtering to avoid loading all versions of all referenced packages when resolving dependencies, which should reduce memory usage further in some cases + * Added support for many new `lib-*` packages in the platform repository and improved version detection for some `ext-*` and `lib-*` packages + * Added an `--ask` flag to `create-project` command to make Composer prompt for the install dir name, [useful for project install instructions](https://github.com/composer/composer/pull/9181) + * Added support for tar in artifact repositories + * Added a `cache-read-only` config option to make the cache usable in read only mode for containers and such + * Added better error reporting for a few more specific cases + * Added a new optional `available-package-patterns` attribute for v2-format Composer repositories, see [UPGRADE](UPGRADE-2.0.md) for details + * Fixed more PHP 8 compatibility issues + * Lots of minor bug fixes for regressions + +### [2.0.0-alpha3] 2020-08-03 + + * Breaking: Zip archives loaded by artifact repositories must now have a composer.json on top level, or a max of one folder on top level of the archive + * Added --no-dev support to `show` and `outdated` commands to skip dev requirements + * Added support for multiple --repository flags being passed into the `create-project` command, only useful in combination with `--add-repository` to persist them to composer.json + * Added a new optional `list` API endpoint for v2-format Composer repositories, see [UPGRADE](UPGRADE-2.0.md) for details + * Fixed `show -a` command not listing anything + * Fixed solver bug where it ended in a "Reached invalid decision id 0" + * Fixed updates of git-installed packages on windows + * Lots of minor bug fixes + +### [2.0.0-alpha2] 2020-06-24 + + * Added parallel installation of packages (requires OSX/Linux/WSL, and that `unzip` is present in PATH) + * Added optimization of constraints by compiling them to PHP code, which should reduce CPU time of updates + * Added handling of Ctrl-C on Windows for PHP 7.4+ + * Added better support for default branch names other than `master` + * Added --format=summary flag to `license` command + * Fixed issue in platform check when requiring ext-zend-opcache + * Fixed inline aliases issues + * Fixed git integration issue when signatures are set to be shown by default + +### [2.0.0-alpha1] 2020-06-03 + + * Breaking: This is a major release and while we tried to keep things compatible for most users, you might want to have a look at the [UPGRADE](UPGRADE-2.0.md) guides + * Many CPU and memory performance improvements + * The update command is now much more deterministic as it does not take the already installed packages into account + * Package installation now performs all network operations first before doing any changes on disk, to reduce the chances of ending up with a partially updated vendor dir + * Partial updates and require/remove are now much faster as they only load the metadata required for the updated packages + * Added a [platform-check step](doc/07-runtime.md#platform-check) when vendor/autoload.php gets initialized which checks the current PHP version/extensions match what is expected and fails hard otherwise. Can be disabled with the platform-check config option + * Added a [`Composer\InstalledVersions`](doc/07-runtime.md#installed-versions) class which is autoloaded in every project and lets you check which packages/versions are present at runtime + * Added a `composer-runtime-api` virtual package which you can require (as e.g. `^2.0`) to ensure things like the InstalledVersions class above are present. It will effectively force people to use Composer 2.x to install your project + * Added support for parallel downloads of package metadata and zip files, this requires that the curl extension is present and we thus strongly recommend enabling curl + * Added much clearer dependency resolution error reporting for common error cases + * Added support for updating to a specific version with partial updates, as well as a [--with flag](doc/03-cli.md#update--u) to pass in temporary constraint overrides + * Added support for TTY mode on Linux/OSX/WSL so that script handlers now run in interactive mode + * Added `only`, `exclude` and `canonical` options to all repositories, see [repository priorities](https://getcomposer.org/repoprio) for details + * Added support for lib-zip platform package + * Added `pre-operations-exec` event to be fired before the packages get installed/upgraded/removed + * Added `pre-pool-create` event to be fired before the package pool for the dependency solver is created, which lets you modify the list of packages going in + * Added `post-file-download` event to be fired after package dist files are downloaded, which lets you do additional checks on the files + * Added --locked flag to `show` command to see the packages from the composer.lock file + * Added --unused flag to `remove` command to make sure any packages which are not needed anymore get removed + * Added --dry-run flag to `require` and `remove` commands + * Added --no-install flag to `update`, `require` and `remove` commands to disable the install step and only do the update step (composer.lock file update) + * Added --with-dependencies and --with-all-dependencies flag aliases to `require` and `remove` commands for consistency with `update` + * Added more info to `vendor/composer/installed.json`, a dev key stores whether dev requirements were installed, and every package now has an install-path key with its install location + * Added COMPOSER_DISABLE_NETWORK which if set makes Composer do its best to run offline. This can be useful when you have poor connectivity or to do benchmarking without network jitter + * Added --json and --merge flags to `config` command to allow editing complex `extra.*` values by using json as input + * Added confirmation prompt when running Composer as superuser in interactive mode + * Added --no-check-version to `validate` command to remove the warning in case the version is defined + * Added --ignore-platform-req (without s) to all commands supporting --ignore-platform-reqs, which accepts a package name so you can ignore only specific platform requirements + * Added support for wildcards (`*`) in classmap autoloader paths + * Added support for configuring GitLab deploy tokens in addition to private tokens, see [gitlab-token](doc/06-config.md#gitlab-token) + * Added support for package version guessing for require and init command to take all platform packages into account, not just php version + * Fixed package ordering when autoloading and especially when loading plugins, to make sure dependencies are loaded before their dependents + * Fixed suggest output being very spammy, it now is only one line long and shows more rarely + * Fixed conflict rules like e.g. >=5 from matching dev-master, as it is not normalized to 9999999-dev internally anymore + +### [1.10.23] 2021-10-05 + + * Security: Fixed command injection vulnerability on Windows (GHSA-frqg-7g38-6gcf / CVE-2021-41116) + ### [1.10.22] 2021-04-27 * Security: Fixed command injection vulnerability in HgDriver/HgDownloader and hardened other VCS drivers and downloaders (GHSA-h5h8-pc6h-jvvx / CVE-2021-29472) @@ -146,7 +482,7 @@ * Fixed archive command to persist file permissions inside the zip files * Fixed init/require command to avoid suggesting packages which are already selected in the search results * Fixed create-project UX issues - * Fixed filemtime for vendor/composer/* files is now only changing when the files actually change + * Fixed filemtime for `vendor/composer/*` files is now only changing when the files actually change * Fixed issues detecting docker environment with an active open_basedir ### [1.9.3] 2020-02-04 @@ -156,7 +492,7 @@ ### [1.9.2] 2020-01-14 * Fixed minor git driver bugs - * Fixed schema validation for version field to allow dev-* versions too + * Fixed schema validation for version field to allow `dev-*` versions too * Fixed external processes' output being formatted even though it should not * Fixed issue with path repositories when trying to install feature branches @@ -457,7 +793,7 @@ * Fixed dist downloads from Bitbucket * Fixed some regressions related to xdebug disabling * Fixed `--minor-only` flag in `outdated` command - * Fixed handling of config.platform.php which did not replace other php-* package's versions + * Fixed handling of config.platform.php which did not replace other `php-*` package's versions ### [1.3.0] - 2016-12-24 @@ -829,7 +1165,7 @@ ### [1.0.0-alpha6] - 2012-10-23 * Schema: Added ability to pass additional options to repositories (i.e. ssh keys/client certificates to secure private repos) - * Schema: Added a new `~` operator that should be preferred over `>=`, see http://getcomposer.org/doc/01-basic-usage.md#package-versions + * Schema: Added a new `~` operator that should be preferred over `>=`, see https://getcomposer.org/doc/01-basic-usage.md#package-versions * Schema: Version constraints ` | [naderman.de](http://naderman.de) -- Jordi Boggiano | [GitHub](https://github.com/Seldaek) | [Twitter](https://twitter.com/seldaek) | | [seld.be](http://seld.be) +- Nils Adermann | [GitHub](https://github.com/naderman) | [Twitter](https://twitter.com/naderman) | | [naderman.de](https://naderman.de) +- Jordi Boggiano | [GitHub](https://github.com/Seldaek) | [Twitter](https://twitter.com/seldaek) | | [seld.be](https://seld.be) See also the list of [contributors](https://github.com/composer/composer/contributors) who participated in this project. @@ -54,7 +52,7 @@ Please send any sensitive issue to [security@packagist.org](mailto:security@pack License ------- -Composer is licensed under the MIT License - see the [LICENSE](LICENSE) file for details +Composer is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. Acknowledgments --------------- diff --git a/app/vendor/composer/composer/UPGRADE-2.0.md b/app/vendor/composer/composer/UPGRADE-2.0.md new file mode 100644 index 000000000..3e5878ea3 --- /dev/null +++ b/app/vendor/composer/composer/UPGRADE-2.0.md @@ -0,0 +1,111 @@ +# Upgrade guides for Composer 1.x to 2.0 + +## For composer CLI users + +- The new platform-check feature means that Composer checks the runtime PHP version and available extensions to ensure they match the project dependencies. If a mismatch is found, it exits with error details to make sure problems are not overlooked. To avoid issues when deploying to production it is recommended to run `composer check-platform-reqs` with the production PHP process as part of your build or deployment process. +- If a package exists in a higher priority repository, it will now be entirely ignored in lower priority repositories. See [repository priorities](https://getcomposer.org/repoprio) for details. +- Invalid PSR-0 / PSR-4 class configurations will not autoload anymore in optimized-autoloader mode, as per the warnings introduced in 1.10 +- On linux systems supporting the XDG Base Directory Specification, Composer will now prefer using XDG_CONFIG_DIR/composer over `~/.composer` if both are available (1.x used `~/.composer` first) +- Package names now must comply to our [naming guidelines](doc/04-schema.md#name) or Composer will abort, as per the warnings introduced in 1.8.1 +- Deprecated --no-suggest flag as it is not needed anymore +- PEAR support (repository, downloader, etc.) has been removed +- `update` now lists changes to the lock file first (update step), and then the changes applied when installing the lock file to the vendor dir (install step) +- `HTTPS_PROXY_REQUEST_FULLURI` if not specified will now default to false as this seems to work better in most environments +- `dev-trunk`, `dev-master` and `dev-default` are no longer aliases for each other. The exact branch names are now preserved. + +## For integrators and plugin authors + +- composer-plugin-api has been bumped to 2.0.0 - you can detect which version of Composer you run via `PluginInterface::PLUGIN_API_VERSION` +- `PluginInterface` added a deactivate (so plugin can stop whatever it is doing) and an uninstall (so the plugin can remove any files it created or do general cleanup) method. +- Plugins implementing `EventSubscriberInterface` will be deregistered from the EventDispatcher automatically when being deactivated, nothing to do there. +- `Pool` objects are now created via the `RepositorySet` class, you should use that in case you were using the `Pool` class directly. +- Custom installers extending from LibraryInstaller should be aware that in Composer 2 it MAY return PromiseInterface instances when calling parent::install/update/uninstall/installCode/removeCode. See [composer/installers](https://github.com/composer/installers/commit/5006d0c28730ade233a8f42ec31ac68fb1c5c9bb) for an example of how to handle this best. +- The `Composer\Installer` class changed quite a bit internally, but the inputs are almost the same: + - `setAdditionalInstalledRepository` is now `setAdditionalFixedRepository` + - `setUpdateWhitelist` is now `setUpdateAllowList` + - `setWhitelistDependencies`, `setWhitelistTransitiveDependencies` and `setWhitelistAllDependencies` are now all rolled into `setUpdateAllowTransitiveDependencies` which takes one of the `Request::UPDATE_*` constants + - `setSkipSuggest` is gone +- `vendor/composer/installed.json` format changed: + - packages are now wrapped into a `"packages"` top level key instead of the whole file being the package array + - packages now contain an `"installed-path"` key which lists where they were installed + - there is a top level `"dev"` key which stores whether dev requirements were installed or not +- Removed `OperationInterface::getReason` as the data was not accurate. There is no replacement available. +- `PreFileDownloadEvent` now receives an `HttpDownloader` instance instead of `RemoteFilesystem`, and that instance cannot be overridden by listeners anymore, you can however call setProcessedUrl or setCustomCacheKey. +- `VersionSelector::findBestCandidate`'s third argument (phpVersion) was removed in favor of passing in a complete PlatformRepository instance into the constructor +- `InitCommand::determineRequirements`'s fourth argument (phpVersion) should now receive a complete PlatformRepository instance or null if platform requirements are to be ignored +- `IOInterface` now extends PSR-3's `LoggerInterface`, and has new `writeRaw` + `writeErrorRaw` methods +- `RepositoryInterface` changes: + - A new `loadPackages(array $packageNameMap, array $acceptableStabilities, array $stabilityFlags)` function was added for use during pool building + - `search` now has a third `$type` argument + - A new `getRepoName()` function was added to describe the repository + - A new `getProviders()` function was added to list packages providing a given package's name +- Removed `BaseRepository` abstract class +- `DownloaderInterface` changes: + - `download` now receives a third `$prevPackage` argument for updates + - `download` should now only do network operations to prepare the package for installation but not actually install anything + - `prepare` (do user prompts or any checks which need to happen to make sure that install/update/remove will most likely succeed), `install` (should do the non-network part that `download` used to do) and `cleanup` (cleaning up anything that may be left over) were added as new steps in the package install flow + - All packages get first downloaded, then all together prepared, then all together installed/updated/uninstalled, then finally cleanup is called for all. Therefore for error recovery it is important to avoid failing during install/update/uninstall as much as possible, and risky things or user prompts should happen in the prepare step rather. In case of failure, cleanup() will be called so that changes can be undone as much as possible. +- If you used `RemoteFilesystem` you probably should use `HttpDownloader` instead now +- `PRE_DEPENDENCIES_SOLVING` and `POST_DEPENDENCIES_SOLVING` events have been removed, use the new `PRE_OPERATIONS_EXEC`, `PRE_POOL_CREATE` or other existing events instead or talk to us if you think you really need this. See below for more details. +- The bundled composer/semver is now the 3.x range, see release notes for [2.0](https://github.com/composer/semver/releases/tag/2.0.0) and [3.0](https://github.com/composer/semver/releases/tag/3.0.0) for the minor breaking changes there +- Run Composer with COMPOSER_DEBUG_EVENTS=1 set in the environment to show which events happen which might help you. + +### Detailed differences in event flow during dependency resolution, composer updates and installs + +#### Composer v1 + +- Composer resolves dependencies (dispatching PRE/POST_DEPENDENCIES_SOLVING) +- It then iterates over all packages one by one (dispatching PRE_PACKAGE_INSTALL/UPDATE/UNINSTALL, then PRE_FILE_DOWNLOAD if needed, then POST_PACKAGE_\*) +- And finally writes the lock file at the end + +#### Composer v2 + +The update and install process have been split up. + +Update does: + +- Composer resolves dependencies (dispatching PRE_POOL_CREATE) +- It then writes the lock file and that's the end of the update + +Install then does: + +- Dispatches PRE_OPERATIONS_EXEC with the full list of operations to be executed +- Downloads all the packages not in cache yet in parallel (dispatching PRE_FILE_DOWNLOAD for those not in cache yet) +- It then iterates over all packages and executes updates/installs/uninstalls in parallel (dispatching PRE_PACKAGE_INSTALL/UPDATE/UNINSTALL then POST_PACKAGE_\* but one package started last may finish installing before another is done for example). + +## For Composer repository implementors + +Composer 2.0 adds support for a new Composer repository format. + +It is possible to build a repository which is compatible with both Composer v1 and v2, you keep everything you had and simply add the new fields in `packages.json`. + +Here are examples of the new values from packagist.org: + +### metadata-url + +`"metadata-url": "/p2/%package%.json",` + +This new metadata-url should serve all packages which are in the repository. + +- Whenever Composer looks for a package, it will replace `%package%` by the package name, and fetch that URL. +- If dev stability is allowed for the package, it will also load the URL again with `$packageName~dev` (e.g. `/p2/foo/bar~dev.json` to look for `foo/bar`'s dev versions). +- Caching is done via the use of If-Modified-Since header, so make sure you return Last-Modified headers and that they are accurate. +- Any requested package which does not exist MUST return a 404 status code, which will indicate to Composer that this package does not exist in your repository. Make sure the 404 response is fast to avoid blocking Composer. Avoid redirects to alternative 404 pages. +- The `foo/bar.json` and `foo/bar~dev.json` files containing package versions MUST contain only versions for the foo/bar package, as `{"packages":{"foo/bar":[ ... versions here ... ]}}`. +- The array of versions can also optionally be minified using `Composer\Util\MetadataMinifier::minify()`. If you do that, you should add a `"minified": "composer/2.0"` key at the top level to indicate to Composer it must expand the version list back into the original data. See https://repo.packagist.org/p2/monolog/monolog.json for an example. + +If your repository only has a small number of packages, and you want to avoid the 404-requests, you can also specify an `"available-packages"` key in `packages.json` which should be an array with all the package names that your repository contain. Alternatively you can specify an `"available-package-patterns"` key which is an array of package name patterns (with `*` matching any string, e.g. `vendor/*` would make composer look up every matching package name in this repository). + +### providers-api + +`"providers-api": "https://packagist.org/providers/%package%.json",` + +The providers-api is optional, but if you implement it it should return packages which provide a given package name, but not the package which has that name. For example https://packagist.org/providers/monolog/monolog.json lists some package which have a "provide" rule for monolog/monolog, but it does not list monolog/monolog itself. + +### list + +This is also optional, it should accept an optional `?filter=xx` query param, which can contain `*` as wildcards matching any substring. + +It must return an array of package names as `{"packageNames": ["a/b", "c/d"]}`. See for example. + +It should return the names of package which names match the filter (or all names if no filter is present). Replace/provide rules should not be considered here. diff --git a/app/vendor/composer/composer/bin/compile b/app/vendor/composer/composer/bin/compile index a2720a95d..f9156245f 100755 --- a/app/vendor/composer/composer/bin/compile +++ b/app/vendor/composer/composer/bin/compile @@ -20,7 +20,7 @@ require __DIR__.'/../src/bootstrap.php'; use Composer\Compiler; error_reporting(-1); -ini_set('display_errors', 1); +ini_set('display_errors', '1'); try { $compiler = new Compiler(); diff --git a/app/vendor/composer/composer/bin/composer b/app/vendor/composer/composer/bin/composer index b7ab5318b..62c18974d 100755 --- a/app/vendor/composer/composer/bin/composer +++ b/app/vendor/composer/composer/bin/composer @@ -10,11 +10,13 @@ require __DIR__.'/../src/bootstrap.php'; use Composer\Console\Application; use Composer\XdebugHandler\XdebugHandler; +use Composer\Util\Platform; +use Composer\Util\ErrorHandler; error_reporting(-1); // Restart without Xdebug -$xdebug = new XdebugHandler('Composer', '--ansi'); +$xdebug = new XdebugHandler('Composer'); $xdebug->check(); unset($xdebug); @@ -24,38 +26,42 @@ if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '4.0', '>=')) { } if (function_exists('ini_set')) { - @ini_set('display_errors', 1); - - $memoryInBytes = function ($value) { - $unit = strtolower(substr($value, -1, 1)); - $value = (int) $value; - switch($unit) { - case 'g': - $value *= 1024; - // no break (cumulative multiplier) - case 'm': - $value *= 1024; - // no break (cumulative multiplier) - case 'k': - $value *= 1024; - } - - return $value; - }; + @ini_set('display_errors', '1'); - $memoryLimit = trim(ini_get('memory_limit')); - // Increase memory_limit if it is lower than 1.5GB - if ($memoryLimit != -1 && $memoryInBytes($memoryLimit) < 1024 * 1024 * 1536) { - @ini_set('memory_limit', '1536M'); - } // Set user defined memory limit if ($memoryLimit = getenv('COMPOSER_MEMORY_LIMIT')) { @ini_set('memory_limit', $memoryLimit); + } else { + $memoryInBytes = function ($value) { + $unit = strtolower(substr($value, -1, 1)); + $value = (int) $value; + switch($unit) { + case 'g': + $value *= 1024; + // no break (cumulative multiplier) + case 'm': + $value *= 1024; + // no break (cumulative multiplier) + case 'k': + $value *= 1024; + } + + return $value; + }; + + $memoryLimit = trim(ini_get('memory_limit')); + // Increase memory_limit if it is lower than 1.5GB + if ($memoryLimit != -1 && $memoryInBytes($memoryLimit) < 1024 * 1024 * 1536) { + @ini_set('memory_limit', '1536M'); + } + unset($memoryInBytes); } - unset($memoryInBytes, $memoryLimit); + unset($memoryLimit); } -putenv('COMPOSER_BINARY='.realpath($_SERVER['argv'][0])); +Platform::putEnv('COMPOSER_BINARY', realpath($_SERVER['argv'][0])); + +ErrorHandler::register(); // run the command application $application = new Application(); diff --git a/app/vendor/composer/composer/composer.json b/app/vendor/composer/composer/composer.json index e0a3d64af..9225f2e5f 100644 --- a/app/vendor/composer/composer/composer.json +++ b/app/vendor/composer/composer/composer.json @@ -13,34 +13,33 @@ { "name": "Nils Adermann", "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" + "homepage": "https://www.naderman.de" }, { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "homepage": "https://seld.be" } ], "require": { "php": "^5.3.2 || ^7.0 || ^8.0", "composer/ca-bundle": "^1.0", - "composer/semver": "^1.0", + "composer/metadata-minifier": "^1.0", + "composer/semver": "^3.0", "composer/spdx-licenses": "^1.2", - "composer/xdebug-handler": "^1.1", - "justinrainbow/json-schema": "^5.2.10", + "composer/xdebug-handler": "^2.0", + "justinrainbow/json-schema": "^5.2.11", "psr/log": "^1.0", "seld/jsonlint": "^1.4", "seld/phar-utils": "^1.0", - "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/finder": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0" - }, - "conflict": { - "symfony/console": "2.8.38" + "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/filesystem": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "react/promise": "^1.2 || ^2.7" }, "require-dev": { - "symfony/phpunit-bridge": "^4.2", + "symfony/phpunit-bridge": "^4.2 || ^5.0 || ^6.0", "phpspec/prophecy": "^1.10" }, "suggest": { @@ -51,11 +50,12 @@ "config": { "platform": { "php": "5.3.9" - } + }, + "platform-check": false }, "extra": { "branch-alias": { - "dev-master": "1.10-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -65,22 +65,36 @@ }, "autoload-dev": { "psr-4": { - "Composer\\Test\\": "tests/Composer/Test" - } + "Composer\\Test\\": "tests/Composer/Test", + "Composer\\PHPStanRules\\": "phpstan/Rules/src", + "Composer\\PHPStanRulesTests\\": "phpstan/Rules/tests" + }, + "classmap": [ + "phpstan/Rules/tests/data" + ] }, "bin": [ "bin/composer" ], "scripts": { "compile": "@php -dphar.readonly=0 bin/compile", - "test": "simple-phpunit" + "test": "simple-phpunit", + "phpstan-setup": [ + "@composer config platform --unset", + "@php composer.phar update", + "@composer require --dev phpstan/phpstan:^0.12.93 phpstan/phpstan-phpunit:^0.12.17 phpunit/phpunit:^7.5.20 --with-all-dependencies", + "git checkout composer.json composer.lock" + ], + "phpstan": "@php vendor/bin/phpstan analyse --configuration=phpstan/config.neon" }, "scripts-descriptions": { "compile": "Compile composer.phar", - "test": "Run all tests" + "test": "Run all tests", + "phpstan-setup": "Prepare environment to run PHPStan locally (must be run with PHP7.4)", + "phpstan": "Runs PHPStan (after phpstan-setup was executed, must be run with PHP7.4)" }, "support": { "issues": "https://github.com/composer/composer/issues", - "irc": "irc://irc.freenode.org/composer" + "irc": "ircs://irc.libera.chat:6697/composer" } } diff --git a/app/vendor/composer/composer/composer.lock b/app/vendor/composer/composer/composer.lock index 816a15fce..22981a039 100644 --- a/app/vendor/composer/composer/composer.lock +++ b/app/vendor/composer/composer/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f572867898a016e14b08d0b15faedffb", + "content-hash": "dfe550a024c0c2bb0784765e457968af", "packages": [ { "name": "composer/ca-bundle", - "version": "1.2.9", + "version": "1.2.11", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "78a0e288fdcebf92aa2318a8d3656168da6ac1a5" + "reference": "0b072d51c5a9c6f3412f7ea3ab043d6603cb2582" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/78a0e288fdcebf92aa2318a8d3656168da6ac1a5", - "reference": "78a0e288fdcebf92aa2318a8d3656168da6ac1a5", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/0b072d51c5a9c6f3412f7ea3ab043d6603cb2582", + "reference": "0b072d51c5a9c6f3412f7ea3ab043d6603cb2582", "shasum": "" }, "require": { @@ -29,7 +29,7 @@ "phpstan/phpstan": "^0.12.55", "psr/log": "^1.0", "symfony/phpunit-bridge": "^4.2 || ^5", - "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" + "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" }, "type": "library", "extra": { @@ -64,7 +64,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.2.9" + "source": "https://github.com/composer/ca-bundle/tree/1.2.11" }, "funding": [ { @@ -80,32 +80,102 @@ "type": "tidelift" } ], - "time": "2021-01-12T12:10:35+00:00" + "time": "2021-09-25T20:32:43+00:00" + }, + { + "name": "composer/metadata-minifier", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/composer/metadata-minifier.git", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/metadata-minifier/zipball/c549d23829536f0d0e984aaabbf02af91f443207", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "composer/composer": "^2", + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\MetadataMinifier\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Small utility library that handles metadata minification and expansion.", + "keywords": [ + "composer", + "compression" + ], + "support": { + "issues": "https://github.com/composer/metadata-minifier/issues", + "source": "https://github.com/composer/metadata-minifier/tree/1.0.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2021-04-07T13:37:33+00:00" }, { "name": "composer/semver", - "version": "1.7.2", + "version": "3.2.5", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "647490bbcaf7fc4891c58f47b825eb99d19c377a" + "reference": "31f3ea725711245195f62e54ffa402d8ef2fdba9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/647490bbcaf7fc4891c58f47b825eb99d19c377a", - "reference": "647490bbcaf7fc4891c58f47b825eb99d19c377a", + "url": "https://api.github.com/repos/composer/semver/zipball/31f3ea725711245195f62e54ffa402d8ef2fdba9", + "reference": "31f3ea725711245195f62e54ffa402d8ef2fdba9", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5" + "phpstan/phpstan": "^0.12.54", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -141,6 +211,11 @@ "validation", "versioning" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.2.5" + }, "funding": [ { "url": "https://packagist.com", @@ -155,7 +230,7 @@ "type": "tidelift" } ], - "time": "2020-12-03T15:47:16+00:00" + "time": "2021-05-24T12:41:47+00:00" }, { "name": "composer/spdx-licenses", @@ -238,21 +313,21 @@ }, { "name": "composer/xdebug-handler", - "version": "1.4.6", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "f27e06cd9675801df441b3656569b328e04aa37c" + "reference": "84674dd3a7575ba617f5a76d7e9e29a7d3891339" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f27e06cd9675801df441b3656569b328e04aa37c", - "reference": "f27e06cd9675801df441b3656569b328e04aa37c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/84674dd3a7575ba617f5a76d7e9e29a7d3891339", + "reference": "84674dd3a7575ba617f5a76d7e9e29a7d3891339", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1.0" + "psr/log": "^1 || ^2 || ^3" }, "require-dev": { "phpstan/phpstan": "^0.12.55", @@ -279,6 +354,11 @@ "Xdebug", "performance" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/2.0.2" + }, "funding": [ { "url": "https://packagist.com", @@ -293,20 +373,20 @@ "type": "tidelift" } ], - "time": "2021-03-25T17:01:18+00:00" + "time": "2021-07-31T17:03:58+00:00" }, { "name": "justinrainbow/json-schema", - "version": "5.2.10", + "version": "5.2.11", "source": { "type": "git", "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b" + "reference": "2ab6744b7296ded80f8cc4f9509abbff393399aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b", - "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ab6744b7296ded80f8cc4f9509abbff393399aa", + "reference": "2ab6744b7296ded80f8cc4f9509abbff393399aa", "shasum": "" }, "require": { @@ -361,22 +441,22 @@ ], "support": { "issues": "https://github.com/justinrainbow/json-schema/issues", - "source": "https://github.com/justinrainbow/json-schema/tree/5.2.10" + "source": "https://github.com/justinrainbow/json-schema/tree/5.2.11" }, - "time": "2020-05-27T16:41:55+00:00" + "time": "2021-07-22T09:24:00+00:00" }, { "name": "psr/log", - "version": "1.1.3", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", "shasum": "" }, "require": { @@ -400,7 +480,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for logging libraries", @@ -411,9 +491,57 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.3" + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "react/promise", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "eefff597e67ff66b719f8171480add3c91474a1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/eefff597e67ff66b719f8171480add3c91474a1e", + "reference": "eefff597e67ff66b719f8171480add3c91474a1e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-0": { + "React\\Promise": "src/" + }, + "files": [ + "src/React/Promise/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/1.0" }, - "time": "2020-03-23T09:12:05+00:00" + "time": "2016-03-07T13:46:50+00:00" }, { "name": "seld/jsonlint", @@ -480,16 +608,16 @@ }, { "name": "seld/phar-utils", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "8674b1d84ffb47cc59a101f5d5a3b61e87d23796" + "reference": "749042a2315705d2dfbbc59234dd9ceb22bf3ff0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8674b1d84ffb47cc59a101f5d5a3b61e87d23796", - "reference": "8674b1d84ffb47cc59a101f5d5a3b61e87d23796", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/749042a2315705d2dfbbc59234dd9ceb22bf3ff0", + "reference": "749042a2315705d2dfbbc59234dd9ceb22bf3ff0", "shasum": "" }, "require": { @@ -522,9 +650,9 @@ ], "support": { "issues": "https://github.com/Seldaek/phar-utils/issues", - "source": "https://github.com/Seldaek/phar-utils/tree/master" + "source": "https://github.com/Seldaek/phar-utils/tree/1.1.2" }, - "time": "2020-07-07T18:42:57+00:00" + "time": "2021-08-19T21:01:38+00:00" }, { "name": "symfony/console", @@ -1479,5 +1607,5 @@ "platform-overrides": { "php": "5.3.9" }, - "plugin-api-version": "1.1.0" + "plugin-api-version": "2.1.0" } diff --git a/app/vendor/composer/composer/doc/00-intro.md b/app/vendor/composer/composer/doc/00-intro.md index a6cf10d71..8afd60090 100644 --- a/app/vendor/composer/composer/doc/00-intro.md +++ b/app/vendor/composer/composer/doc/00-intro.md @@ -9,7 +9,7 @@ for you. Composer is **not** a package manager in the same sense as Yum or Apt are. Yes, it deals with "packages" or libraries, but it manages them on a per-project basis, installing them in a directory (e.g. `vendor`) inside your project. By -default it does not install anything globally. Thus, it is a dependency +default, it does not install anything globally. Thus, it is a dependency manager. It does however support a "global" project for convenience via the [global](03-cli.md#global) command. @@ -155,7 +155,7 @@ Close your current terminal. Test usage with a new terminal: ```sh C:\Users\username>composer -V -Composer version 1.0.0 2016-01-10 20:34:53 +Composer version 2.0.12 2021-04-01 10:14:59 ``` ## Using Composer diff --git a/app/vendor/composer/composer/doc/01-basic-usage.md b/app/vendor/composer/composer/doc/01-basic-usage.md index cbdd3ebb7..4ccc50965 100644 --- a/app/vendor/composer/composer/doc/01-basic-usage.md +++ b/app/vendor/composer/composer/doc/01-basic-usage.md @@ -13,18 +13,21 @@ a logging library. If you have not yet installed Composer, refer to the To start using Composer in your project, all you need is a `composer.json` file. This file describes the dependencies of your project and may contain -other metadata as well. +other metadata as well. It typically should go in the top-most directory of +your project/VCS repository. You can technically run Composer anywhere but +if you want to publish a package to Packagist.org, it will have to be able +to find the file at the top of your VCS repository. ### The `require` key -The first (and often only) thing you specify in `composer.json` is the +The first thing you specify in `composer.json` is the [`require`](04-schema.md#require) key. You are telling Composer which packages your project depends on. ```json { "require": { - "monolog/monolog": "1.0.*" + "monolog/monolog": "2.0.*" } } ``` @@ -35,11 +38,11 @@ As you can see, [`require`](04-schema.md#require) takes an object that maps Composer uses this information to search for the right set of files in package "repositories" that you register using the [`repositories`](04-schema.md#repositories) -key, or in Packagist, the default package repository. In the above example, -since no other repository has been registered in the `composer.json` file, it is -assumed that the `monolog/monolog` package is registered on Packagist. (See more -about Packagist [below](#packagist), or read more about repositories -[here](05-repositories.md)). +key, or in [Packagist.org](https://packagist.org), the default package repository. +In the above example, since no other repository has been registered in the +`composer.json` file, it is assumed that the `monolog/monolog` package is registered +on Packagist.org. (See more about Packagist [below](#packagist), or read more +about repositories [here](05-repositories.md)). ### Package names @@ -56,9 +59,9 @@ you to require certain versions of server software. See ### Package version constraints In our example, we are requesting the Monolog package with the version constraint -[`1.0.*`](https://semver.mwl.be/#?package=monolog%2Fmonolog&version=1.0.*). -This means any version in the `1.0` development branch, or any version that is -greater than or equal to 1.0 and less than 1.1 (`>=1.0 <1.1`). +[`2.0.*`](https://semver.mwl.be/#?package=monolog%2Fmonolog&version=2.0.*). +This means any version in the `2.0` development branch, or any version that is +greater than or equal to 2.0 and less than 2.1 (`>=2.0 <2.1`). Please read [versions](articles/versions.md) for more in-depth information on versions, how versions relate to each other, and on version constraints. @@ -68,16 +71,16 @@ versions, how versions relate to each other, and on version constraints. > and searches for it in any repositories that you have registered using the > [`repositories`](04-schema.md#repositories) key. If you have not registered > any extra repositories, or it does not find a package with that name in the -> repositories you have specified, it falls back to Packagist (more [below](#packagist)). +> repositories you have specified, it falls back to Packagist.org (more [below](#packagist)). > -> When Composer finds the right package, either in Packagist or in a repo you have specified, +> When Composer finds the right package, either in Packagist.org or in a repo you have specified, > it then uses the versioning features of the package's VCS (i.e., branches and tags) > to attempt to find the best match for the version constraint you have specified. Be sure to read > about versions and package resolution in the [versions article](articles/versions.md). > **Note:** If you are trying to require a package but Composer throws an error > regarding package stability, the version you have specified may not meet your -> default minimum stability requirements. By default only stable releases are taken +> default minimum stability requirements. By default, only stable releases are taken > into consideration when searching for valid package versions in your VCS. > > You might run into this if you are trying to require dev, alpha, beta, or RC @@ -86,40 +89,51 @@ versions, how versions relate to each other, and on version constraints. ## Installing dependencies -To install the defined dependencies for your project, run the -[`install`](03-cli.md#install) command. +To initially install the defined dependencies for your project, you should run the +[`update`](03-cli.md#update-u) command. ```sh -php composer.phar install +php composer.phar update ``` -When you run this command, one of two things may happen: +This will make Composer do two things: -### Installing without `composer.lock` - -If you have never run the command before and there is also no `composer.lock` file present, -Composer resolves all dependencies listed in your `composer.json` file and downloads -the latest version of their files into the `vendor` directory in your project. (The `vendor` -directory is the conventional location for all third-party code in a project). In our -example from above, you would end up with the Monolog source files in -`vendor/monolog/monolog/`. If Monolog listed any dependencies, those would also be in -folders under `vendor/`. +- It resolves all dependencies listed in your `composer.json` file and writes all of the + packages and their exact versions to the `composer.lock` file, locking the project to + those specific versions. You should commit the `composer.lock` file to your project repo + so that all people working on the project are locked to the same versions of dependencies + (more below). This is the main role of the `update` command. +- It then implicitly runs the [`install`](03-cli.md#install-i) command. This will download + the dependencies' files into the `vendor` directory in your project. (The `vendor` + directory is the conventional location for all third-party code in a project). In our + example from above, you would end up with the Monolog source files in + `vendor/monolog/monolog/`. As Monolog has a dependency on `psr/log`, that package's files + can also be found inside `vendor/`. > **Tip:** If you are using git for your project, you probably want to add > `vendor` in your `.gitignore`. You really don't want to add all of that > third-party code to your versioned repository. -When Composer has finished installing, it writes all of the packages and the exact versions -of them that it downloaded to the `composer.lock` file, locking the project to those specific -versions. You should commit the `composer.lock` file to your project repo so that all people -working on the project are locked to the same versions of dependencies (more below). +### Commit your `composer.lock` file to version control + +Committing this file to version control is important because it will cause anyone +who sets up the project to use the exact same +versions of the dependencies that you are using. Your CI server, production +machines, other developers in your team, everything and everyone runs on the +same dependencies, which mitigates the potential for bugs affecting only some +parts of the deployments. Even if you develop alone, in six months when +reinstalling the project you can feel confident the dependencies installed are +still working even if your dependencies released many new versions since then. +(See note below about using the `update` command.) + +> **Note:** For libraries it is not necessary to commit the lock +> file, see also: [Libraries - Lock file](02-libraries.md#lock-file). -### Installing with `composer.lock` +### Installing from `composer.lock` -This brings us to the second scenario. If there is already a `composer.lock` file as well as a -`composer.json` file when you run `composer install`, it means either you ran the -`install` command before, or someone else on the project ran the `install` command and -committed the `composer.lock` file to the project (which is good). +If there is already a `composer.lock` file in the project folder, it means either +you ran the `update` command before, or someone else on the project ran the `update` +command and committed the `composer.lock` file to the project (which is good). Either way, running `install` when a `composer.lock` file is present resolves and installs all dependencies that you listed in `composer.json`, but Composer uses the exact versions listed @@ -130,26 +144,21 @@ working on your project. As a result you will have all dependencies requested by the file was created). This is by design, it ensures that your project does not break because of unexpected changes in dependencies. -### Commit your `composer.lock` file to version control +So after fetching new changes from your VCS repository it is recommended to run +a Composer `install` to make sure the vendor directory is up in sync with your +`composer.lock` file. -Committing this file to VC is important because it will cause anyone who sets -up the project to use the exact same -versions of the dependencies that you are using. Your CI server, production -machines, other developers in your team, everything and everyone runs on the -same dependencies, which mitigates the potential for bugs affecting only some -parts of the deployments. Even if you develop alone, in six months when -reinstalling the project you can feel confident the dependencies installed are -still working even if your dependencies released many new versions since then. -(See note below about using the `update` command.) +```sh +php composer.phar install +``` ## Updating dependencies to their latest versions As mentioned above, the `composer.lock` file prevents you from automatically getting the latest versions of your dependencies. To update to the latest versions, use the -[`update`](03-cli.md#update) command. This will fetch the latest matching +[`update`](03-cli.md#update-u) command. This will fetch the latest matching versions (according to your `composer.json` file) and update the lock file -with the new versions. (This is equivalent to deleting the `composer.lock` file -and running `install` again.) +with the new versions. ```sh php composer.phar update @@ -159,24 +168,21 @@ php composer.phar update > if the `composer.lock` has not been updated since changes were made to the > `composer.json` that might affect dependency resolution. -If you only want to install or update one dependency, you can allow them: +If you only want to install, upgrade or remove one dependency, you can explicitly list it as an argument: ```sh php composer.phar update monolog/monolog [...] ``` -> **Note:** For libraries it is not necessary to commit the lock -> file, see also: [Libraries - Lock file](02-libraries.md#lock-file). - ## Packagist -[Packagist](https://packagist.org/) is the main Composer repository. A Composer +[Packagist.org](https://packagist.org/) is the main Composer repository. A Composer repository is basically a package source: a place where you can get packages from. Packagist aims to be the central repository that everybody uses. This means that you can automatically `require` any package that is available there, without further specifying where Composer should look for the package. -If you go to the [Packagist website](https://packagist.org/) (packagist.org), +If you go to the [Packagist.org website](https://packagist.org/), you can browse and search for packages. Any open source project using Composer is recommended to publish their packages @@ -219,7 +225,7 @@ require __DIR__ . '/vendor/autoload.php'; $log = new Monolog\Logger('name'); $log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING)); -$log->addWarning('Foo'); +$log->warning('Foo'); ``` You can even add your own code to the autoloader by adding an @@ -233,7 +239,7 @@ You can even add your own code to the autoloader by adding an } ``` -Composer will register a [PSR-4](http://www.php-fig.org/psr/psr-4/) autoloader +Composer will register a [PSR-4](https://www.php-fig.org/psr/psr-4/) autoloader for the `Acme` namespace. You define a mapping from namespaces to directories. The `src` directory would diff --git a/app/vendor/composer/composer/doc/02-libraries.md b/app/vendor/composer/composer/doc/02-libraries.md index 6eb3f2fde..c731f105d 100644 --- a/app/vendor/composer/composer/doc/02-libraries.md +++ b/app/vendor/composer/composer/doc/02-libraries.md @@ -26,14 +26,14 @@ In this case the project name is `acme/hello-world`, where `acme` is the vendor name. Supplying a vendor name is mandatory. > **Note:** If you don't know what to use as a vendor name, your GitHub -> username is usually a good bet. While package names are case insensitive, the -> convention is all lowercase and dashes for word separation. +> username is usually a good bet. Package names must be lowercase, and the +> convention is to use dashes for word separation. ## Library Versioning In the vast majority of cases, you will be maintaining your library using some sort of version control system like git, svn, hg or fossil. In these cases, -Composer infers versions from your VCS and you **should not** specify a version +Composer infers versions from your VCS, and you **should not** specify a version in your `composer.json` file. (See the [Versions article](articles/versions.md) to learn about how Composer uses VCS branches and tags to resolve version constraints.) @@ -69,7 +69,7 @@ can help your team to always test against the same dependency versions. However, this lock file will not have any effect on other projects that depend on it. It only has an effect on the main project. -If you do not want to commit the lock file and you are using git, add it to +If you do not want to commit the lock file, and you are using git, add it to the `.gitignore`. ## Publishing to a VCS diff --git a/app/vendor/composer/composer/doc/03-cli.md b/app/vendor/composer/composer/doc/03-cli.md index 88d21c01d..c35eabb61 100644 --- a/app/vendor/composer/composer/doc/03-cli.md +++ b/app/vendor/composer/composer/doc/03-cli.md @@ -65,6 +65,7 @@ php composer.phar init the list of requires. Every repository can be either an HTTP URL pointing to a `composer` repository or a JSON string which similar to what the [repositories](04-schema.md#repositories) key accepts. +* **--autoload (-a):** Add a PSR-4 autoload mapping to the composer.json. Automatically maps your package's namespace to the provided directory. (Expects a relative path, e.g. src/) See also [PSR-4 autoload](04-schema.md#psr-4). ## install / i @@ -84,17 +85,14 @@ resolution. ### Options -* **--prefer-source:** There are two ways of downloading a package: `source` - and `dist`. For stable versions Composer will use the `dist` by default. - The `source` is a version control repository. If `--prefer-source` is - enabled, Composer will install from `source` if there is one. This is - useful if you want to make a bugfix to a project and get a local git - clone of the dependency directly. -* **--prefer-dist:** Reverse of `--prefer-source`, Composer will install - from `dist` if possible. This can speed up installs substantially on build - servers and other use cases where you typically do not run updates of the - vendors. It is also a way to circumvent problems with git if you do not - have a proper setup. +* **--prefer-install:** There are two ways of downloading a package: `source` + and `dist`. Composer uses `dist` by default. If you pass + `--prefer-install=source` (or `--prefer-source`) Composer will install from + `source` if there is one. This is useful if you want to make a bugfix to a + project and get a local git clone of the dependency directly. + To get the legacy behavior where Composer use `source` automatically for dev + versions of packages, use `--prefer-install=auto`. See also [config.preferred-install](06-config.md#preferred-install). + Passing this flag will override the config value. * **--dry-run:** If you want to run through an installation without actually installing a package, you can use `--dry-run`. This will simulate the installation and show you what would happen. @@ -105,16 +103,21 @@ resolution. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--no-progress:** Removes the progress display that can mess with some terminals or scripts which don't handle backspace characters. -* **--no-suggest:** Skips suggested packages in the output. * **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but can take a bit of time to run so it is currently not done by default. * **--classmap-authoritative (-a):** Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`. * **--apcu-autoloader:** Use APCu to cache found/not-found classes. -* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` - requirements and force the installation even if the local machine does not - fulfill these. See also the [`platform`](06-config.md#platform) config option. +* **--apcu-autoloader-prefix:** Use a custom prefix for the APCu autoloader cache. + Implicitly enables `--apcu-autoloader`. +* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`, + `lib-*` and `ext-*`) and force the installation even if the local machine does + not fulfill these. + See also the [`platform`](06-config.md#platform) config option. +* **--ignore-platform-req:** ignore a specific platform requirement(`php`, + `hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine + does not fulfill it. ## update / u @@ -142,31 +145,64 @@ You can also use wildcards to update a bunch of packages at once: php composer.phar update "vendor/*" ``` + +If you want to downgrade a package to a specific version without changing your +composer.json you can use `--with` and provide a custom version constraint: + +```sh +php composer.phar update --with vendor/package:2.0.1 +``` + +The custom constraint has to be a subset of the existing constraint you have, +and this feature is only available for your root package dependencies. + +If you only want to update the package(s) for which you provide custom constraints +using `--with`, you can skip `--with` and just use constraints with the partial +update syntax: + +```sh +php composer.phar update vendor/package:2.0.1 vendor/package2:3.0.* +``` + + ### Options -* **--prefer-source:** Install packages from `source` when available. -* **--prefer-dist:** Install packages from `dist` when available. +* **--prefer-install:** There are two ways of downloading a package: `source` + and `dist`. Composer uses `dist` by default. If you pass + `--prefer-install=source` (or `--prefer-source`) Composer will install from + `source` if there is one. This is useful if you want to make a bugfix to a + project and get a local git clone of the dependency directly. + To get the legacy behavior where Composer use `source` automatically for dev + versions of packages, use `--prefer-install=auto`. See also [config.preferred-install](06-config.md#preferred-install). + Passing this flag will override the config value. * **--dry-run:** Simulate the command without actually doing anything. * **--dev:** Install packages listed in `require-dev` (this is the default behavior). * **--no-dev:** Skip installing packages listed in `require-dev`. The autoloader generation skips the `autoload-dev` rules. +* **--no-install:** Does not run the install step after updating the composer.lock file. * **--lock:** Only updates the lock file hash to suppress warning about the lock file being out of date. +* **--with:** Temporary version constraint to add, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 * **--no-autoloader:** Skips autoloader generation. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--no-progress:** Removes the progress display that can mess with some terminals or scripts which don't handle backspace characters. -* **--no-suggest:** Skips suggested packages in the output. -* **--with-dependencies:** Add also dependencies of allowed packages to the allow list, except those that are root requirements. -* **--with-all-dependencies:** Add also all dependencies of allowed packages to the allow list, including those that are root requirements. +* **--with-dependencies (-w):** Update also dependencies of packages in the argument list, except those which are root requirements. +* **--with-all-dependencies (-W):** Update also dependencies of packages in the argument list, including those which are root requirements. * **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but can take - a bit of time to run so it is currently not done by default. + a bit of time to run, so it is currently not done by default. * **--classmap-authoritative (-a):** Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`. * **--apcu-autoloader:** Use APCu to cache found/not-found classes. -* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` - requirements and force the installation even if the local machine does not - fulfill these. See also the [`platform`](06-config.md#platform) config option. +* **--apcu-autoloader-prefix:** Use a custom prefix for the APCu autoloader cache. + Implicitly enables `--apcu-autoloader`. +* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`, + `lib-*` and `ext-*`) and force the installation even if the local machine does + not fulfill these. + See also the [`platform`](06-config.md#platform) config option. +* **--ignore-platform-req:** ignore a specific platform requirement(`php`, + `hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine + does not fulfill it. * **--prefer-stable:** Prefer stable versions of dependencies. * **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal versions of requirements, generally used with `--prefer-stable`. @@ -199,29 +235,42 @@ If you do not specify a package, composer will prompt you to search for a packag ### Options * **--dev:** Add packages to `require-dev`. -* **--prefer-source:** Install packages from `source` when available. -* **--prefer-dist:** Install packages from `dist` when available. +* **--dry-run:** Simulate the command without actually doing anything. +* **--prefer-install:** There are two ways of downloading a package: `source` + and `dist`. Composer uses `dist` by default. If you pass + `--prefer-install=source` (or `--prefer-source`) Composer will install from + `source` if there is one. This is useful if you want to make a bugfix to a + project and get a local git clone of the dependency directly. + To get the legacy behavior where Composer use `source` automatically for dev + versions of packages, use `--prefer-install=auto`. See also [config.preferred-install](06-config.md#preferred-install). + Passing this flag will override the config value. * **--no-progress:** Removes the progress display that can mess with some terminals or scripts which don't handle backspace characters. -* **--no-suggest:** Skips suggested packages in the output. -* **--no-update:** Disables the automatic update of the dependencies. +* **--no-update:** Disables the automatic update of the dependencies (implies --no-install). +* **--no-install:** Does not run the install step after updating the composer.lock file. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--update-no-dev:** Run the dependency update with the `--no-dev` option. -* **--update-with-dependencies:** Also update dependencies of the newly required packages, except those that are root requirements. -* **--update-with-all-dependencies:** Also update dependencies of the newly required packages, including those that are root requirements. -* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` - requirements and force the installation even if the local machine does not - fulfill these. See also the [`platform`](06-config.md#platform) config option. +* **--update-with-dependencies (-w):** Also update dependencies of the newly required packages, except those that are root requirements. +* **--update-with-all-dependencies (-W):** Also update dependencies of the newly required packages, including those that are root requirements. +* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`, + `lib-*` and `ext-*`) and force the installation even if the local machine does + not fulfill these. + See also the [`platform`](06-config.md#platform) config option. +* **--ignore-platform-req:** ignore a specific platform requirement(`php`, + `hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine + does not fulfill it. * **--prefer-stable:** Prefer stable versions of dependencies. * **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal versions of requirements, generally used with `--prefer-stable`. * **--sort-packages:** Keep packages sorted in `composer.json`. * **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but - can take a bit of time to run so it is currently not done by default. + can take a bit of time to run, so it is currently not done by default. * **--classmap-authoritative (-a):** Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`. * **--apcu-autoloader:** Use APCu to cache found/not-found classes. +* **--apcu-autoloader-prefix:** Use a custom prefix for the APCu autoloader cache. + Implicitly enables `--apcu-autoloader`. ## remove @@ -237,21 +286,79 @@ uninstalled. ### Options * **--dev:** Remove packages from `require-dev`. +* **--dry-run:** Simulate the command without actually doing anything. * **--no-progress:** Removes the progress display that can mess with some terminals or scripts which don't handle backspace characters. -* **--no-update:** Disables the automatic update of the dependencies. +* **--no-update:** Disables the automatic update of the dependencies (implies --no-install). +* **--no-install:** Does not run the install step after updating the composer.lock file. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--update-no-dev:** Run the dependency update with the --no-dev option. -* **--update-with-dependencies:** Also update dependencies of the removed packages. -* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` - requirements and force the installation even if the local machine does not - fulfill these. See also the [`platform`](06-config.md#platform) config option. +* **--update-with-dependencies (-w):** Also update dependencies of the removed packages. + (Deprecated, is now default behavior) +* **--update-with-all-dependencies (-W):** Allows all inherited dependencies to be updated, + including those that are root requirements. +* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`, + `lib-*` and `ext-*`) and force the installation even if the local machine does + not fulfill these. + See also the [`platform`](06-config.md#platform) config option. +* **--ignore-platform-req:** ignore a specific platform requirement(`php`, + `hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine + does not fulfill it. * **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but can take a bit of time to run so it is currently not done by default. * **--classmap-authoritative (-a):** Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`. * **--apcu-autoloader:** Use APCu to cache found/not-found classes. +* **--apcu-autoloader-prefix:** Use a custom prefix for the APCu autoloader cache. + Implicitly enables `--apcu-autoloader`. + +## reinstall + +The `reinstall` command looks up installed packages by name, +uninstalls them and reinstalls them. This lets you do a clean install +of a package if you messed with its files, or if you wish to change +the installation type using --prefer-install. + +```sh +php composer.phar reinstall acme/foo acme/bar +``` + +You can specify more than one package name to reinstall, or use a +wildcard to select several packages at once: + +```sh +php composer.phar reinstall "acme/*" +``` + +### Options + +* **--prefer-install:** There are two ways of downloading a package: `source` + and `dist`. Composer uses `dist` by default. If you pass + `--prefer-install=source` (or `--prefer-source`) Composer will install from + `source` if there is one. This is useful if you want to make a bugfix to a + project and get a local git clone of the dependency directly. + To get the legacy behavior where Composer use `source` automatically for dev + versions of packages, use `--prefer-install=auto`. See also [config.preferred-install](06-config.md#preferred-install). + Passing this flag will override the config value. +* **--no-autoloader:** Skips autoloader generation. +* **--no-scripts:** Skips execution of scripts defined in `composer.json`. +* **--no-progress:** Removes the progress display that can mess with some + terminals or scripts which don't handle backspace characters. +* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster + autoloader. This is recommended especially for production, but can take + a bit of time to run so it is currently not done by default. +* **--classmap-authoritative (-a):** Autoload classes from the classmap only. + Implicitly enables `--optimize-autoloader`. +* **--apcu-autoloader:** Use APCu to cache found/not-found classes. +* **--apcu-autoloader-prefix:** Use a custom prefix for the APCu autoloader cache. + Implicitly enables `--apcu-autoloader`. +* **--ignore-platform-reqs:** ignore all platform requirements. This only + has an effect in the context of the autoloader generation for the + reinstall command. +* **--ignore-platform-req:** ignore a specific platform requirement. This only + has an effect in the context of the autoloader generation for the + reinstall command. ## check-platform-reqs @@ -309,6 +416,11 @@ You can also search for more than one term by passing multiple arguments. * **--only-name (-N):** Search only in name. * **--type (-t):** Search for a specific package type. +* **--format (-f):** Lets you pick between text (default) or json output format. + Note that in the json, only the name and description keys are guaranteed to be + present. The rest (`url`, `repository`, `downloads` and `favers`) are available + for Packagist.org search results and other repositories may return more or less + data. ## show @@ -359,6 +471,7 @@ php composer.phar show monolog/monolog 1.0.2 * **--all :** List all packages available in all your repositories. * **--installed (-i):** List the packages that are installed (this is enabled by default, and deprecated). +* **--locked:** List the locked packages from composer.lock. * **--platform (-p):** List only platform packages (php & extensions). * **--available (-a):** List available packages only. * **--self (-s):** List the root package info. @@ -367,6 +480,7 @@ php composer.phar show monolog/monolog 1.0.2 * **--tree (-t):** List your dependencies as a tree. If you pass a package name it will show the dependency tree for that package. * **--latest (-l):** List all installed packages including their latest version. * **--outdated (-o):** Implies --latest, but this lists *only* packages that have a newer version available. +* **--no-dev:** Filters dev dependencies from the package list. * **--minor-only (-m):** Use with --latest. Only shows packages that have minor SemVer-compatible updates. * **--direct (-D):** Restricts the list of packages to your direct dependencies. * **--strict:** Return a non-zero exit code when there are outdated packages. @@ -381,7 +495,7 @@ including their current and latest versions. This is basically an alias for The color coding is as such: - **green (=)**: Dependency is in the latest version and is up to date. -- **yellow (~)**: Dependency has a new version available that includes backwards compatibility breaks according to semver, so upgrade when +- **yellow (`~`)**: Dependency has a new version available that includes backwards compatibility breaks according to semver, so upgrade when you can but it may involve work. - **red (!)**: Dependency has a new version that is semver-compatible and you should upgrade it. @@ -392,6 +506,8 @@ The color coding is as such: * **--strict:** Returns non-zero exit code if any package is outdated. * **--minor-only (-m):** Only shows packages that have minor SemVer-compatible updates. * **--format (-f):** Lets you pick between text (default) or json output format. +* **--no-dev:** Do not show outdated dev dependencies. +* **--locked:** Shows updates for packages from the lock file, regardless of what is currently in vendor dir. ## browse / home @@ -409,22 +525,29 @@ Lists all packages suggested by currently installed set of packages. You can optionally pass one or multiple package names in the format of `vendor/package` to limit output to suggestions made by those packages only. -Use the `--by-package` or `--by-suggestion` flags to group the output by +Use the `--by-package` (default) or `--by-suggestion` flags to group the output by the package offering the suggestions or the suggested packages respectively. -Use the `--verbose (-v)` flag to display the suggesting package and the suggestion reason. -This implies `--by-package --by-suggestion`, showing both lists. +If you only want a list of suggested package names, use `--list`. ### Options -* **--by-package:** Groups output by suggesting package. +* **--by-package:** Groups output by suggesting package (default). * **--by-suggestion:** Groups output by suggested package. +* **--all:** Show suggestions from all dependencies, including transitive ones (by + default only direct dependencies' suggestions are shown). +* **--list:** Show only list of suggested package names. * **--no-dev:** Excludes suggestions from `require-dev` packages. ## fund Discover how to help fund the maintenance of your dependencies. This lists -all funding links from the installed dependencies. +all funding links from the installed dependencies. Use `--format=json` to +get machine-readable output. + +### Options + +* **--format (-f):** Lets you pick between text (default) or json output format. ## depends (why) @@ -552,6 +675,9 @@ you may have to run the command with `root` privileges sudo -H composer self-update ``` +If Composer was not installed as a PHAR, this command is not available. +(This is sometimes the case when Composer was installed by an operating system package manager.) + ### Options * **--rollback (-r):** Rollback to the last version you had installed. @@ -562,6 +688,9 @@ sudo -H composer self-update * **--stable:** Force an update to the stable channel. * **--preview:** Force an update to the preview channel. * **--snapshot:** Force an update to the snapshot channel. +* **--1:** Force an update to the stable channel, but only use 1.x versions +* **--2:** Force an update to the stable channel, but only use 2.x versions +* **--set-channel-only:** Only store the channel as the default one and then exit ## config @@ -603,8 +732,10 @@ See the [Config](06-config.md) chapter for valid configuration options. option this lists the global configuration only. * **--file="..." (-f):** Operate on a specific file instead of composer.json. Note that this cannot be used in conjunction with the `--global` option. -* **--absolute:** Returns absolute paths when fetching *-dir config values +* **--absolute:** Returns absolute paths when fetching `*-dir` config values instead of relative. +* **--json:** JSON decode the setting value, to be used with `extra.*` keys. +* **--merge:** Merge the setting value with the current value, to be used with `extra.*` keys in combination with `--json`. ### Modifying Repositories @@ -633,6 +764,13 @@ php composer.phar config extra.foo.bar value The dots indicate array nesting, a max depth of 3 levels is allowed though. The above would set `"extra": { "foo": { "bar": "value" } }`. +If you have a complex value to add/modify, you can use the `--json` and `--merge` flags +to edit extra fields as json: + +```sh +php composer.phar config --json extra.foo.bar '{"baz": true, "qux": []}' +``` + ## create-project You can use Composer to create new projects from an existing package. This is @@ -664,14 +802,21 @@ By default the command checks for the packages on packagist.org. ### Options * **--stability (-s):** Minimum stability of package. Defaults to `stable`. -* **--prefer-source:** Install packages from `source` when available. -* **--prefer-dist:** Install packages from `dist` when available. +* **--prefer-install:** There are two ways of downloading a package: `source` + and `dist`. Composer uses `dist` by default. If you pass + `--prefer-install=source` (or `--prefer-source`) Composer will install from + `source` if there is one. This is useful if you want to make a bugfix to a + project and get a local git clone of the dependency directly. + To get the legacy behavior where Composer use `source` automatically for dev + versions of packages, use `--prefer-install=auto`. See also [config.preferred-install](06-config.md#preferred-install). + Passing this flag will override the config value. * **--repository:** Provide a custom repository to search for the package, which will be used instead of packagist. Can be either an HTTP URL pointing to a `composer` repository, a path to a local `packages.json` file, or a JSON string which similar to what the [repositories](04-schema.md#repositories) - key accepts. -* **--add-repository:** Add the repository option to the composer.json. + key accepts. You can use this multiple times to configure multiple repositories. +* **--add-repository:** Add the custom repository in the composer.json. If a lock + file is present it will be deleted and an update will be run instead of install. * **--dev:** Install packages listed in `require-dev`. * **--no-dev:** Disables installation of require-dev packages. * **--no-scripts:** Disables the execution of the scripts defined in the root @@ -686,9 +831,14 @@ By default the command checks for the packages on packagist.org. mode. * **--remove-vcs:** Force-remove the VCS metadata without prompting. * **--no-install:** Disables installation of the vendors. -* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` - requirements and force the installation even if the local machine does not - fulfill these. +* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`, + `lib-*` and `ext-*`) and force the installation even if the local machine does + not fulfill these. + See also the [`platform`](06-config.md#platform) config option. +* **--ignore-platform-req:** ignore a specific platform requirement(`php`, + `hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine + does not fulfill it. +* **--ask:** Ask user to provide target directory for new project. ## dump-autoload (dumpautoload) @@ -707,11 +857,21 @@ performance. * **--no-scripts:** Skips the execution of all scripts defined in `composer.json` file. * **--optimize (-o):** Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but can take - a bit of time to run so it is currently not done by default. + a bit of time to run, so it is currently not done by default. * **--classmap-authoritative (-a):** Autoload classes from the classmap only. Implicitly enables `--optimize`. * **--apcu:** Use APCu to cache found/not-found classes. -* **--no-dev:** Disables autoload-dev rules. +* **--apcu-prefix:** Use a custom prefix for the APCu autoloader cache. + Implicitly enables `--apcu`. +* **--no-dev:** Disables autoload-dev rules. Composer will by default infer this + automatically according to the last `install` or `update` `--no-dev` state. +* **--dev:** Enables autoload-dev rules. Composer will by default infer this + automatically according to the last `install` or `update` `--no-dev` state. +* **--ignore-platform-reqs:** ignore all `php`, `hhvm`, `lib-*` and `ext-*` + requirements and skip the [platform check](07-runtime.md#platform-check) for these. + See also the [`platform`](06-config.md#platform) config option. +* **--ignore-platform-req:** ignore a specific platform requirement (`php`, `hhvm`, + `lib-*` and `ext-*`) and skip the [platform check](07-runtime.md#platform-check) for it. ## clear-cache / clearcache / cc @@ -720,11 +880,11 @@ Deletes all content from Composer's cache directories. ## licenses Lists the name, version and license of every package installed. Use -`--format=json` to get machine readable output. +`--format=json` to get machine-readable output. ### Options -* **--format:** Format of the output: text or json (default: "text") +* **--format:** Format of the output: text, json or summary (default: "text") * **--no-dev:** Remove dev dependencies from the output ## run-script @@ -822,9 +982,10 @@ If set to 1, this env allows running Composer when the Xdebug extension is enabl ### COMPOSER_AUTH The `COMPOSER_AUTH` var allows you to set up authentication as an environment variable. -The contents of the variable should be a JSON formatted object containing http-basic, -github-oauth, bitbucket-oauth, ... objects as needed, and following the -[spec from the config](06-config.md#gitlab-oauth). +The contents of the variable should be a JSON formatted object containing [http-basic, +github-oauth, bitbucket-oauth, ... objects as needed](articles/authentication-for-private-packages.md), +and following the +[spec from the config](06-config.md). ### COMPOSER_BIN_DIR @@ -836,7 +997,7 @@ directory to something other than `vendor/bin`. The `COMPOSER_CACHE_DIR` var allows you to change the Composer cache directory, which is also configurable via the [`cache-dir`](06-config.md#cache-dir) option. -By default it points to `$COMPOSER_HOME/cache` on \*nix and macOS, and +By default, it points to `$COMPOSER_HOME/cache` on \*nix and macOS, and `C:\Users\\AppData\Local\Composer` (or `%LOCALAPPDATA%/Composer`) on Windows. ### COMPOSER_CAFILE @@ -858,7 +1019,9 @@ The `COMPOSER_HOME` var allows you to change the Composer home directory. This is a hidden, global (per-user on the machine) directory that is shared between all projects. -By default it points to `C:\Users\\AppData\Roaming\Composer` on Windows +Use `composer config --global home` to see the location of the home directory. + +By default, it points to `C:\Users\\AppData\Roaming\Composer` on Windows and `/Users//.composer` on macOS. On \*nix systems that follow the [XDG Base Directory Specifications](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html), it points to `$XDG_CONFIG_HOME/composer`. On other \*nix systems, it points to @@ -903,14 +1066,23 @@ commands) to finish executing. The default value is 300 seconds (5 minutes). ### COMPOSER_ROOT_VERSION -By setting this var you can specify the version of the root package, if it can -not be guessed from VCS info and is not present in `composer.json`. +By setting this var you can specify the version of the root package, if it +cannot be guessed from VCS info and is not present in `composer.json`. ### COMPOSER_VENDOR_DIR By setting this var you can make Composer install the dependencies into a directory other than `vendor`. +### COMPOSER_RUNTIME_ENV + +This lets you hint under which environment Composer is running, which can help Composer +work around some environment specific issues. The only value currently supported is +`virtualbox`, which then enables some short `sleep()` calls to wait for the filesystem +to have written files properly before we attempt reading them. You can set the +environment variable if you use Vagrant or VirtualBox and experience issues with files not +being found during installation even though they should be present. + ### http_proxy or HTTP_PROXY If you are using Composer from behind an HTTP proxy, you can use the standard @@ -927,15 +1099,22 @@ similar use case), and need to support proxies, please provide the `CGI_HTTP_PRO environment variable instead. See [httpoxy.org](https://httpoxy.org/) for further details. +### COMPOSER_MAX_PARALLEL_HTTP + +Set to an integer to configure how many files can be downloaded in parallel. This +defaults to 12 and must be between 1 and 50. If your proxy has issues with +concurrency maybe you want to lower this. Increasing it should generally not result +in performance gains. + ### HTTP_PROXY_REQUEST_FULLURI -If you use a proxy but it does not support the request_fulluri flag, then you +If you use a proxy, but it does not support the request_fulluri flag, then you should set this env var to `false` or `0` to prevent Composer from setting the request_fulluri option. ### HTTPS_PROXY_REQUEST_FULLURI -If you use a proxy but it does not support the request_fulluri flag for HTTPS +If you use a proxy, but it does not support the request_fulluri flag for HTTPS requests, then you should set this env var to `false` or `0` to prevent Composer from setting the request_fulluri option. @@ -953,4 +1132,17 @@ The env var accepts domains, IP addresses, and IP address blocks in CIDR notation. You can restrict the filter to a particular port (e.g. `:80`). You can also set it to `*` to ignore the proxy for all HTTP requests. +### COMPOSER_DISABLE_NETWORK + +If set to `1`, disables network access (best effort). This can be used for debugging or +to run Composer on a plane or a starship with poor connectivity. + +If set to `prime`, GitHub VCS repositories will prime the cache, so it can then be used +fully offline with `1`. + +### COMPOSER_DEBUG_EVENTS + +If set to `1`, outputs information about events being dispatched, which can be +useful for plugin authors to identify what is firing when exactly. + ← [Libraries](02-libraries.md) | [Schema](04-schema.md) → diff --git a/app/vendor/composer/composer/doc/04-schema.md b/app/vendor/composer/composer/doc/04-schema.md index 8599d934b..849e5ee68 100644 --- a/app/vendor/composer/composer/doc/04-schema.md +++ b/app/vendor/composer/composer/doc/04-schema.md @@ -1,10 +1,10 @@ -# The composer.json Schema +# The composer.json schema This chapter will explain all of the fields available in `composer.json`. ## JSON schema -We have a [JSON schema](http://json-schema.org) that documents the format and +We have a [JSON schema](https://json-schema.org) that documents the format and can also be used to validate your `composer.json`. In fact, it is used by the `validate` command. You can find it at: https://getcomposer.org/schema.json @@ -34,12 +34,12 @@ separated by `/`. Examples: * monolog/monolog * igorw/event-source -The name can contain any character, including white spaces, and it's case -insensitive (`foo/bar` and `Foo/Bar` are considered the same package). In order -to simplify its installation, it's recommended to define a short and lowercase -name that doesn't include non-alphanumeric characters or white spaces. +The name must be lowercased and consist of words separated by `-`, `.` or `_`. +The complete name should match `^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]?|-{0,2})[a-z0-9]+)*$`. -Required for published packages (libraries). +The `name` property is required for published packages (libraries). + +> **Note:** Before Composer version 2.0, a name could contain any character, including white spaces. ### description @@ -123,7 +123,7 @@ Optional. ### homepage -An URL to the website of the project. +A URL to the website of the project. Optional. @@ -160,7 +160,7 @@ The recommended notation for the most common licenses is (alphabetical): Optional, but it is highly recommended to supply this. More identifiers are listed at the [SPDX Open Source License Registry](https://spdx.org/licenses/). -For closed-source software, you may use `"proprietary"` as the license identifier. +> **Note:** For closed-source software, you may use `"proprietary"` as the license identifier. An Example: @@ -184,7 +184,7 @@ An Example for disjunctive licenses: } ``` -Alternatively they can be separated with "or" and enclosed in parenthesis; +Alternatively they can be separated with "or" and enclosed in parentheses; ```json { @@ -192,8 +192,8 @@ Alternatively they can be separated with "or" and enclosed in parenthesis; } ``` -Similarly when multiple licenses need to be applied ("conjunctive license"), -they should be separated with "and" and enclosed in parenthesis. +Similarly, when multiple licenses need to be applied ("conjunctive license"), +they should be separated with "and" and enclosed in parentheses. ### authors @@ -214,7 +214,7 @@ An example: { "name": "Nils Adermann", "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de", + "homepage": "https://www.naderman.de", "role": "Developer" }, { @@ -265,8 +265,8 @@ development of new functionality. Each entry consists of the following -* **type:** The type of funding or the platform through which funding can be provided, e.g. patreon, opencollective, tidelift or github. -* **url:** URL to a website with details and a way to fund the package. +* **type:** The type of funding, or the platform through which funding can be provided, e.g. patreon, opencollective, tidelift or github. +* **url:** URL to a website with details, and a way to fund the package. An example: @@ -310,7 +310,7 @@ Example: All links are optional fields. `require` and `require-dev` additionally support _stability flags_ ([root-only](04-schema.md#root-package)). -They take the form "_constraint_@_stability flag_". +They take the form "_constraint_@_stability flag_". These allow you to further restrict or expand the stability of a package beyond the scope of the [minimum-stability](#minimum-stability) setting. You can apply them to a constraint, or apply them to an empty _constraint_ if you want to @@ -382,7 +382,7 @@ Example: ```json { "require" : { - "php" : "^5.5 || ^7.0", + "php" : ">=7.4", "ext-mbstring": "*" } } @@ -401,19 +401,19 @@ Example: #### require -Lists packages required by this package. The package will not be installed +Map of packages required by this package. The package will not be installed unless those requirements can be met. #### require-dev ([root-only](04-schema.md#root-package)) -Lists packages required for developing this package, or running +Map of packages required for developing this package, or running tests, etc. The dev requirements of the root package are installed by default. Both `install` or `update` support the `--no-dev` option that prevents dev dependencies from being installed. #### conflict -Lists packages that conflict with this version of this package. They +Map of packages that conflict with this version of this package. They will not be allowed to be installed together with your package. Note that when specifying ranges like `<1.0 >=1.1` in a `conflict` link, @@ -423,7 +423,7 @@ probably want to go for `<1.0 || >=1.1` in this case. #### replace -Lists packages that are replaced by this package. This allows you to fork a +Map of packages that are replaced by this package. This allows you to fork a package, publish it under a different name with its own version numbers, while packages requiring the original package continue to work with your fork because it replaces the original package. @@ -441,10 +441,12 @@ that exact version, and not any other version, which would be incorrect. #### provide -List of other packages that are provided by this package. This is mostly +Map of packages that are provided by this package. This is mostly useful for implementations of common interfaces. A package could depend on -some virtual `logger-implementation` package, any library that implements -this logger interface would list it in `provide`. +some virtual package e.g. `psr/logger-implementation`, any library that implements +this logger interface would list it in `provide`. Implementors can then +be [found on Packagist.org](https://packagist.org/providers/psr/log-implementation). + Using `provide` with the name of an actual package rather than a virtual one implies that the code of that package is also shipped, in which case `replace` is generally a better choice. A common convention for packages providing an @@ -477,7 +479,7 @@ Example: Autoload mapping for a PHP autoloader. -[`PSR-4`](http://www.php-fig.org/psr/psr-4/) and [`PSR-0`](http://www.php-fig.org/psr/psr-0/) +[`PSR-4`](https://www.php-fig.org/psr/psr-4/) and [`PSR-0`](http://www.php-fig.org/psr/psr-0/) autoloading, `classmap` generation and `files` includes are supported. PSR-4 is the recommended way since it offers greater ease of use (no need @@ -616,6 +618,18 @@ Example: } ``` +Wildcards (`*`) are also supported in a classmap paths, and expand to match any directory name: + +Example: + +```json +{ + "autoload": { + "classmap": ["src/addons/*/lib/", "3rd-party/*", "Something.php"] + } +} +``` + #### Files If you want to require certain files explicitly on every request then you can use @@ -776,8 +790,6 @@ The following repository types are supported: using the `options` parameter. * **vcs:** The version control system repository can fetch packages from git, svn, fossil and hg repositories. -* **pear:** With this you can import any pear repository into your Composer - project. * **package:** If you depend on a project that does not have any support for composer whatsoever you can define the package inline using a `package` repository. You basically inline the `composer.json` object. @@ -806,10 +818,6 @@ Example: "type": "vcs", "url": "https://github.com/Seldaek/monolog" }, - { - "type": "pear", - "url": "https://pear2.php.net" - }, { "type": "package", "package": { @@ -889,6 +897,21 @@ A set of options for creating package archives. The following options are supported: +* **name:** Allows configuring base name for archive. + By default (if not configured, and `--file` is not passed as command-line argument), + `preg_replace('#[^a-z0-9-_]#i', '-', name)` is used. + +Example: + +```json +{ + "name": "org/strangeName", + "archive": { + "name": "Strange_name" + } +} +``` + * **exclude:** Allows configuring a list of patterns for excluded paths. The pattern syntax matches .gitignore files. A leading exclamation mark (!) will result in any matching files to be included even if a previous pattern @@ -919,7 +942,7 @@ It can be boolean or a package name/URL pointing to a recommended alternative. Examples: Use `"abandoned": true` to indicates this package is abandoned. -Use `"abandoned": "monolog/monolog"` to indicates this package is abandoned and the +Use `"abandoned": "monolog/monolog"` to indicates this package is abandoned, and the recommended alternative is `monolog/monolog`. Defaults to false. @@ -934,7 +957,7 @@ that will NOT be handled as feature branches. This is an array of strings. If you have non-numeric branch names, for example like "latest", "current", "latest-stable" or something, that do not look like a version number, then Composer handles such branches as feature branches. This means it searches for parent branches, that look like a version -or ends at special branches (like master) and the root package version number becomes the +or ends at special branches (like master), and the root package version number becomes the version of the parent branch or at least master or something. To handle non-numeric named branches as versions instead of searching for a parent branch diff --git a/app/vendor/composer/composer/doc/05-repositories.md b/app/vendor/composer/composer/doc/05-repositories.md index 4c1c7e000..f8a68fa30 100644 --- a/app/vendor/composer/composer/doc/05-repositories.md +++ b/app/vendor/composer/composer/doc/05-repositories.md @@ -6,7 +6,7 @@ of repositories are available, and how they work. ## Concepts Before we look at the different types of repositories that exist, we need to -understand some of the basic concepts that Composer is built on. +understand some basic concepts that Composer is built on. ### Package @@ -41,7 +41,7 @@ be preferred. A repository is a package source. It's a list of packages/versions. Composer will look in all your repositories to find the packages your project requires. -By default only the Packagist repository is registered in Composer. You can +By default, only the Packagist.org repository is registered in Composer. You can add more repositories to your project by declaring them in `composer.json`. Repositories are only available to the root package and the repositories @@ -49,6 +49,12 @@ defined in your dependencies will not be loaded. Read the [FAQ entry](faqs/why-can't-composer-load-repositories-recursively.md) if you want to learn why. +When resolving dependencies, packages are looked up from repositories from +top to bottom, and by default, as soon as a package is found in one, Composer +stops looking in other repositories. Read the +[repository priorities](articles/repository-priorities.md) article for more +details and to see how to change this behavior. + ## Types ### Composer @@ -62,6 +68,17 @@ In the case of packagist, that file is located at `/packages.json`, so the URL o the repository would be `repo.packagist.org`. For `example.org/packages.json` the repository URL would be `example.org`. +```json +{ + "repositories": [ + { + "type": "composer", + "url": "https://example.org" + } + ] +} +``` + #### packages The only required field is `packages`. The JSON structure is as follows: @@ -105,7 +122,7 @@ It may include any of the other fields specified in the [schema](04-schema.md). The `notify-batch` field allows you to specify a URL that will be called every time a user installs a package. The URL can be either an absolute path -(that will use the same domain as the repository) or a fully qualified URL. +(that will use the same domain as the repository), or a fully qualified URL. An example value: @@ -132,6 +149,102 @@ number. This field is optional. +### metadata-url, available-packages and available-package-patterns + +The `metadata-url` field allows you to provide a URL template to serve all +packages which are in the repository. It must contain the placeholder +`%package%`. + +This field is new in Composer v2, and is prioritised over the +`provider-includes` and `providers-url` fields if both are present. +For compatibility with both Composer v1 and v2 you ideally want +to provide both. New repository implementations may only need to +support v2 however. + +An example: + +```json +{ + "metadata-url": "/p2/%package%.json" +} +``` + +Whenever Composer looks for a package, it will replace `%package%` by the +package name, and fetch that URL. If dev stability is allowed for the package, +it will also load the URL again with `$packageName~dev` (e.g. +`/p2/foo/bar~dev.json` to look for `foo/bar`'s dev versions). + +The `foo/bar.json` and `foo/bar~dev.json` files containing package versions +MUST contain only versions for the foo/bar package, as +`{"packages":{"foo/bar":[ ... versions here ... ]}}`. + +Caching is done via the use of If-Modified-Since header, so make sure you +return Last-Modified headers and that they are accurate. + +The array of versions can also optionally be minified using +`Composer\MetadataMinifier\MetadataMinifier::minify()` from +[composer/metadata-minifier](https://packagist.org/packages/composer/metadata-minifier). +If you do that, you should add a `"minified": "composer/2.0"` key +at the top level to indicate to Composer it must expand the version +list back into the original data. See +https://repo.packagist.org/p2/monolog/monolog.json for an example. + +Any requested package which does not exist MUST return a 404 status code, +which will indicate to Composer that this package does not exist in your +repository. Make sure the 404 response is fast to avoid blocking Composer. +Avoid redirects to alternative 404 pages. + +If your repository only has a small number of packages, and you want to avoid +the 404-requests, you can also specify an `"available-packages"` key in +`packages.json` which should be an array with all the package names that your +repository contain. Alternatively you can specify an +`"available-package-patterns"` key which is an array of package name patterns +(with `*` matching any string, e.g. `vendor/*` would make composer look up +every matching package name in this repository). + +This field is optional. + +### providers-api + +The `providers-api` field allows you to provide a URL template to serve all +packages which provide a given package name, but not the package which has +that name. It must contain the placeholder `%package%`. + +For example https://packagist.org/providers/monolog/monolog.json lists some +package which have a "provide" rule for monolog/monolog, but it does not list +monolog/monolog itself. + +```json +{ + "providers-api": "https://packagist.org/providers/%package%.json", +} +``` + +This field is optional. + +### list + +The `list` field allows you to return the names of packages which match a +given field (or all names if no filter is present). It should accept an +optional `?filter=xx` query param, which can contain `*` as wildcards matching +any substring. + +Replace/provide rules should not be considered here. + +It must return an array of package names: +```json +{ + "packageNames": [ + "a/b", + "c/d" + ] +} +``` + +See for example. + +This field is optional. + #### provider-includes and providers-url The `provider-includes` field allows you to list a set of files that list @@ -142,6 +255,9 @@ The `providers-url` describes how provider files are found on the server. It is an absolute path from the repository root. It must contain the placeholders `%package%` and `%hash%`. +These fields are used by Composer v1, or if your repository does not have the +`metadata-url` field set. + An example: ```json @@ -200,7 +316,7 @@ from these systems. There are a few use cases for this. The most common one is maintaining your own fork of a third party library. If you are using a certain library for your -project and you decide to change something in the library, you will want your +project, and you decide to change something in the library, you will want your project to use the patched version. If the library is on GitHub (this is the case most of the time), you can fork it there and push your changes to your fork. After that you update the project's `composer.json`. All you have @@ -299,23 +415,10 @@ Please note: #### BitBucket Driver Configuration -The BitBucket driver uses OAuth to access your private repositories via the BitBucket REST APIs and you will need to create an OAuth consumer to use the driver, please refer to [Atlassian's Documentation](https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html). You will need to fill the callback url with something to satisfy BitBucket, but the address does not need to go anywhere and is not used by Composer. +> **Note that the repository endpoint for BitBucket needs to be https rather than git.** -After creating an OAuth consumer in the BitBucket control panel, you need to setup your auth.json file with -the credentials like this (more info [here](https://getcomposer.org/doc/06-config.md#bitbucket-oauth)): -```json -{ - "bitbucket-oauth": { - "bitbucket.org": { - "consumer-key": "myKey", - "consumer-secret": "mySecret" - } - } -} -``` -**Note that the repository endpoint needs to be https rather than git.** - -Alternatively if you prefer not to have your OAuth credentials on your filesystem you may export the ```bitbucket-oauth``` block above to the [COMPOSER_AUTH](https://getcomposer.org/doc/03-cli.md#composer-auth) environment variable instead. +After setting up your bitbucket repository, you will also need to +[set up authentication](articles/authentication-for-private-packages.md#bitbucket-oauth). #### Subversion Options @@ -378,92 +481,6 @@ for this server will be overwritten. To change this behavior by setting the } ``` -### PEAR - -It is possible to install packages from any PEAR channel by using the `pear` -repository. Composer will prefix all package names with `pear-{channelName}/` -to avoid conflicts. All packages are also aliased with prefix -`pear-{channelAlias}/`. - -Example using `pear2.php.net`: - -```json -{ - "repositories": [ - { - "type": "pear", - "url": "https://pear2.php.net" - } - ], - "require": { - "pear-pear2.php.net/PEAR2_Text_Markdown": "*", - "pear-pear2/PEAR2_HTTP_Request": "*" - } -} -``` - -In this case the short name of the channel is `pear2`, so the -`PEAR2_HTTP_Request` package name becomes `pear-pear2/PEAR2_HTTP_Request`. - -> **Note:** The `pear` repository requires doing quite a few requests per -> package, so this may considerably slow down the installation process. - -#### Custom vendor alias - -It is possible to alias PEAR channel packages with a custom vendor name. - -Example: - -Suppose you have a private PEAR repository and wish to use Composer to -incorporate dependencies from a VCS. Your PEAR repository contains the -following packages: - - * `BasePackage` - * `IntermediatePackage`, which depends on `BasePackage` - * `TopLevelPackage1` and `TopLevelPackage2` which both depend - on `IntermediatePackage` - -Without a vendor alias, Composer will use the PEAR channel name as the -vendor portion of the package name: - - * `pear-pear.foobar.repo/BasePackage` - * `pear-pear.foobar.repo/IntermediatePackage` - * `pear-pear.foobar.repo/TopLevelPackage1` - * `pear-pear.foobar.repo/TopLevelPackage2` - -Suppose at a later time you wish to migrate your PEAR packages to a -Composer repository and naming scheme, and adopt the vendor name of `foobar`. -Projects using your PEAR packages would not see the updated packages, since -they have a different vendor name (`foobar/IntermediatePackage` vs -`pear-pear.foobar.repo/IntermediatePackage`). - -By specifying `vendor-alias` for the PEAR repository from the start, you can -avoid this scenario and future-proof your package names. - -To illustrate, the following example would get the `BasePackage`, -`TopLevelPackage1`, and `TopLevelPackage2` packages from your PEAR repository -and `IntermediatePackage` from a Github repository: - -```json -{ - "repositories": [ - { - "type": "git", - "url": "https://github.com/foobar/intermediate.git" - }, - { - "type": "pear", - "url": "http://pear.foobar.repo", - "vendor-alias": "foobar" - } - ], - "require": { - "foobar/TopLevelPackage1": "*", - "foobar/TopLevelPackage2": "*" - } -} -``` - ### Package If you want to use a project that does not support Composer through any of the @@ -506,7 +523,7 @@ Here is an example for the smarty template engine: } ``` -Typically you would leave the source part off, as you don't really need it. +Typically, you would leave the source part off, as you don't really need it. > **Note**: This repository type has a few limitations and should be avoided > whenever possible: @@ -576,7 +593,7 @@ package repository definitions. It will fetch all the packages that are `require`d and dump a `packages.json` that is your `composer` repository. Check [the satis GitHub repository](https://github.com/composer/satis) and -the [Satis article](articles/handling-private-packages-with-satis.md) for more +the [handling private packages article](articles/handling-private-packages.md) for more information. ### Artifact @@ -585,8 +602,8 @@ There are some cases, when there is no ability to have one of the previously mentioned repository types online, even the VCS one. A typical example could be cross-organisation library exchange through build artifacts. Of course, most of the time these are private. To use these archives as-is, one can use a -repository of type `artifact` with a folder containing ZIP archives of those -private packages: +repository of type `artifact` with a folder containing ZIP or TAR archives of +those private packages: ```json { @@ -712,8 +729,8 @@ variables are parsed in both Windows and Linux/Mac notations. For example `/home//git/mypackage`, equivalent to `$HOME/git/mypackage` or `%USERPROFILE%/git/mypackage`. -> **Note:** Repository paths can also contain wildcards like ``*`` and ``?``. -> For details, see the [PHP glob function](http://php.net/glob). +> **Note:** Repository paths can also contain wildcards like `*` and `?`. +> For details, see the [PHP glob function](https://php.net/glob). ## Disabling Packagist.org diff --git a/app/vendor/composer/composer/doc/06-config.md b/app/vendor/composer/composer/doc/06-config.md index 1761ac7c1..dffff2104 100644 --- a/app/vendor/composer/composer/doc/06-config.md +++ b/app/vendor/composer/composer/doc/06-config.md @@ -5,7 +5,8 @@ This chapter will describe the `config` section of the `composer.json` ## process-timeout -Defaults to `300`. The duration processes like git clones can run before +The timeout in seconds for process executions, defaults to 300 (5mins). +The duration processes like git clones can run before Composer assumes they died out. You may need to make this higher if you have a slow connection or huge vendors. @@ -30,7 +31,7 @@ in the PHP include path. ## preferred-install -Defaults to `auto` and can be any of `source`, `dist` or `auto`. This option +Defaults to `dist` and can be any of `source`, `dist` or `auto`. This option allows you to set the install method Composer will prefer to use. Can optionally be a hash of patterns for more granular install preferences. @@ -47,6 +48,16 @@ optionally be a hash of patterns for more granular install preferences. } ``` +- `source` means Composer will install packages from their `source` if there + is one. This is typically a git clone or equivalent checkout of the version + control system the package uses. This is useful if you want to make a bugfix + to a project and get a local git clone of the dependency directly. +- `auto` is the legacy behavior where Composer uses `source` automatically + for dev versions, and `dist` otherwise. +- `dist` (the default as of Composer 2.1) means Composer installs from `dist`, + where possible. This is typically a zip file download, which is faster than + cloning the entire repository. + > **Note:** Order matters. More specific patterns should be earlier than > more relaxed patterns. When mixing the string notation with the hash > configuration in global and package configurations the string notation @@ -71,9 +82,14 @@ URL. A list of domain names and oauth keys. For example using `{"github.com": "oauthtoken"}` as the value of this option will use `oauthtoken` to access private repositories on github and to circumvent the low IP-based rate limiting -of their API. [Read -more](articles/troubleshooting.md#api-rate-limit-and-oauth-tokens) on how to get -an OAuth token for GitHub. +of their API. Composer may prompt for credentials when needed, but these can also be +manually set. Read more on how to get an OAuth token for GitHub and cli syntax +[here](articles/authentication-for-private-packages.md#github-oauth). + +## gitlab-domains + +Defaults to `["gitlab.com"]`. A list of domains of GitLab servers. +This is used if you use the `gitlab` repository type. ## gitlab-oauth @@ -82,14 +98,31 @@ A list of domain names and oauth keys. For example using `{"gitlab.com": private repositories on gitlab. Please note: If the package is not hosted at gitlab.com the domain names must be also specified with the [`gitlab-domains`](06-config.md#gitlab-domains) option. +Further info can also be found [here](articles/authentication-for-private-packages.md#gitlab-oauth) ## gitlab-token -A list of domain names and private tokens. For example using `{"gitlab.com": +A list of domain names and private tokens. Private token can be either simple +string, or array with username and token. For example using `{"gitlab.com": "privatetoken"}` as the value of this option will use `privatetoken` to access -private repositories on gitlab. Please note: If the package is not hosted at +private repositories on gitlab. Using `{"gitlab.com": {"username": "gitlabuser", + "token": "privatetoken"}}` will use both username and token for gitlab deploy +token functionality (https://docs.gitlab.com/ee/user/project/deploy_tokens/) +Please note: If the package is not hosted at gitlab.com the domain names must be also specified with the -[`gitlab-domains`](06-config.md#gitlab-domains) option. +[`gitlab-domains`](06-config.md#gitlab-domains) option. The token must have +`api` or `read_api` scope. +Further info can also be found [here](articles/authentication-for-private-packages.md#gitlab-token) + +## gitlab-protocol + +A protocol to force use of when creating a repository URL for the `source` +value of the package metadata. One of `git` or `http`. (`https` is treated +as a synonym for `http`.) Helpful when working with projects referencing +private repositories which will later be cloned in GitLab CI jobs with a +[GitLab CI_JOB_TOKEN](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html#predefined-variables-reference) +using HTTP basic auth. By default, Composer will generate a git-over-SSH +URL for private repositories and HTTP(S) only for public. ## disable-tls @@ -109,8 +142,8 @@ get a free SSL certificate is generally a better alternative. ## bitbucket-oauth A list of domain names and consumers. For example using `{"bitbucket.org": -{"consumer-key": "myKey", "consumer-secret": "mySecret"}}`. [Read](https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html) -how to set up a consumer on Bitbucket. +{"consumer-key": "myKey", "consumer-secret": "mySecret"}}`. +Read more [here](articles/authentication-for-private-packages.md#bitbucket-oauth). ## cafile @@ -129,11 +162,7 @@ capath must be a correctly hashed certificate directory. A list of domain names and username/passwords to authenticate against them. For example using `{"example.org": {"username": "alice", "password": "foo"}}` as the value of this option will let Composer authenticate against example.org. - -> **Note:** Authentication-related config options like `http-basic`, `bearer` and -> `github-oauth` can also be specified inside a `auth.json` file that goes -> besides your `composer.json`. That way you can gitignore it and every -> developer can place their own credentials in there. +More info can be found [here](articles/authentication-for-private-packages.md#http-basic). ## bearer @@ -147,6 +176,21 @@ Lets you fake platform packages (PHP and extensions) so that you can emulate a production env or define your target platform in the config. Example: `{"php": "7.0.3", "ext-something": "4.0.3"}`. +This will make sure that no package requiring more than PHP 7.0.3 can be installed +regardless of the actual PHP version you run locally. However it also means +the dependencies are not checked correctly anymore, if you run PHP 5.6 it will +install fine as it assumes 7.0.3, but then it will fail at runtime. + +Therefore if you use this it is recommended, and safer, to also run the +[`check-platform-reqs`](03-cli.md#check-platform-reqs) command as part of your +deployment strategy. + +If a dependency requires some extension that you do not have installed locally +you may ignore it instead by passing `--ignore-platform-req=ext-foo` to `update`, +`install` or `require`. In the long run though you should install required +extensions as if you ignore one now and a new package you add a month later also +requires it, you may introduce issues in production unknowingly. + ## vendor-dir Defaults to `vendor`. You can install dependencies into a different directory if @@ -169,9 +213,10 @@ versions. See also [COMPOSER_HOME](03-cli.md#composer-home). ## cache-dir Defaults to `C:\Users\\AppData\Local\Composer` on Windows, -`$XDG_CACHE_HOME/composer` on unix systems that follow the XDG Base Directory -Specifications, and `$home/cache` on other unix systems. Stores all the caches -used by Composer. See also [COMPOSER_HOME](03-cli.md#composer-home). +`/Users//Library/Caches/composer` on macOS, `$XDG_CACHE_HOME/composer` +on unix systems that follow the XDG Base Directory Specifications, and +`$home/cache` on other unix systems. Stores all the caches used by Composer. +See also [COMPOSER_HOME](03-cli.md#composer-home). ## cache-files-dir @@ -189,26 +234,31 @@ metadata for the `git`/`hg` types and to speed up installs. ## cache-files-ttl -Defaults to `15552000` (6 months). Composer caches all dist (zip, tar, ..) +Defaults to `15552000` (6 months). Composer caches all dist (zip, tar, ...) packages that it downloads. Those are purged after six months of being unused by default. This option allows you to tweak this duration (in seconds) or disable it completely by setting it to 0. ## cache-files-maxsize -Defaults to `300MiB`. Composer caches all dist (zip, tar, ..) packages that it +Defaults to `300MiB`. Composer caches all dist (zip, tar, ...) packages that it downloads. When the garbage collection is periodically ran, this is the maximum size the cache will be able to use. Older (less used) files will be removed first until the cache fits. +## cache-read-only + +Defaults to `false`. Whether to use the Composer cache in read-only mode. + ## bin-compat Defaults to `auto`. Determines the compatibility of the binaries to be installed. -If it is `auto` then Composer only installs .bat proxy files when on Windows. If +If it is `auto` then Composer only installs .bat proxy files when on Windows or WSL. If set to `full` then both .bat files for Windows and scripts for Unix-based operating systems will be installed for each binary. This is mainly useful if you -run Composer inside a linux VM but still want the .bat proxies available for use -in the Windows host OS. +run Composer inside a linux VM but still want the `.bat` proxies available for use +in the Windows host OS. If set to `symlink` Composer will always symlink even on +Windows/WSL. ## prepend-autoloader @@ -250,11 +300,6 @@ used for GitHub Enterprise setups. Defaults to `true`. If `false`, the OAuth tokens created to access the github API will have a date instead of the machine hostname. -## gitlab-domains - -Defaults to `["gitlab.com"]`. A list of domains of GitLab servers. -This is used if you use the `gitlab` repository type. - ## use-github-api Defaults to `true`. Similar to the `no-api` key on a specific repository, @@ -306,4 +351,18 @@ in the composer home, cache, and data directories. Defaults to `true`. If set to `false`, Composer will not create a `composer.lock` file. -← [Repositories](05-repositories.md) | [Community](07-community.md) → +## platform-check + +Defaults to `php-only` which only checks the PHP version. Set to `true` to also +check the presence of extension. If set to `false`, Composer will not create and +require a `platform_check.php` file as part of the autoloader bootstrap. + +## secure-svn-domains + +Defaults to `[]`. Lists domains which should be trusted/marked as using a secure +Subversion/SVN transport. By default svn:// protocol is seen as insecure and will +throw, but you can set this config option to `["example.org"]` to allow using svn +URLs on that hostname. This is a better/safer alternative to disabling `secure-http` +altogether. + +← [Repositories](05-repositories.md) | [Runtime](07-runtime.md) → diff --git a/app/vendor/composer/composer/doc/07-community.md b/app/vendor/composer/composer/doc/07-community.md deleted file mode 100644 index 4296f90cd..000000000 --- a/app/vendor/composer/composer/doc/07-community.md +++ /dev/null @@ -1,35 +0,0 @@ -# Community - -There are many people using Composer already, and quite a few of them are -contributing. - -## Contributing - -If you would like to contribute to Composer, please read the -[README](https://github.com/composer/composer) and -[CONTRIBUTING](https://github.com/composer/composer/blob/master/.github/CONTRIBUTING.md) -documents. - -The most important guidelines are described as follows: - -> All code contributions - including those of people having commit access - must -> go through a pull request and approved by a core developer before being -> merged. This is to ensure proper review of all the code. -> -> Fork the project, create a feature branch, and send us a pull request. -> -> To ensure a consistent code base, you should make sure the code follows -> the [PSR-2 Coding Standards](http://www.php-fig.org/psr/psr-2/). - -## IRC / mailing list - -Mailing lists for [user support](https://groups.google.com/group/composer-users) and -[development](https://groups.google.com/group/composer-dev). - -IRC channels are on irc.freenode.org: [#composer](irc://irc.freenode.org/composer) -for users and [#composer-dev](irc://irc.freenode.org/composer-dev) for development. - -Stack Overflow has a growing collection of -[Composer related questions](https://stackoverflow.com/questions/tagged/composer-php). - -← [Config](06-config.md) diff --git a/app/vendor/composer/composer/doc/07-runtime.md b/app/vendor/composer/composer/doc/07-runtime.md new file mode 100644 index 000000000..3e345ad38 --- /dev/null +++ b/app/vendor/composer/composer/doc/07-runtime.md @@ -0,0 +1,155 @@ +# Runtime Composer utilities + +While Composer is mostly used around your project to install its dependencies, +there are a few things which are made available to you at runtime. + +If you need to rely on some of these in a specific version, you can require +the `composer-runtime-api` package. + +## Autoload + +The autoloader is the most used one, and is already covered in our +[basic usage guide](01-basic-usage.md#autoloading). It is available in all +Composer versions. + +## Installed versions + +composer-runtime-api 2.0 introduced a new `Composer\InstalledVersions` class which offers +a few static methods to inspect which versions are currently installed. This is +automatically available to your code as long as you include the Composer autoloader. + +The main use cases for this class are the following: + +### Knowing whether package X (or virtual package) is present + +```php +\Composer\InstalledVersions::isInstalled('vendor/package'); // returns bool +\Composer\InstalledVersions::isInstalled('psr/log-implementation'); // returns bool +``` + +As of Composer 2.1, you may also check if something was installed via require-dev or not by +passing false as second argument: + +```php +\Composer\InstalledVersions::isInstalled('vendor/package'); // returns true assuming this package is installed +\Composer\InstalledVersions::isInstalled('vendor/package', false); // returns true if vendor/package is in require, false if in require-dev +``` + +Note that this can not be used to check whether platform packages are installed. + +### Knowing whether package X is installed in version Y + +> **Note:** To use this, your package must require `"composer/semver": "^3.0"`. + +```php +use Composer\Semver\VersionParser; + +\Composer\InstalledVersions::satisfies(new VersionParser, 'vendor/package', '2.0.*'); +\Composer\InstalledVersions::satisfies(new VersionParser, 'psr/log-implementation', '^1.0'); +``` + +This will return true if e.g. vendor/package is installed in a version matching +`2.0.*`, but also if the given package name is replaced or provided by some other +package. + +### Knowing the version of package X + +> **Note:** This will return `null` if the package name you ask for is not itself installed +> but merely provided or replaced by another package. We therefore recommend using satisfies() +> in library code at least. In application code you have a bit more control and it is less +> important. + +```php +// returns a normalized version (e.g. 1.2.3.0) if vendor/package is installed, +// or null if it is provided/replaced, +// or throws OutOfBoundsException if the package is not installed at all +\Composer\InstalledVersions::getVersion('vendor/package'); +``` + +```php +// returns the original version (e.g. v1.2.3) if vendor/package is installed, +// or null if it is provided/replaced, +// or throws OutOfBoundsException if the package is not installed at all +\Composer\InstalledVersions::getPrettyVersion('vendor/package'); +``` + +```php +// returns the package dist or source reference (e.g. a git commit hash) if vendor/package is installed, +// or null if it is provided/replaced, +// or throws OutOfBoundsException if the package is not installed at all +\Composer\InstalledVersions::getReference('vendor/package'); +``` + +### Knowing a package's own installed version + +If you are only interested in getting a package's own version, e.g. in the source of acme/foo you want +to know which version acme/foo is currently running to display that to the user, then it is +acceptable to use getVersion/getPrettyVersion/getReference. + +The warning in the section above does not apply in this case as you are sure the package is present +and not being replaced if your code is running. + +It is nonetheless a good idea to make sure you handle the `null` return value as gracefully as +possible for safety. + +---- + +A few other methods are available for more complex usages, please refer to the +source/docblocks of [the class itself](https://github.com/composer/composer/blob/master/src/Composer/InstalledVersions.php). + +### Knowing the path in which a package is installed + +The `getInstallPath` method to retrieve a package's absolute install path. + +```php +// returns an absolute path to the package installation location if vendor/package is installed, +// or null if it is provided/replaced, or the package is a metapackage +// or throws OutOfBoundsException if the package is not installed at all +\Composer\InstalledVersions::getInstallPath('vendor/package'); +``` + +> Available as of Composer 2.1 (i.e. `composer-runtime-api ^2.1`) + +### Knowing which packages of a given type are installed + +The `getInstalledPackagesByType` method accepts a package type (e.g. foo-plugin) and lists +the packages of that type which are installed. You can then use the methods above to retrieve +more information about each package if needed. + +This method should alleviate the need for custom installers placing plugins in a specific path +instead of leaving them in the vendor dir. You can then find plugins to initialize at runtime +via InstalledVersions, including their paths via getInstallPath if needed. + +```php +\Composer\InstalledVersions::getInstalledPackagesByType('foo-plugin'); +``` + +> Available as of Composer 2.1 (i.e. `composer-runtime-api ^2.1`) + +## Platform check + +composer-runtime-api 2.0 introduced a new `vendor/composer/platform_check.php` file, which +is included automatically when you include the Composer autoloader. + +It verifies that platform requirements (i.e. php and php extensions) are fulfilled +by the PHP process currently running. If the requirements are not met, the script +prints a warning with the missing requirements and exits with code 104. + +To avoid an unexpected white page of death with some obscure PHP extension warning in +production, you can run `composer check-platform-reqs` as part of your +deployment/build and if that returns a non-0 code you should abort. + +The default value is `php-only` which only checks the PHP version. + +If you for some reason do not want to use this safety check, and would rather +risk runtime errors when your code executes, you can disable this by setting the +[`platform-check`](06-config.md#platform-check) config option to `false`. + +If you want the check to include verifying the presence of PHP extensions, +set the config option to `true`. `ext-*` requirements will then be verified +but for performance reasons Composer only checks the extension is present, +not its exact version. + +`lib-*` requirements are never supported/checked by the platform check feature. + +← [Config](06-config.md) | [Community](08-community.md) → diff --git a/app/vendor/composer/composer/doc/08-community.md b/app/vendor/composer/composer/doc/08-community.md new file mode 100644 index 000000000..7c48a94a8 --- /dev/null +++ b/app/vendor/composer/composer/doc/08-community.md @@ -0,0 +1,36 @@ +# Community + +There are many people using Composer already, and quite a few of them are +contributing. + +## Contributing + +If you would like to contribute to Composer, please read the +[README](https://github.com/composer/composer) and +[CONTRIBUTING](https://github.com/composer/composer/blob/master/.github/CONTRIBUTING.md) +documents. + +The most important guidelines are described as follows: + +> All code contributions - including those of people having commit access - must +> go through a pull request and approved by a core developer before being +> merged. This is to ensure proper review of all the code. +> +> Fork the project, create a feature branch, and send us a pull request. +> +> To ensure a consistent code base, you should make sure the code follows +> the [PSR-2 Coding Standards](https://www.php-fig.org/psr/psr-2/). + +## Support + +The IRC channel is on irc.libera.chat: [#composer](ircs://irc.libera.chat:6697/composer). + +[Stack Overflow](https://stackoverflow.com/questions/tagged/composer-php) and +[GitHub Discussions](https://github.com/composer/composer/discussions) both have a +collection of Composer related questions. + +For paid support, we do provide Composer-related support via chat and email to +[Private Packagist](https://packagist.com) customers. + + +← [Config](07-runtime.md) diff --git a/app/vendor/composer/composer/doc/articles/aliases.md b/app/vendor/composer/composer/doc/articles/aliases.md index d36eb11ff..c03ec52f2 100644 --- a/app/vendor/composer/composer/doc/articles/aliases.md +++ b/app/vendor/composer/composer/doc/articles/aliases.md @@ -107,5 +107,5 @@ and alias it to `1.0.x-dev`. > inline-aliased again in A's `composer.json`. > **Note:** Inline aliasing should be avoided, especially for published -> packages/libraries. If you found a bug, try and get your fix merged upstream. +> packages/libraries. If you found a bug, try to get your fix merged upstream. > This helps to avoid issues for users of your package. diff --git a/app/vendor/composer/composer/doc/articles/authentication-for-private-packages.md b/app/vendor/composer/composer/doc/articles/authentication-for-private-packages.md new file mode 100644 index 000000000..bbc21ee0d --- /dev/null +++ b/app/vendor/composer/composer/doc/articles/authentication-for-private-packages.md @@ -0,0 +1,301 @@ + + +# Authentication for privately hosted packages and repositories + +Your [private package server](handling-private-packages.md) or version control system is probably secured with one +or more authentication options. In order to allow your project to have access to these +packages and repositories you will have to tell Composer how to authenticate with the server that hosts them. + +# Authentication principles + +Whenever Composer encounters a protected Composer repository it will try to authenticate +using already defined credentials first. When none of those credentials apply it will prompt +for credentials and save them (or a token if Composer is able to retrieve one). + +|type|Generated by Prompt?| +|---|---| +|[http-basic](#http-basic)|yes| +|[Inline http-basic](#inline-http-basic)|no| +|[Custom header](#custom-token-authentication)|no| +|[gitlab-oauth](#gitlab-oauth)|yes| +|[gitlab-token](#gitlab-token)|yes| +|[github-oauth](#github-oauth)|yes| +|[bitbucket-oauth](#bitbucket-oauth)|yes| + +Sometimes automatic authentication is not possible, or you may want to predefine +authentication credentials. + +Credentials can be stored on 3 different places; in an `auth.json` for the project, a global +`auth.json` or in the `composer.json` itself. + +## Authentication in auth.json per project + +In this authentication storage method, an `auth.json` file will be present in the same folder +as the projects' `composer.json` file. You can either create and edit this file using the +command line or manually edit or create it. + +> **Note: Make sure the `auth.json` file is in `.gitignore`** to avoid +> leaking credentials into your git history. + +## Global authentication credentials + +If you don't want to supply credentials for every project you work on, storing your credentials +globally might be a better idea. These credentials are stored in a global `auth.json` in your +Composer home directory. + +### Command line global credential editing + +For all authentication methods it is possible to edit them using the command line; + - [http-basic](#command-line-http-basic) + - [Inline http-basic](#command-line-inline-http-basic) + - [gitlab-oauth](#command-line-gitlab-oauth) + - [gitlab-token](#command-line-gitlab-token) + - [github-oauth](#command-line-github-oauth) + - [bitbucket-oauth](#command-line-bitbucket-oauth) + +### Manually editing global authentication credentials + +> **Note:** It is not recommended to manually edit your authentication options as this might +> result in invalid json. Instead preferably use [the command line](#command-line-global-credential-editing). + +To manually edit it, run: + +```sh +composer config --global --editor [--auth] +``` + +For specific authentication implementations, see their sections; + - [http-basic](#manual-http-basic) + - [Inline http-basic](#manual-inline-http-basic) + - [custom header](#manual-custom-token-authentication) + - [gitlab-oauth](#manual-gitlab-oauth) + - [gitlab-token](#manual-gitlab-token) + - [github-oauth](#manual-github-oauth) + - [bitbucket-oauth](#manual-bitbucket-oauth) + +Manually editing this file instead of using the command line may result in invalid json errors. +To fix this you need to open the file in an editor and fix the error. To find the location of +your global `auth.json`, execute: + +```sh +composer config --global home +``` + +The folder will contain your global `auth.json` if it exists. + +You can open this file in your favorite editor and fix the error. + +## Authentication in composer.json file itself + +> **Note:** **This is not recommended** as these credentials are visible +> to anyone who has access to the composer.json, either when it is shared through +> a version control system like git or when an attacker gains (read) access to +> your production server files. + +It is also possible to add credentials to a `composer.json` on a per-project basis in the `config` +section or directly in the repository definition. + +## Authentication using the COMPOSER_AUTH environment variable + +> **Note:** Using the command line environment variable method also has security implications. +> These credentials will most likely be stored in memory, +> and on be persisted to a file like `~/.bash_history`(linux) or `ConsoleHost_history.txt` +> (PowerShell on Windows) when closing a session. + +The final option to supply Composer with credentials is to use the `COMPOSER_AUTH` environment variable. +These variables can be either passed as command line variables or set in actual environment variables. +Read more about the usage of this environment variable [here](../03-cli.md#composer-auth). + +# Authentication methods + +## http-basic + +### Command line http-basic + +```sh +composer config [--global] http-basic.example.org username password +``` + +### Manual http-basic + +```sh +composer config [--global] --editor --auth +``` + +```json +{ + "http-basic": { + "example.org": { + "username": "username", + "password": "password" + } + } +} +``` + +## Inline http-basic + +For the inline http-basic authentication method the credentials are not stored in a separate +`auth.json` in the project or globally, but in the `composer.json` or global configuration +in the same place where the Composer repository definition is defined. + +Make sure that the username and password are encoded according to [RFC 3986](http://www.faqs.org/rfcs/rfc3986.html) (2.1. Percent-Encoding). +If the username e.g. is an email address it needs to be passed as `name%40example.com`. + +### Command line inline http-basic + +```sh +composer config [--global] repositories composer.unique-name https://username:password@repo.example.org +``` + +### Manual inline http-basic + +```sh +composer config [--global] --editor +``` + +```json +{ + "repositories": [ + { + "type": "composer", + "url": "https://username:password@example.org" + } + ] +} +``` + +## Custom token authentication + +### Manual custom token authentication + +```sh +composer config [--global] --editor +``` + +```json +{ + "repositories": [ + { + "type": "composer", + "url": "https://example.org", + "options": { + "http": { + "header": [ + "API-TOKEN: YOUR-API-TOKEN" + ] + } + } + } + ] +} +``` + +## gitlab-oauth + +> **Note:** For the gitlab authentication to work on private gitlab instances, the +> [`gitlab-domains`](../06-config.md#gitlab-domains) section should also contain the url. + +### Command line gitlab-oauth + +```sh +composer config [--global] gitlab-oauth.example.org token +``` + +### Manual gitlab-oauth + +```sh +composer config [--global] --editor --auth +``` + +```json +{ + "gitlab-oauth": { + "example.org": "token" + } +} +``` + +## gitlab-token + +> **Note:** For the gitlab authentication to work on private gitlab instances, the +> [`gitlab-domains`](../06-config.md#gitlab-domains) section should also contain the url. + +To create a new access token, go to your [access tokens section on GitLab](https://gitlab.com/-/profile/personal_access_tokens) +(or the equivalent URL on your private instance) and create a new token. See also [the GitLab access token documentation](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#creating-a-personal-access-token) for more informations. + +When creating a gitlab token manually, make sure it has either the `read_api` or `api` scope. + +### Command line gitlab-token + +```sh +composer config [--global] gitlab-token.example.org token +``` + +### Manual gitlab-token + +```sh +composer config [--global] --editor --auth +``` + +```json +{ + "gitlab-token": { + "example.org": "token" + } +} +``` + +## github-oauth + +To create a new access token, head to your [token settings section on Github](https://github.com/settings/tokens) and [generate a new token](https://github.com/settings/tokens/new). For public repositories when rate limited, the `public_repo` scope is required, for private repositories the `repo:status` scope is needed. +Read more about it [here](https://github.com/blog/1509-personal-api-tokens). + +### Command line github-oauth + +```sh +composer config [--global] github-oauth.github.com token +``` + +### Manual github-oauth + +```sh +composer config [--global] --editor --auth +``` + +```json +{ + "github-oauth": { + "github.com": "token" + } +} +``` + +## bitbucket-oauth + +The BitBucket driver uses OAuth to access your private repositories via the BitBucket REST APIs, and you will need to create an OAuth consumer to use the driver, please refer to [Atlassian's Documentation](https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/). You will need to fill the callback url with something to satisfy BitBucket, but the address does not need to go anywhere and is not used by Composer. + +### Command line bitbucket-oauth + +```sh +composer config [--global] bitbucket-oauth.bitbucket.org consumer-key consumer-secret +``` + +### Manual bitbucket-oauth + +```sh +composer config [--global] --editor --auth +``` + +```json +{ + "bitbucket-oauth": { + "bitbucket.org": { + "consumer-key": "key", + "consumer-secret": "secret" + } + } +} +``` diff --git a/app/vendor/composer/composer/doc/articles/autoloader-optimization.md b/app/vendor/composer/composer/doc/articles/autoloader-optimization.md index 4479e9002..07a0739bf 100644 --- a/app/vendor/composer/composer/doc/articles/autoloader-optimization.md +++ b/app/vendor/composer/composer/doc/articles/autoloader-optimization.md @@ -2,7 +2,7 @@ tagline: How to reduce the performance impact of the autoloader --> -# Autoloader Optimization +# Autoloader optimization By default, the Composer autoloader runs relatively fast. However, due to the way PSR-4 and PSR-0 autoloading rules are set up, it needs to check the filesystem @@ -48,7 +48,7 @@ There are no real trade-offs with this method. It should always be enabled in production. The only issue is it does not keep track of autoload misses (i.e. when -it can not find a given class), so those fallback to PSR-4 rules and can still +it cannot find a given class), so those fallback to PSR-4 rules and can still result in slow filesystem checks. To solve this issue two Level 2 optimization options exist, and you can decide to enable either if you have a lot of class_exists checks that are done for classes that do not exist in your project. @@ -78,7 +78,7 @@ also means that in case a class is generated at runtime for some reason, it will not be allowed to be autoloaded. If your project or any of your dependencies does that then you might experience "class not found" issues in production. Enable this with care. -> Note: This can not be combined with Level 2/B optimizations. You have to choose one as +> Note: This cannot be combined with Level 2/B optimizations. You have to choose one as > they address the same issue in different ways. ## Optimization Level 2/B: APCu cache @@ -97,15 +97,15 @@ This option adds an APCu cache as a fallback for the class map. It will not automatically generate the class map though, so you should still enable Level 1 optimizations manually if you so desire. -Whether a class is found or not, that fact is always cached in APCu so it can be +Whether a class is found or not, that fact is always cached in APCu, so it can be returned quickly on the next request. ### Trade-offs This option requires APCu which may or may not be available to you. It also -uses APCu memory for autoloading purposes, but it is safe to use and can not +uses APCu memory for autoloading purposes, but it is safe to use and cannot result in classes not being found like the authoritative class map optimization above. -> Note: This can not be combined with Level 2/A optimizations. You have to choose one as +> Note: This cannot be combined with Level 2/A optimizations. You have to choose one as > they address the same issue in different ways. diff --git a/app/vendor/composer/composer/doc/articles/custom-installers.md b/app/vendor/composer/composer/doc/articles/custom-installers.md index bf327428a..9c0ee2b65 100644 --- a/app/vendor/composer/composer/doc/articles/custom-installers.md +++ b/app/vendor/composer/composer/doc/articles/custom-installers.md @@ -6,7 +6,7 @@ ## Synopsis -At times it may be necessary for a package to require additional actions during +At times, it may be necessary for a package to require additional actions during installation, such as installing packages outside of the default `vendor` library. @@ -149,6 +149,7 @@ source for the exact signature): when the package needs to be removed. * **getInstallPath()**, this method should return the location where the package is to be installed, _relative from the location of composer.json._ + The path _must not end with a slash._ Example: diff --git a/app/vendor/composer/composer/doc/articles/handling-private-packages-with-satis.md b/app/vendor/composer/composer/doc/articles/handling-private-packages-with-satis.md deleted file mode 100644 index f1c681afc..000000000 --- a/app/vendor/composer/composer/doc/articles/handling-private-packages-with-satis.md +++ /dev/null @@ -1,356 +0,0 @@ - - -# Handling private packages - -# Private Packagist - -[Private Packagist](https://packagist.com) is a commercial package hosting product -offering professional support and web based management of private and public packages, -and granular access permissions. Private Packagist provides mirroring for packages' zip -files which makes installs faster and independent from third party systems - e.g. -you can deploy even if GitHub is down because your zip files are mirrored. - -Private Packagist is available as a hosted SaaS solution or as an on-premise self-hosted -package, providing an interactive set up experience. - -Some of Private Packagist's revenue is used to pay for Composer and Packagist.org -development and hosting so using it is a good way to support the maintenance of -these open source projects financially. You can find more information about how to -set up your own package archive on [Packagist.com](https://packagist.com). - -# Satis - -Satis on the other hand is open source but only a static `composer` repository -generator. It is a bit like an ultra-lightweight, static file-based version of -packagist and can be used to host the metadata of your company's private -packages, or your own. You can get it from -[GitHub](https://github.com/composer/satis) or install via CLI: - - php composer.phar create-project composer/satis --stability=dev --keep-vcs - -## Setup - -For example let's assume you have a few packages you want to reuse across your -company but don't really want to open-source. You would first define a Satis -configuration: a json file with an arbitrary name that lists your curated -[repositories](../05-repositories.md). - -Here is an example configuration, you see that it holds a few VCS repositories, -but those could be any types of [repositories](../05-repositories.md). Then it -uses `"require-all": true` which selects all versions of all packages in the -repositories you defined. - -The default file Satis looks for is `satis.json` in the root of the repository. - -```json -{ - "name": "My Repository", - "homepage": "http://packages.example.org", - "repositories": [ - { "type": "vcs", "url": "https://github.com/mycompany/privaterepo" }, - { "type": "vcs", "url": "http://svn.example.org/private/repo" }, - { "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" } - ], - "require-all": true -} -``` - -If you want to cherry pick which packages you want, you can list all the -packages you want to have in your satis repository inside the classic composer -`require` key, using a `"*"` constraint to make sure all versions are selected, -or another constraint if you want really specific versions. - -```json -{ - "repositories": [ - { "type": "vcs", "url": "https://github.com/mycompany/privaterepo" }, - { "type": "vcs", "url": "http://svn.example.org/private/repo" }, - { "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" } - ], - "require": { - "company/package": "*", - "company/package2": "*", - "company/package3": "2.0.0" - } -} -``` - -Once you've done this, you run: - - php bin/satis build - -When you ironed out that process, what you would typically do is run this -command as a cron job on a server. It would then update all your package info -much like Packagist does. - -Note that if your private packages are hosted on GitHub, your server should -have an ssh key that gives it access to those packages, and then you should add -the `--no-interaction` (or `-n`) flag to the command to make sure it falls back -to ssh key authentication instead of prompting for a password. This is also a -good trick for continuous integration servers. - -Set up a virtual-host that points to that `web/` directory, let's say it is -`packages.example.org`. Alternatively, with PHP >= 5.4.0, you can use the -built-in CLI server `php -S localhost:port -t satis-output-dir/` for a -temporary solution. - -### Partial Updates - -You can tell Satis to selectively update only particular packages or process -only a repository with a given URL. This cuts down the time it takes to rebuild -the `package.json` file and is helpful if you use (custom) webhooks to trigger -rebuilds whenever code is pushed into one of your repositories. - -To rebuild only particular packages, pass the package names on the command line -like so: - - php bin/satis build satis.json web/ this/package that/other-package - -Note that this will still need to pull and scan all of your VCS repositories -because any VCS repository might contain (on any branch) one of the selected -packages. - -If you want to scan only the selected package and not all VCS repositories you need -to declare a *name* for all your package (this only work on VCS repositories type) : - -```json -{ - "repositories": [ - { "name": "company/privaterepo", "type": "vcs", "url": "https://github.com/mycompany/privaterepo" }, - { "name": "private/repo", "type": "vcs", "url": "http://svn.example.org/private/repo" }, - { "name": "mycompany/privaterepo2", "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" } - ] -} -``` - -If you want to scan only a single repository and update all packages found in -it, pass the VCS repository URL as an optional argument: - - php bin/satis build --repository-url https://only.my/repo.git satis.json web/ - -## Usage - -In your projects all you need to add now is your own composer repository using -the `packages.example.org` as URL, then you can require your private packages -and everything should work smoothly. You don't need to copy all your -repositories in every project anymore. Only that one unique repository that -will update itself. - -```json -{ - "repositories": [ { "type": "composer", "url": "http://packages.example.org/" } ], - "require": { - "company/package": "1.2.0", - "company/package2": "1.5.2", - "company/package3": "dev-master" - } -} -``` - -### Security - -To secure your private repository you can host it over SSH or SSL using a client -certificate. In your project you can use the `options` parameter to specify the -connection options for the server. - -Example using a custom repository using SSH (requires the SSH2 PECL extension): - -```json -{ - "repositories": [{ - "type": "composer", - "url": "ssh2.sftp://example.org", - "options": { - "ssh2": { - "username": "composer", - "pubkey_file": "/home/composer/.ssh/id_rsa.pub", - "privkey_file": "/home/composer/.ssh/id_rsa" - } - } - }] -} -``` - -> **Tip:** See [ssh2 context options] for more information. - -Example using SSL/TLS (HTTPS) using a client certificate: - -```json -{ - "repositories": [{ - "type": "composer", - "url": "https://example.org", - "options": { - "ssl": { - "local_cert": "/home/composer/.ssl/composer.pem" - } - } - }] -} -``` - -> **Tip:** See [ssl context options] for more information. - -Example using a custom HTTP Header field for token authentication: - -```json -{ - "repositories": [{ - "type": "composer", - "url": "https://example.org", - "options": { - "http": { - "header": [ - "API-TOKEN: YOUR-API-TOKEN" - ] - } - } - }] -} -``` - -### Authentication - -When your private repositories are password protected, you can store the -authentication details permanently. The first time Composer needs to -authenticate against some domain it will prompt you for a username/password and -then you will be asked whether you want to store it. - -The storage can be done either globally in the `COMPOSER_HOME/auth.json` file -(`COMPOSER_HOME` defaults to `~/.composer` or `%APPDATA%/Composer` on Windows) -or also in the project directory directly sitting besides your composer.json. - -You can also configure these by hand using the config command if you need to -configure a production machine to be able to run non-interactive installs. For -example to enter credentials for example.org one could type: - - composer config http-basic.example.org username password - -That will store it in the current directory's auth.json, but if you want it -available globally you can use the `--global` (`-g`) flag. - -### Downloads - -When GitHub, GitLab or BitBucket repositories are mirrored on your local satis, the -build process will include the location of the downloads these platforms make -available. This means that the repository and your setup depend on the -availability of these services. - -At the same time, this implies that all code which is hosted somewhere else (on -another service or for example in Subversion) will not have downloads available -and thus installations usually take a lot longer. - -To enable your satis installation to create downloads for all (Git, Mercurial -and Subversion) your packages, add the following to your `satis.json`: - -``` json -{ - "archive": { - "directory": "dist", - "format": "tar", - "prefix-url": "https://amazing.cdn.example.org", - "skip-dev": true - } -} -``` - -#### Options explained - - * `directory`: required, the location of the dist files (inside the - `output-dir`) - * `format`: optional, `zip` (default) or `tar` - * `prefix-url`: optional, location of the downloads, homepage (from - `satis.json`) followed by `directory` by default - * `skip-dev`: optional, `false` by default, when enabled (`true`) satis will - not create downloads for branches - * `absolute-directory`: optional, a _local_ directory where the dist files are - dumped instead of `output-dir`/`directory` - * `whitelist`: optional, if set as a list of package names, satis will only - dump the dist files of these packages - * `blacklist`: optional, if set as a list of package names, satis will not - dump the dist files of these packages - * `checksum`: optional, `true` by default, when disabled (`false`) satis will - not provide the sha1 checksum for the dist files - -Once enabled, all downloads (include those from GitHub and BitBucket) will be -replaced with a _local_ version. - -#### prefix-url - -Prefixing the URL with another host is especially helpful if the downloads end -up in a private Amazon S3 bucket or on a CDN host. A CDN would drastically -improve download times and therefore package installation. - -Example: A `prefix-url` of `https://my-bucket.s3.amazonaws.com` (and -`directory` set to `dist`) creates download URLs which look like the following: -`https://my-bucket.s3.amazonaws.com/dist/vendor-package-version-ref.zip`. - -### Web outputs - - * `output-html`: optional, `true` by default, when disabled (`false`) satis - will not generate the `output-dir`/index.html page. - * `twig-template`: optional, a path to a personalized [Twig] template for - the `output-dir`/index.html page. - -### Abandoned packages - -To enable your satis installation to indicate that some packages are abandoned, -add the following to your `satis.json`: - -```json -{ - "abandoned": { - "company/package": true, - "company/package2": "company/newpackage" - } -} -``` - -The `true` value indicates that the package is truly abandoned while the -`"company/newpackage"` value specifies that the package is replaced by the -`company/newpackage` package. - -Note that all packages set as abandoned in their own `composer.json` file will -be marked abandoned as well. - -### Resolving dependencies - -It is possible to make satis automatically resolve and add all dependencies for -your projects. This can be used with the Downloads functionality to have a -complete local mirror of packages. Add the following to your `satis.json`: - -```json -{ - "require-dependencies": true, - "require-dev-dependencies": true -} -``` - -When searching for packages, satis will attempt to resolve all the required -packages from the listed repositories. Therefore, if you are requiring a -package from Packagist, you will need to define it in your `satis.json`. - -Dev dependencies are packaged only if the `require-dev-dependencies` parameter -is set to true. - -### Other options - - * `providers`: optional, `false` by default, when enabled (`true`) each - package will be dumped into a separate include file which will be only - loaded by composer when the package is really required. Speeds up composer - handling for repositories with huge number of packages like f.i. packagist. - * `output-dir`: optional, defines where to output the repository files if not - provided as an argument when calling the `build` command. - * `config`: optional, lets you define all config options from composer, except - `archive-format` and `archive-dir` as the configuration is done through - [archive](#downloads) instead. See docs on [config schema] for more details. - * `notify-batch`: optional, specify a URL that will be called every time a - user installs a package. See [notify-batch]. - -[ssh2 context options]: https://secure.php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-options -[ssl context options]: https://secure.php.net/manual/en/context.ssl.php -[Twig]: https://twig.sensiolabs.org/ -[config schema]: https://getcomposer.org/doc/04-schema.md#config -[notify-batch]: https://getcomposer.org/doc/05-repositories.md#notify-batch diff --git a/app/vendor/composer/composer/doc/articles/handling-private-packages.md b/app/vendor/composer/composer/doc/articles/handling-private-packages.md new file mode 100644 index 000000000..6dd0b8ee9 --- /dev/null +++ b/app/vendor/composer/composer/doc/articles/handling-private-packages.md @@ -0,0 +1,340 @@ + + +# Handling private packages + +# Private Packagist + +[Private Packagist](https://packagist.com) is a commercial package hosting product +offering professional support and web based management of private and public packages, +and granular access permissions. Private Packagist provides mirroring for packages' zip +files which makes installs faster and independent from third party systems - e.g. +you can deploy even if GitHub is down because your zip files are mirrored. + +Private Packagist is available as a hosted SaaS solution or as an on-premise self-hosted +package, providing an interactive set up experience. + +Some of Private Packagist's revenue is used to pay for Composer and Packagist.org +development and hosting so using it is a good way to support the maintenance of +these open source projects financially. You can find more information about how to +set up your own package archive on [Packagist.com](https://packagist.com). + +# Satis + +Satis on the other hand is open source but only a static `composer` repository +generator. It is a bit like an ultra-lightweight, static file-based version of +packagist and can be used to host the metadata of your company's private +packages, or your own. You can get it from +[GitHub](https://github.com/composer/satis) or install via CLI: + + php composer.phar create-project composer/satis --stability=dev --keep-vcs + +## Setup + +For example let's assume you have a few packages you want to reuse across your +company but don't really want to open-source. You would first define a Satis +configuration: a json file with an arbitrary name that lists your curated +[repositories](../05-repositories.md). + +Here is an example configuration, you see that it holds a few VCS repositories, +but those could be any types of [repositories](../05-repositories.md). Then it +uses `"require-all": true` which selects all versions of all packages in the +repositories you defined. + +The default file Satis looks for is `satis.json` in the root of the repository. + +```json +{ + "name": "My Repository", + "homepage": "http://packages.example.org", + "repositories": [ + { "type": "vcs", "url": "https://github.com/mycompany/privaterepo" }, + { "type": "vcs", "url": "http://svn.example.org/private/repo" }, + { "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" } + ], + "require-all": true +} +``` + +If you want to cherry pick which packages you want, you can list all the +packages you want to have in your satis repository inside the classic composer +`require` key, using a `"*"` constraint to make sure all versions are selected, +or another constraint if you want really specific versions. + +```json +{ + "repositories": [ + { "type": "vcs", "url": "https://github.com/mycompany/privaterepo" }, + { "type": "vcs", "url": "http://svn.example.org/private/repo" }, + { "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" } + ], + "require": { + "company/package": "*", + "company/package2": "*", + "company/package3": "2.0.0" + } +} +``` + +Once you've done this, you run: + + php bin/satis build + +When you ironed out that process, what you would typically do is run this +command as a cron job on a server. It would then update all your package info +much like Packagist does. + +Note that if your private packages are hosted on GitHub, your server should +have an ssh key that gives it access to those packages, and then you should add +the `--no-interaction` (or `-n`) flag to the command to make sure it falls back +to ssh key authentication instead of prompting for a password. This is also a +good trick for continuous integration servers. + +Set up a virtual-host that points to that `web/` directory, let's say it is +`packages.example.org`. Alternatively, with PHP >= 5.4.0, you can use the +built-in CLI server `php -S localhost:port -t satis-output-dir/` for a +temporary solution. + +### Partial Updates + +You can tell Satis to selectively update only particular packages or process +only a repository with a given URL. This cuts down the time it takes to rebuild +the `package.json` file and is helpful if you use (custom) webhooks to trigger +rebuilds whenever code is pushed into one of your repositories. + +To rebuild only particular packages, pass the package names on the command line +like so: + + php bin/satis build satis.json web/ this/package that/other-package + +Note that this will still need to pull and scan all of your VCS repositories +because any VCS repository might contain (on any branch) one of the selected +packages. + +If you want to scan only the selected package and not all VCS repositories you need +to declare a *name* for all your package (this only work on VCS repositories type) : + +```json +{ + "repositories": [ + { "name": "company/privaterepo", "type": "vcs", "url": "https://github.com/mycompany/privaterepo" }, + { "name": "private/repo", "type": "vcs", "url": "http://svn.example.org/private/repo" }, + { "name": "mycompany/privaterepo2", "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" } + ] +} +``` + +If you want to scan only a single repository and update all packages found in +it, pass the VCS repository URL as an optional argument: + + php bin/satis build --repository-url https://only.my/repo.git satis.json web/ + +## Usage + +In your projects all you need to add now is your own composer repository using +the `packages.example.org` as URL, then you can require your private packages +and everything should work smoothly. You don't need to copy all your +repositories in every project anymore. Only that one unique repository that +will update itself. + +```json +{ + "repositories": [ { "type": "composer", "url": "http://packages.example.org/" } ], + "require": { + "company/package": "1.2.0", + "company/package2": "1.5.2", + "company/package3": "dev-master" + } +} +``` + +### Security + +To secure your private repository you can host it over SSH or SSL using a client +certificate. In your project you can use the `options` parameter to specify the +connection options for the server. + +Example using a custom repository using SSH (requires the SSH2 PECL extension): + +```json +{ + "repositories": [{ + "type": "composer", + "url": "ssh2.sftp://example.org", + "options": { + "ssh2": { + "username": "composer", + "pubkey_file": "/home/composer/.ssh/id_rsa.pub", + "privkey_file": "/home/composer/.ssh/id_rsa" + } + } + }] +} +``` + +> **Tip:** See [ssh2 context options] for more information. + +Example using SSL/TLS (HTTPS) using a client certificate: + +```json +{ + "repositories": [{ + "type": "composer", + "url": "https://example.org", + "options": { + "ssl": { + "local_cert": "/home/composer/.ssl/composer.pem" + } + } + }] +} +``` + +> **Tip:** See [ssl context options] for more information. + +Example using a custom HTTP Header field for token authentication: + +```json +{ + "repositories": [{ + "type": "composer", + "url": "https://example.org", + "options": { + "http": { + "header": [ + "API-TOKEN: YOUR-API-TOKEN" + ] + } + } + }] +} +``` + +### Authentication + +Authentication can be handled in [several different ways](authentication-for-private-packages.md). + +### Downloads + +When GitHub, GitLab or BitBucket repositories are mirrored on your local satis, the +build process will include the location of the downloads these platforms make +available. This means that the repository and your setup depend on the +availability of these services. + +At the same time, this implies that all code which is hosted somewhere else (on +another service or for example in Subversion) will not have downloads available +and thus installations usually take a lot longer. + +To enable your satis installation to create downloads for all (Git, Mercurial +and Subversion) your packages, add the following to your `satis.json`: + +``` json +{ + "archive": { + "directory": "dist", + "format": "tar", + "prefix-url": "https://amazing.cdn.example.org", + "skip-dev": true + } +} +``` + +#### Options explained + + * `directory`: required, the location of the dist files (inside the + `output-dir`) + * `format`: optional, `zip` (default) or `tar` + * `prefix-url`: optional, location of the downloads, homepage (from + `satis.json`) followed by `directory` by default + * `skip-dev`: optional, `false` by default, when enabled (`true`) satis will + not create downloads for branches + * `absolute-directory`: optional, a _local_ directory where the dist files are + dumped instead of `output-dir`/`directory` + * `whitelist`: optional, if set as a list of package names, satis will only + dump the dist files of these packages + * `blacklist`: optional, if set as a list of package names, satis will not + dump the dist files of these packages + * `checksum`: optional, `true` by default, when disabled (`false`) satis will + not provide the sha1 checksum for the dist files + +Once enabled, all downloads (include those from GitHub and BitBucket) will be +replaced with a _local_ version. + +#### prefix-url + +Prefixing the URL with another host is especially helpful if the downloads end +up in a private Amazon S3 bucket or on a CDN host. A CDN would drastically +improve download times and therefore package installation. + +Example: A `prefix-url` of `https://my-bucket.s3.amazonaws.com` (and +`directory` set to `dist`) creates download URLs which look like the following: +`https://my-bucket.s3.amazonaws.com/dist/vendor-package-version-ref.zip`. + +### Web outputs + + * `output-html`: optional, `true` by default, when disabled (`false`) satis + will not generate the `output-dir`/index.html page. + * `twig-template`: optional, a path to a personalized [Twig] template for + the `output-dir`/index.html page. + +### Abandoned packages + +To enable your satis installation to indicate that some packages are abandoned, +add the following to your `satis.json`: + +```json +{ + "abandoned": { + "company/package": true, + "company/package2": "company/newpackage" + } +} +``` + +The `true` value indicates that the package is truly abandoned while the +`"company/newpackage"` value specifies that the package is replaced by the +`company/newpackage` package. + +Note that all packages set as abandoned in their own `composer.json` file will +be marked abandoned as well. + +### Resolving dependencies + +It is possible to make satis automatically resolve and add all dependencies for +your projects. This can be used with the Downloads functionality to have a +complete local mirror of packages. Add the following to your `satis.json`: + +```json +{ + "require-dependencies": true, + "require-dev-dependencies": true +} +``` + +When searching for packages, satis will attempt to resolve all the required +packages from the listed repositories. Therefore, if you are requiring a +package from Packagist, you will need to define it in your `satis.json`. + +Dev dependencies are packaged only if the `require-dev-dependencies` parameter +is set to true. + +### Other options + + * `providers`: optional, `false` by default, when enabled (`true`) each + package will be dumped into a separate include file which will be only + loaded by composer when the package is really required. Speeds up composer + handling for repositories with huge number of packages like f.i. packagist. + * `output-dir`: optional, defines where to output the repository files if not + provided as an argument when calling the `build` command. + * `config`: optional, lets you define all config options from composer, except + `archive-format` and `archive-dir` as the configuration is done through + [archive](#downloads) instead. See docs on [config schema] for more details. + * `notify-batch`: optional, specify a URL that will be called every time a + user installs a package. See [notify-batch]. + +[ssh2 context options]: https://secure.php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-options +[ssl context options]: https://secure.php.net/manual/en/context.ssl.php +[Twig]: https://twig.sensiolabs.org/ +[config schema]: https://getcomposer.org/doc/04-schema.md#config +[notify-batch]: https://getcomposer.org/doc/05-repositories.md#notify-batch diff --git a/app/vendor/composer/composer/doc/articles/http-basic-authentication.md b/app/vendor/composer/composer/doc/articles/http-basic-authentication.md deleted file mode 100644 index fad17d28f..000000000 --- a/app/vendor/composer/composer/doc/articles/http-basic-authentication.md +++ /dev/null @@ -1,59 +0,0 @@ - - -# HTTP basic authentication - -Your [Satis or Private Packagist](handling-private-packages-with-satis.md) server -could be secured with http basic authentication. In order to allow your project -to have access to these packages you will have to tell composer how to -authenticate with your credentials. - -The simplest way to provide your credentials is providing your set -of credentials inline with the repository specification such as: - -```json -{ - "repositories": [ - { - "type": "composer", - "url": "https://extremely:secret@repo.example.org" - } - ] -} -``` - -This will basically teach composer how to authenticate automatically -when reading packages from the provided composer repository. - -This does not work for everybody especially when you don't want to -hard code your credentials into your composer.json. There is a second -way to provide these details and it is via interaction. If you don't -provide the authentication credentials composer will prompt you upon -connection to enter the username and password. - -The third way if you want to pre-configure it is via an `auth.json` file -located in your `COMPOSER_HOME` or besides your `composer.json`. - -The file should contain a set of hostnames followed each with their own -username/password pairs, for example: - -```json -{ - "http-basic": { - "repo.example1.org": { - "username": "my-username1", - "password": "my-secret-password1" - }, - "repo.example2.org": { - "username": "my-username2", - "password": "my-secret-password2" - } - } -} -``` - -The main advantage of the auth.json file is that it can be gitignored so -that every developer in your team can place their own credentials in there, -which makes revocation of credentials much easier than if you all share the -same. diff --git a/app/vendor/composer/composer/doc/articles/plugins.md b/app/vendor/composer/composer/doc/articles/plugins.md index 86e24d87b..df374405b 100644 --- a/app/vendor/composer/composer/doc/articles/plugins.md +++ b/app/vendor/composer/composer/doc/articles/plugins.md @@ -9,7 +9,7 @@ You may wish to alter or expand Composer's functionality with your own. For example if your environment poses special requirements on the behaviour of Composer which do not apply to the majority of its users or if you wish to -accomplish something with composer in a way that is not desired by most users. +accomplish something with Composer in a way that is not desired by most users. In these cases you could consider creating a plugin to handle your specific logic. @@ -30,21 +30,29 @@ requirements: multiple plugins, this can be array of class names. 3. You must require the special package called `composer-plugin-api` to define which Plugin API versions your plugin is compatible with. + Requiring this package doesn't actually include any extra dependencies, + it only specifies which version of the plugin API to use. + +> **Note:** When developing a plugin, although not required, it's useful to add +> a require-dev dependency on `composer/composer` to have IDE auto completion on Composer classes. The required version of the `composer-plugin-api` follows the same [rules][7] as a normal package's. -The current composer plugin API version is 1.1.0. +The current Composer plugin API version is `2.1.0`. An example of a valid plugin `composer.json` file (with the autoloading -part omitted): +part omitted and an optional require-dev dependency on `composer/composer` for IDE auto completion): ```json { "name": "my/plugin-package", "type": "composer-plugin", "require": { - "composer-plugin-api": "^1.1" + "composer-plugin-api": "^2.0" + }, + "require-dev": { + "composer/composer": "^2.0" }, "extra": { "class": "My\\Plugin" @@ -94,6 +102,10 @@ and have it return an array. The array key must be the [event name](https://getcomposer.org/doc/articles/scripts.md#event-names) and the value is the name of the method in this class to be called. +> **Note:** If you don't know which event to listen to, you can run a Composer +> command with the COMPOSER_DEBUG_EVENTS=1 environment variable set, which might +> help you identify what event you are looking for. + ```php public static function getSubscribedEvents() { @@ -161,6 +173,14 @@ class AwsPlugin implements PluginInterface, EventSubscriberInterface $this->io = $io; } + public function deactivate(Composer $composer, IOInterface $io) + { + } + + public function uninstall(Composer $composer, IOInterface $io) + { + } + public static function getSubscribedEvents() { return array( @@ -175,9 +195,7 @@ class AwsPlugin implements PluginInterface, EventSubscriberInterface $protocol = parse_url($event->getProcessedUrl(), PHP_URL_SCHEME); if ($protocol === 's3') { - $awsClient = new AwsClient($this->io, $this->composer->getConfig()); - $s3RemoteFilesystem = new S3RemoteFilesystem($this->io, $event->getRemoteFilesystem()->getOptions(), $awsClient); - $event->setRemoteFilesystem($s3RemoteFilesystem); + // ... } } } @@ -223,7 +241,7 @@ class Plugin implements PluginInterface, Capable ### Command provider The [`Composer\Plugin\Capability\CommandProvider`][9] capability allows to register -additional commands for Composer : +additional commands for Composer: ```php You may pass the `--no-plugins` option to composer commands to disable all +> You may pass the `--no-plugins` option to Composer commands to disable all > installed plugins. This may be particularly helpful if any of the plugins > causes errors and you wish to update or uninstall it. +## Plugin Helpers + +As of Composer 2, due to the fact that DownloaderInterface can sometimes return Promises +and have been split up in more steps than they used to, we provide a [SyncHelper][11] +to make downloading and installing packages easier. + +## Plugin Extra Attributes + +A few special plugin capabilities can be unlocked using extra attributes in the plugin's composer.json. + +### class + +[See above](#plugin-package) for an explanation of the class attribute and how it works. + +### plugin-modifies-downloads + +Some special plugins need to update package download URLs before they get downloaded. + +As of Composer 2.0, all packages are downloaded before they get installed. This means +on the first installation, your plugin is not yet installed when the download occurs, +and it does not get a chance to update the URLs on time. + +Specifying `{"extra": {"plugin-modifies-downloads": true}}` in your composer.json will +hint to Composer that the plugin should be installed on its own before proceeding with +the rest of the package downloads. This slightly slows down the overall installation +process however, so do not use it in plugins which do not absolutely require it. + [1]: ../04-schema.md#type [2]: ../04-schema.md#extra [3]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/PluginInterface.php @@ -288,3 +333,4 @@ local project plugins are loaded. [8]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/Capable.php [9]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/Capability/CommandProvider.php [10]: https://symfony.com/doc/current/components/console.html +[11]: https://github.com/composer/composer/blob/master/src/Composer/Util/SyncHelper.php diff --git a/app/vendor/composer/composer/doc/articles/repository-priorities.md b/app/vendor/composer/composer/doc/articles/repository-priorities.md new file mode 100644 index 000000000..e0053a80b --- /dev/null +++ b/app/vendor/composer/composer/doc/articles/repository-priorities.md @@ -0,0 +1,95 @@ + + +# Repository priorities + +## Canonical repositories + +When Composer resolves dependencies, it will look up a given package in the +topmost repository. If that repository does not contain the package, it +goes on to the next one, until one repository contains it and the process ends. + +Canonical repositories are better for a few reasons: + +- Performance wise, it is more efficient to stop looking for a package once it +has been found somewhere. It also avoids loading duplicate packages in case +the same package is present in several of your repositories. +- Security wise, it is safer to treat them canonically as it means that packages you +expect to come from your most important repositories will never be loaded from +another repository instead. Let's +say you have a private repository which is not canonical, and you require your +private package `foo/bar ^2.0` for example. Now if someone publishes +`foo/bar 2.999` to packagist.org, suddenly Composer will pick that package as it +has a higher version than your latest release (say 2.4.3), and you end up installing +something you may not have meant to. If the private repository is canonical +however, that 2.999 version from packagist.org will not be considered at all. + +There are however a few cases where you may want to specifically load some packages +from a given repository, but not all. Or you may want a given repository to not be +canonical, and to be only preferred if it has higher package versions than the +repositories defined below. + +## Default behavior + +By default in Composer 2.x all repositories are canonical. Composer 1.x treated +all repositories as non-canonical. + +Another default is that the packagist.org repository is always added implicitly +as the last repository, unless you [disable it](../05-repositories.md#disabling-packagist-org). + +## Making repositories non-canonical + +You can add the canonical option to any repository to disable this default behavior +and make sure Composer keeps looking in other repositories, even if that repository +contains a given package. + +```json +{ + "repositories": [ + { + "type": "composer", + "url": "https://example.org", + "canonical": false + } + ] +} +``` + +## Filtering packages + +You can also filter packages which a repository will be able to load, either by +selecting which ones you want, or by excluding those you do not want. + +For example here we want to pick only the package `foo/bar` and all the packages from +`some-vendor/` from this composer repository. + +```json +{ + "repositories": [ + { + "type": "composer", + "url": "https://example.org", + "only": ["foo/bar", "some-vendor/*"] + } + ] +} +``` + +And in this other example we exclude `toy/package` from a repository, which +we may not want to load in this project. + +```json +{ + "repositories": [ + { + "type": "composer", + "url": "https://example.org", + "exclude": ["toy/package"] + } + ] +} +``` + +Both `only` and `exclude` should be arrays of package names, which can also +contain wildcards (`*`) which will match any characters. diff --git a/app/vendor/composer/composer/doc/articles/resolving-merge-conflicts.md b/app/vendor/composer/composer/doc/articles/resolving-merge-conflicts.md new file mode 100644 index 000000000..2ee30a6d1 --- /dev/null +++ b/app/vendor/composer/composer/doc/articles/resolving-merge-conflicts.md @@ -0,0 +1,62 @@ + + +# Resolving merge conflicts + +When working as a team on the same Composer project, you will eventually run into a scenario +where multiple people added, updated or removed something in the `composer.json` and +`composer.lock` files in multiple branches. When those branches are eventually merged +together, you will get merge conflicts. Resolving these merge conflicts is not as straight +forward as on other files, especially not regarding the `composer.lock` file. + +> **Note:** It might not immediately be obvious why text based merging is not possible for +> lock files, so let's imagine the following example where we want to merge two branches; +> +> - Branch 1 has added package A which requires package B. Package B is locked at version `1.0.0`. +> - Branch 2 has added package C which conflicts with all versions below `1.2.0` of package B. +> +> A text based merge would result in package A version `1.0.0`, package B version `1.0.0` +> and package C version `1.0.0`. This is an invalid result, as the conflict of package C +> was not considered and would require an upgrade of package B. + +## 1. Reapplying changes + +The safest method to merge Composer files is to accept the version from one branch and apply +the changes from the other branch. + +An example where we have two branches: + +1. Package 'A' has been added +2. Package 'B' has been removed and package 'C' is added. + +To resolve the conflict when we merge these two branches: + +- We choose the branch that has the most changes, and accept the `composer.json` and `composer.lock` + files from that branch. In this case, we choose the composer files from branch 2. +- We reapply the changes from the other branch (branch 1). In this case we have to run + ```composer require package/A``` again. + +## 2. Validating your merged files + +Before committing, make sure the resulting `composer.json` and `composer.lock` files are valid. +To do this, run the following commands: + +```sh +composer validate +composer install [--dry-run] +``` + +## Important considerations + +Keep in mind that whenever merge conflicts occur on the lock file, the information about the exact version +new packages were locked on for one of the branches gets lost. When package A in branch 1 is constrained +as `^1.2.0` and locked as `1.2.0`, it might get updated when branch 2 is used as baseline and a new +`composer require package/A:^1.2.0` is executed, as that will use the most recent version that the +constraint allows when possible. There might be a version 1.3.0 for that package available by now, which +will now be used instead. + +Choosing the correct [version constraints](../articles/versions.md) and making sure the packages adhere +to [semantic versioning](https://semver.org/) when using +[next significant release operators](versions.md#next-significant-release-operators) should make sure +that merging branches does not break anything by accidentally updating a dependency. diff --git a/app/vendor/composer/composer/doc/articles/scripts.md b/app/vendor/composer/composer/doc/articles/scripts.md index 776c56bd0..ae43f26a0 100644 --- a/app/vendor/composer/composer/doc/articles/scripts.md +++ b/app/vendor/composer/composer/doc/articles/scripts.md @@ -29,6 +29,7 @@ Composer fires the following named events during its execution process: the `install` command is executed without a lock file present. - **post-update-cmd**: occurs after the `update` command has been executed, or after the `install` command has been executed without a lock file present. +- **pre-status-cmd**: occurs before the `status` command is executed. - **post-status-cmd**: occurs after the `status` command has been executed. - **pre-archive-cmd**: occurs before the `archive` command is executed. - **post-archive-cmd**: occurs after the `archive` command has been executed. @@ -37,14 +38,15 @@ Composer fires the following named events during its execution process: - **post-autoload-dump**: occurs after the autoloader has been dumped, either during `install`/`update`, or via the `dump-autoload` command. - **post-root-package-install**: occurs after the root package has been - installed, during the `create-project` command. + installed during the `create-project` command (but before its + dependencies are installed). - **post-create-project-cmd**: occurs after the `create-project` command has been executed. ### Installer Events -- **pre-dependencies-solving**: occurs before the dependencies are resolved. -- **post-dependencies-solving**: occurs after the dependencies have been resolved. +- **pre-operations-exec**: occurs before the install/upgrade/.. operations + are executed when installing a lock file. ### Package Events @@ -61,11 +63,15 @@ Composer fires the following named events during its execution process: - **command**: occurs before any Composer Command is executed on the CLI. It provides you with access to the input and output objects of the program. - **pre-file-download**: occurs before files are downloaded and allows - you to manipulate the `RemoteFilesystem` object prior to downloading files + you to manipulate the `HttpDownloader` object prior to downloading files based on the URL to be downloaded. +- **post-file-download**: occurs after package dist files are downloaded and + allows you to perform additional checks on the file if required. - **pre-command-run**: occurs before a command is executed and allows you to manipulate the `InputInterface` object's options and arguments to tweak a command's behavior. +- **pre-pool-create**: occurs before the Pool of packages is created, and lets + you filter the list of packages which is going to enter the Solver. > **Note:** Composer makes no assumptions about the state of your dependencies > prior to `install` or `update`. Therefore, you should not specify scripts @@ -156,10 +162,11 @@ class MyClass } ``` -**Note:** During a composer install or update process, a variable named +**Note:** During a Composer `install` or `update` command run, a variable named `COMPOSER_DEV_MODE` will be added to the environment. If the command was run with the `--no-dev` flag, this variable will be set to 0, otherwise it will be -set to 1. +set to 1. The variable is also available while `dump-autoload` runs, and it +will be set to same as the last `install` or `update` was run in. ## Event classes @@ -171,14 +178,15 @@ Depending on the [script types](#event-names) you will get various event subclasses containing various getters with relevant data and associated objects: -- Base class: [`Composer\EventDispatcher\Event`](https://getcomposer.org/apidoc/master/Composer/EventDispatcher/Event.html) -- Command Events: [`Composer\Script\Event`](https://getcomposer.org/apidoc/master/Composer/Script/Event.html) -- Installer Events: [`Composer\Installer\InstallerEvent`](https://getcomposer.org/apidoc/master/Composer/Installer/InstallerEvent.html) -- Package Events: [`Composer\Installer\PackageEvent`](https://getcomposer.org/apidoc/master/Composer/Installer/PackageEvent.html) +- Base class: [`Composer\EventDispatcher\Event`](https://github.com/composer/composer/blob/master/src/Composer/EventDispatcher/Event.php) +- Command Events: [`Composer\Script\Event`](https://github.com/composer/composer/blob/master/src/Composer/Script/Event.php) +- Installer Events: [`Composer\Installer\InstallerEvent`](https://github.com/composer/composer/blob/master/src/Composer/Installer/InstallerEvent.php) +- Package Events: [`Composer\Installer\PackageEvent`](https://github.com/composer/composer/blob/master/src/Composer/Installer/PackageEvent.php) - Plugin Events: - - init: [`Composer\EventDispatcher\Event`](https://getcomposer.org/apidoc/master/Composer/EventDispatcher/Event.html) - - command: [`Composer\Plugin\CommandEvent`](https://getcomposer.org/apidoc/master/Composer/Plugin/CommandEvent.html) - - pre-file-download: [`Composer\Plugin\PreFileDownloadEvent`](https://getcomposer.org/apidoc/master/Composer/Plugin/PreFileDownloadEvent.html) + - init: [`Composer\EventDispatcher\Event`](https://github.com/composer/composer/blob/master/src/Composer/EventDispatcher/Event.php) + - command: [`Composer\Plugin\CommandEvent`](https://github.com/composer/composer/blob/master/src/Composer/Plugin/CommandEvent.php) + - pre-file-download: [`Composer\Plugin\PreFileDownloadEvent`](https://github.com/composer/composer/blob/master/src/Composer/Plugin/PreFileDownloadEvent.php) + - post-file-download: [`Composer\Plugin\PostFileDownloadEvent`](https://github.com/composer/composer/blob/master/src/Composer/Plugin/PostFileDownloadEvent.php) ## Running scripts manually @@ -369,4 +377,7 @@ You can set custom script descriptions with the following in your `composer.json } ``` +The descriptions are used in `composer list` or `composer run -l` commands to +describe what the scripts do when the command is run. + > **Note:** You can only set custom descriptions of custom commands. diff --git a/app/vendor/composer/composer/doc/articles/troubleshooting.md b/app/vendor/composer/composer/doc/articles/troubleshooting.md index 33471cd53..669d4ce55 100644 --- a/app/vendor/composer/composer/doc/articles/troubleshooting.md +++ b/app/vendor/composer/composer/doc/articles/troubleshooting.md @@ -7,22 +7,22 @@ This is a list of common pitfalls on using Composer, and how to avoid them. ## General -1. Before asking anyone, run [`composer diagnose`](../03-cli.md#diagnose) to check - for common problems. If it all checks out, proceed to the next steps. - -2. When facing any kind of problems using Composer, be sure to **work with the +1. When facing any kind of problems using Composer, be sure to **work with the latest version**. See [self-update](../03-cli.md#self-update) for details. + +2. Before asking anyone, run [`composer diagnose`](../03-cli.md#diagnose) to check + for common problems. If it all checks out, proceed to the next steps. 3. Make sure you have no problems with your setup by running the installer's checks via `curl -sS https://getcomposer.org/installer | php -- --check`. -4. Ensure you're **installing vendors straight from your `composer.json`** via +4. Try clearing Composer's cache by running `composer clear-cache`. + +5. Ensure you're **installing vendors straight from your `composer.json`** via `rm -rf vendor && composer update -v` when troubleshooting, excluding any possible interferences with existing vendor installations or `composer.lock` entries. -5. Try clearing Composer's cache by running `composer clear-cache`. - ## Package not found 1. Double-check you **don't have typos** in your `composer.json` or repository @@ -47,32 +47,39 @@ This is a list of common pitfalls on using Composer, and how to avoid them. In this case add the `--with-dependencies` argument **or** add all dependencies which need an update to the command. -## Package not found on travis-ci.org +## Dependencies on the root package -1. Check the ["Package not found"](#package-not-found) item above. +When your root package depends on a package which ends up depending (directly or +indirectly) back on the root package itself, issues can occur in two cases: + +1. During development, if you are on a branch like `dev-main` and the branch has no + [branch-alias](aliases.md#branch-alias) defined, and the dependency on the root package + requires version `^2.0` for example, the `dev-main` version will not satisfy it. + The best solution here is to make sure you first define a branch alias. -2. If the package tested is a dependency of one of its dependencies (cyclic - dependency), the problem might be that Composer is not able to detect the version - of the package properly. If it is a git clone it is generally alright and Composer - will detect the version of the current branch, but travis does shallow clones so - that process can fail when testing pull requests and feature branches in general. +2. In CI (Continuous Integration) runs, the problem might be that Composer is not able + to detect the version of the root package properly. If it is a git clone it is + generally alright and Composer will detect the version of the current branch, + but some CIs do shallow clones so that process can fail when testing pull requests + and feature branches. In these cases the branch alias may then not be recognized. The best solution is to define the version you are on via an environment variable - called COMPOSER_ROOT_VERSION. You set it to `dev-master` for example to define - the root package's version as `dev-master`. - Use: `before_script: COMPOSER_ROOT_VERSION=dev-master composer install` to export - the variable for the call to composer. + called COMPOSER_ROOT_VERSION. You set it to `dev-main` for example to define + the root package's version as `dev-main`. + Use for example: `COMPOSER_ROOT_VERSION=dev-main composer install` to export + the variable only for the call to composer, or you can define it globally in the + CI env vars. ## Package not found in a Jenkins-build 1. Check the ["Package not found"](#package-not-found) item above. -2. Reason for failing is similar to the problem which can occur on travis-ci.org: The - git-clone / checkout within Jenkins leaves the branch in a "detached HEAD"-state. As - a result, Composer is not able to identify the version of the current checked out branch - and may not be able to resolve a cyclic dependency. To solve this problem, you can use - the "Additional Behaviours" -> "Check out to specific local branch" in your Git-settings - for your Jenkins-job, where your "local branch" shall be the same branch as you are - checking out. Using this, the checkout will not be in detached state any more and cyclic - dependency is recognized correctly. + +2. The git-clone / checkout within Jenkins leaves the branch in a "detached HEAD"-state. As + a result, Composer may not able to identify the version of the current checked out branch + and may not be able to resolve a [dependency on the root package](#dependencies-on-the-root-package). + To solve this problem, you can use the "Additional Behaviours" -> "Check out to specific local + branch" in your Git-settings for your Jenkins-job, where your "local branch" shall be the same + branch as you are checking out. Using this, the checkout will not be in detached state any more + and the dependency on the root package should become satisfied. ## I have a dependency which contains a "repositories" definition in its composer.json, but it seems to be ignored. @@ -177,12 +184,7 @@ Because of GitHub's rate limits on their API it can happen that Composer prompts for authentication asking your username and password so it can go ahead with its work. If you would prefer not to provide your GitHub credentials to Composer you can -manually create a token using the following procedure: - -1. [Create](https://github.com/settings/tokens) an OAuth token on GitHub. -[Read more](https://github.com/blog/1509-personal-api-tokens) on this. - -2. Add it to the configuration running `composer config -g github-oauth.github.com ` +manually create a token using the [procedure documented here](authentication-for-private-packages.md#github-oauth). Now Composer should install/update without asking for authentication. @@ -207,6 +209,7 @@ To enable the swap you can use for example: ```sh /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024 /sbin/mkswap /var/swap.1 +/bin/chmod 0600 /var/swap.1 /sbin/swapon /var/swap.1 ``` You can make a permanent swap file following this [tutorial](https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04). diff --git a/app/vendor/composer/composer/doc/articles/versions.md b/app/vendor/composer/composer/doc/articles/versions.md index 8a46be8b3..47f1f8910 100644 --- a/app/vendor/composer/composer/doc/articles/versions.md +++ b/app/vendor/composer/composer/doc/articles/versions.md @@ -80,6 +80,19 @@ In the above example, if you wanted to check out the `my-feature` branch, you wo When branch names look like versions, we have to clarify for composer that we're trying to check out a branch and not a tag. In the above example, we have two version branches: `v1` and `v2`. To get Composer to check out one of these branches, you must specify a version constraint that looks like this: `v1.x-dev`. The `.x` is an arbitrary string that Composer requires to tell it that we're talking about the `v1` branch and not a `v1` tag (alternatively, you can name the branch `v1.x` instead of `v1`). In the case of a branch with a version-like name (`v1`, in this case), you append `-dev` as a suffix, rather than using `dev-` as a prefix. +### Stabilities + +Composer recognizes the following stabilities (in order of stability): dev, +alpha, beta, RC, and stable where RC stands for release candidate. The stability +of a version is defined by its suffix e.g version `v1.1-BETA` has a stability of +`beta` and `v1.1-RC1` has a stability of `RC`. If such a suffix is missing +e.g. version `v1.1` then Composer considers that version `stable`. In addition +to that Composer automatically adds a `-dev` suffix to all numeric branches and +prefixes all other branches imported from a VCS repository with `dev-`. In both +cases the stability `dev` gets assigned. + +Keeping this in mind will help you in the next section. + ### Minimum Stability There's one more thing that will affect which files are checked out of a library's VCS and added to your project: Composer allows you to specify stability constraints to limit which tags are considered valid. In the above example, note that the library released a beta and two release candidates for version `1.1` before the final official release. To receive these versions when running `composer install` or `composer update`, we have to explicitly tell Composer that we are ok with release candidates and beta releases (and alpha releases, if we want those). This can be done using either a project-wide `minimum-stability` value in `composer.json` or using "stability flags" in version constraints. Read more on the [schema page](../04-schema.md#minimum-stability). @@ -111,13 +124,18 @@ will be treated as a **logical OR**. AND has higher precedence than OR. > unexpectedly installing versions that break backwards compatibility. > Consider using the [caret](#caret-version-range-) operator instead for safety. + +> **Note:** In older versions of Composer the single pipe (`|`) was the +> recommended alternative to the **logical OR**. Thus for backwards compatibility +> the single pipe (`|`) will still be treated as a **logical OR**. + Examples: * `>=1.0` * `>=1.0 <2.0` * `>=1.0 <1.1 || >=1.2` -### Hyphenated Version Range ( - ) +### Hyphenated Version Range (` - `) Inclusive set of versions. Partial versions on the right include are completed with a wildcard. For example `1.0 - 2.0` is equivalent to `>=1.0.0 <2.1` as the @@ -126,7 +144,7 @@ with a wildcard. For example `1.0 - 2.0` is equivalent to `>=1.0.0 <2.1` as the Example: `1.0 - 2.0` -### Wildcard Version Range (.*) +### Wildcard Version Range (`.*`) You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of `>=1.0 <1.1`. @@ -135,7 +153,7 @@ Example: `1.0.*` ## Next Significant Release Operators -### Tilde Version Range (~) +### Tilde Version Range (`~`) The `~` operator is best explained by example: `~1.2` is equivalent to `>=1.2 <2.0.0`, while `~1.2.3` is equivalent to `>=1.2.3 <1.3.0`. As you can see @@ -157,9 +175,9 @@ Example: `~1.2` > it will not allow the major number to increase trying to keep backwards > compatibility. -### Caret Version Range (^) +### Caret Version Range (`^`) -The `^` operator behaves very similarly but it sticks closer to semantic +The `^` operator behaves very similarly, but it sticks closer to semantic versioning, and will always allow non-breaking updates. For example `^1.2.3` is equivalent to `>=1.2.3 <2.0.0` as none of the releases until 2.0 should break backwards compatibility. For pre-1.0 versions it also acts with safety @@ -201,7 +219,7 @@ setting. All available stability flags are listed on the minimum-stability section of the [schema page](../04-schema.md#minimum-stability). ## Summary -```json +```jsonc "require": { "vendor/package": "1.3.2", // exactly 1.3.2 diff --git a/app/vendor/composer/composer/doc/faqs/how-do-i-install-a-package-to-a-custom-path-for-my-framework.md b/app/vendor/composer/composer/doc/faqs/how-do-i-install-a-package-to-a-custom-path-for-my-framework.md index 4f95aecc7..986fd5ec8 100644 --- a/app/vendor/composer/composer/doc/faqs/how-do-i-install-a-package-to-a-custom-path-for-my-framework.md +++ b/app/vendor/composer/composer/doc/faqs/how-do-i-install-a-package-to-a-custom-path-for-my-framework.md @@ -7,7 +7,7 @@ the default `vendor` folder by using If you are a **package author** and want your package installed to a custom directory, require `composer/installers` and set the appropriate `type`. -Specifying the package type, will override the default installer path. +Specifying the package type, will override the default installer path. This is common if your package is intended for a specific framework such as CakePHP, Drupal or WordPress. Here is an example composer.json file for a WordPress theme: @@ -46,7 +46,7 @@ for a module that uses composer/installers, as well as putting all packages of t ``` Now the package would be installed to your folder location, rather than the default -composer/installers determined location. In addition, `installer-paths` is +composer/installers determined location. In addition, `installer-paths` is order-dependent, which means moving a package by name should come before the installer path of a `type:*` that matches the same package. diff --git a/app/vendor/composer/composer/doc/faqs/how-to-install-composer-programmatically.md b/app/vendor/composer/composer/doc/faqs/how-to-install-composer-programmatically.md index 3b378a5ab..6299b6d40 100644 --- a/app/vendor/composer/composer/doc/faqs/how-to-install-composer-programmatically.md +++ b/app/vendor/composer/composer/doc/faqs/how-to-install-composer-programmatically.md @@ -9,7 +9,7 @@ An alternative is to use this script which only works with UNIX utilities: ```bash #!/bin/sh -EXPECTED_CHECKSUM="$(wget -q -O - https://composer.github.io/installer.sig)" +EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')" php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" diff --git a/app/vendor/composer/composer/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md b/app/vendor/composer/composer/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md index 14da5f5e3..67d4133d4 100644 --- a/app/vendor/composer/composer/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md +++ b/app/vendor/composer/composer/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md @@ -25,7 +25,7 @@ If you really feel like you must do this, you have a few options: 3. Remove the `.git` directory of every dependency after the installation, then you can add them to your git repo. You can do that with `rm -rf vendor/**/.git` in ZSH or `find vendor/ -type d -name ".git" -exec rm -rf {} \;` in Bash. - but this means you will have to delete those dependencies from disk before + But this means you will have to delete those dependencies from disk before running composer update. 4. Add a .gitignore rule (`/vendor/**/.git`) to ignore all the vendor `.git` folders. This approach does not require that you delete dependencies from disk prior to diff --git a/app/vendor/composer/composer/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md b/app/vendor/composer/composer/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md index 705f823f9..2039fa81b 100644 --- a/app/vendor/composer/composer/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md +++ b/app/vendor/composer/composer/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md @@ -5,7 +5,7 @@ A version constraint without an upper bound such as `*`, `>=3.4` or This includes major versions breaking backward compatibility. Once a release of your package is tagged, you cannot tweak its dependencies -anymore in case a dependency breaks BC - you have to do a new release but the +anymore in case a dependency breaks BC - you have to do a new release, but the previous one stays broken. The only good alternative is to define an upper bound on your constraints, @@ -16,6 +16,6 @@ For example instead of using `>=3.4` you should use `~3.4` which allows all versions up to `3.999` but does not include `4.0` and above. The `^` operator works very well with libraries following [semantic versioning](https://semver.org). -**Note:** As a package maintainer, you can make the help your users +**Note:** As a package maintainer, you can help your users by providing an [alias version](../articles/aliases.md) for your development branch to allow it to match bound constraints. diff --git a/app/vendor/composer/composer/doc/faqs/why-can't-composer-load-repositories-recursively.md b/app/vendor/composer/composer/doc/faqs/why-can't-composer-load-repositories-recursively.md index a39aff6fb..1dff52c40 100644 --- a/app/vendor/composer/composer/doc/faqs/why-can't-composer-load-repositories-recursively.md +++ b/app/vendor/composer/composer/doc/faqs/why-can't-composer-load-repositories-recursively.md @@ -24,7 +24,7 @@ their package's packages, etc, then resolve requirements. It could work, but it slows down the initialization a lot since VCS repos can each take a few seconds, and it could end up in a completely broken state since many versions of a package could define the same packages inside a package repository, but with different -dist/source. There are many many ways this could go wrong. +dist/source. There are many ways this could go wrong. - Fetch the repositories of root package, then fetch the repositories of the first level dependencies, then fetch the repositories of their dependencies, etc, diff --git a/app/vendor/composer/composer/doc/fixtures/fixtures.md b/app/vendor/composer/composer/doc/fixtures/fixtures.md index a245f8992..051d5aad3 100644 --- a/app/vendor/composer/composer/doc/fixtures/fixtures.md +++ b/app/vendor/composer/composer/doc/fixtures/fixtures.md @@ -12,11 +12,10 @@ also be used as (initial) fixtures for tests. All these repositories contain the following packages. -* `foo/bar` versions 1.0.0, 1.0.1 and 1.1.0; dev-default and 1.0.x-dev branches. - On dev-default and in 1.1.0, `bar/baz` ~1.0 is required. -* `qux/quux` only has a dev-default branch. It `replace`s `gar/nix`. -* `gar/nix` has a 1.0.0 version and a dev-default branch. It is being replaced +* `foo/bar` versions `1.0.0`, `1.0.1` and `1.1.0`; `dev-default` and `1.0.x-dev` branches. + On `dev-default` and in `1.1.0`, `bar/baz` `~1.0` is required. +* `qux/quux` only has a `dev-default` branch. It `replace`s `gar/nix`. +* `gar/nix` has a `1.0.0` version and a `dev-default` branch. It is being replaced by `qux/quux`. -* `bar/baz` has a 1.0.0 version and 1.0.x-dev as well as dev-default branches. - Additionally, 1.1.x-dev is a branch alias for dev-default. - +* `bar/baz` has a `1.0.0` version and `1.0.x-dev` as well as `dev-default` branches. + Additionally, `1.1.x-dev` is a branch alias for `dev-default`. diff --git a/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/bar/baz$923363b3c22e73abb2e3fd891c8156dd4d0821a97fd3e428bc910833e3e46dbe.json b/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/bar/baz$923363b3c22e73abb2e3fd891c8156dd4d0821a97fd3e428bc910833e3e46dbe.json index 94a43b019..77739fece 100644 --- a/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/bar/baz$923363b3c22e73abb2e3fd891c8156dd4d0821a97fd3e428bc910833e3e46dbe.json +++ b/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/bar/baz$923363b3c22e73abb2e3fd891c8156dd4d0821a97fd3e428bc910833e3e46dbe.json @@ -47,4 +47,4 @@ } } } -} \ No newline at end of file +} diff --git a/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/foo/bar$4baabb3303afa3e34a4d3af18fb138e5f3b79029c1f8d9ab5b477ea15776ba0a.json b/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/foo/bar$4baabb3303afa3e34a4d3af18fb138e5f3b79029c1f8d9ab5b477ea15776ba0a.json index 7dc7cc91e..378b04003 100644 --- a/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/foo/bar$4baabb3303afa3e34a4d3af18fb138e5f3b79029c1f8d9ab5b477ea15776ba0a.json +++ b/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/foo/bar$4baabb3303afa3e34a4d3af18fb138e5f3b79029c1f8d9ab5b477ea15776ba0a.json @@ -74,4 +74,4 @@ } } } -} \ No newline at end of file +} diff --git a/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/gar/nix$5d210670cb46c8364c8e3fb449967b9bea558b971e5b082f330ae4f1d484c321.json b/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/gar/nix$5d210670cb46c8364c8e3fb449967b9bea558b971e5b082f330ae4f1d484c321.json index 512b8d882..68e399351 100644 --- a/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/gar/nix$5d210670cb46c8364c8e3fb449967b9bea558b971e5b082f330ae4f1d484c321.json +++ b/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/gar/nix$5d210670cb46c8364c8e3fb449967b9bea558b971e5b082f330ae4f1d484c321.json @@ -47,4 +47,4 @@ } } } -} \ No newline at end of file +} diff --git a/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/provider-active$1893a061e579543822389ecd12d791c612db0c05e22d90e9286e233cacd86ed8.json b/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/provider-active$1893a061e579543822389ecd12d791c612db0c05e22d90e9286e233cacd86ed8.json index b82eb418e..6c45294f8 100644 --- a/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/provider-active$1893a061e579543822389ecd12d791c612db0c05e22d90e9286e233cacd86ed8.json +++ b/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/provider-active$1893a061e579543822389ecd12d791c612db0c05e22d90e9286e233cacd86ed8.json @@ -13,4 +13,4 @@ "sha256": "c142d1a07ca354be46b613f59f1d601923a5a00ccc5fcce50a77ecdd461eb72d" } } -} \ No newline at end of file +} diff --git a/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/qux/quux$c142d1a07ca354be46b613f59f1d601923a5a00ccc5fcce50a77ecdd461eb72d.json b/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/qux/quux$c142d1a07ca354be46b613f59f1d601923a5a00ccc5fcce50a77ecdd461eb72d.json index 014187206..dc1b84dce 100644 --- a/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/qux/quux$c142d1a07ca354be46b613f59f1d601923a5a00ccc5fcce50a77ecdd461eb72d.json +++ b/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/p/qux/quux$c142d1a07ca354be46b613f59f1d601923a5a00ccc5fcce50a77ecdd461eb72d.json @@ -19,4 +19,4 @@ } } } -} \ No newline at end of file +} diff --git a/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/packages.json b/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/packages.json index 65968a861..35fd6e30b 100644 --- a/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/packages.json +++ b/app/vendor/composer/composer/doc/fixtures/repo-composer-with-providers/packages.json @@ -6,4 +6,4 @@ "sha256": "1893a061e579543822389ecd12d791c612db0c05e22d90e9286e233cacd86ed8" } } -} \ No newline at end of file +} diff --git a/app/vendor/composer/composer/res/composer-repository-schema.json b/app/vendor/composer/composer/res/composer-repository-schema.json index 914d8e22a..adcc299d6 100644 --- a/app/vendor/composer/composer/res/composer-repository-schema.json +++ b/app/vendor/composer/composer/res/composer-repository-schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "https://json-schema.org/draft-04/schema#", "description": "A representation of packages metadata.", "type": "object", "oneOf": [ diff --git a/app/vendor/composer/composer/res/composer-schema.json b/app/vendor/composer/composer/res/composer-schema.json index 84fb8c6c7..bb86996e7 100644 --- a/app/vendor/composer/composer/res/composer-schema.json +++ b/app/vendor/composer/composer/res/composer-schema.json @@ -1,13 +1,12 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", - "name": "Package", + "$schema": "https://json-schema.org/draft-04/schema#", + "title": "Package", "type": "object", - "additionalProperties": false, - "required": [ "name", "description" ], "properties": { "name": { "type": "string", - "description": "Package name, including 'vendor-name/' prefix." + "description": "Package name, including 'vendor-name/' prefix.", + "pattern": "^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]?|-{0,2})[a-z0-9]+)*$" }, "type": { "description": "Package type, either 'library' for common packages, 'composer-plugin' for plugins, 'metapackage' for empty packages, or a custom type ([a-z0-9-]+) defined by whatever project this package applies to.", @@ -41,7 +40,7 @@ "version": { "type": "string", "description": "Package version, see https://getcomposer.org/doc/04-schema.md#version for more info on valid schemes.", - "pattern": "^v?\\d+(((\\.\\d+)?\\.\\d+)?\\.\\d+)?|^dev-" + "pattern": "^v?\\d+(\\.\\d+){0,3}|^dev-" }, "time": { "type": "string", @@ -144,6 +143,10 @@ "type": "string" } }, + "gitlab-protocol": { + "enum": ["git", "http", "https"], + "description": "A protocol to force use of when creating a repository URL for the `source` value of the package metadata. One of `git` or `http`. By default, Composer will generate a git URL for private repositories and http one for public repos." + }, "bearer": { "type": "object", "description": "A hash of domain name => bearer authentication token, for example {\"example.com\":\"\"}.", @@ -159,6 +162,13 @@ "type": "boolean", "description": "Defaults to `true`. If set to true only HTTPS URLs are allowed to be downloaded via Composer. If you really absolutely need HTTP access to something then you can disable it, but using \"Let's Encrypt\" to get a free SSL certificate is generally a better alternative." }, + "secure-svn-domains": { + "type": "array", + "description": "A list of domains which should be trusted/marked as using a secure Subversion/SVN transport. By default svn:// protocol is seen as insecure and will throw. This is a better/safer alternative to disabling `secure-http` altogether.", + "items": { + "type": "string" + } + }, "cafile": { "type": "string", "description": "A way to set the path to the openssl CA file. In PHP 5.6+ you should rather set this via openssl.cafile in php.ini, although PHP 5.6+ should be able to detect your system CA file automatically." @@ -236,9 +246,13 @@ "type": ["string", "integer"], "description": "The cache max size for the files cache, defaults to \"300MiB\"." }, + "cache-read-only": { + "type": ["boolean"], + "description": "Whether to use the Composer cache in read-only mode." + }, "bin-compat": { - "enum": ["auto", "full"], - "description": "The compatibility of the binaries, defaults to \"auto\" (automatically guessed) and can be \"full\" (compatible with both Windows and Unix-based systems)." + "enum": ["auto", "full", "symlink"], + "description": "The compatibility of the binaries, defaults to \"auto\" (automatically guessed), can be \"full\" (compatible with both Windows and Unix-based systems) and \"symlink\" (symlink also for WSL)." }, "discard-changes": { "type": ["string", "boolean"], @@ -305,6 +319,10 @@ "lock": { "type": "boolean", "description": "Defaults to true. If set to false, Composer will not create a composer.lock file." + }, + "platform-check": { + "type": ["boolean", "string"], + "description": "Defaults to \"php-only\" which checks only the PHP version. Setting to true will also check the presence of required PHP extensions. If set to false, Composer will not create and require a platform_check.php file as part of the autoloader bootstrap." } } }, @@ -342,7 +360,7 @@ }, "classmap": { "type": "array", - "description": "This is an array of directories that contain classes to be included in the class-map generation process." + "description": "This is an array of paths that contain classes to be included in the class-map generation process." }, "files": { "type": "array", @@ -354,6 +372,10 @@ "type": ["object"], "description": "Options for creating package archives for distribution.", "properties": { + "name": { + "type": "string", + "description": "A base name for archive." + }, "exclude": { "type": "array", "description": "A list of patterns for paths to exclude or include if prefixed with an exclamation mark." @@ -384,7 +406,7 @@ "minimum-stability": { "type": ["string"], "description": "The minimum stability the packages must have to be install-able. Possible values are: dev, alpha, beta, RC, stable.", - "pattern": "^dev|alpha|beta|rc|RC|stable$" + "enum": ["dev", "alpha", "beta", "rc", "RC", "stable"] }, "prefer-stable": { "type": ["boolean"], @@ -556,6 +578,10 @@ "type": "string" } }, + "default-branch": { + "type": ["boolean"], + "description": "Internal use only, do not specify this in composer.json. Indicates whether this version is the default branch of the linked VCS repository. Defaults to false." + }, "abandoned": { "type": ["boolean", "string"], "description": "Indicates whether this package has been abandoned, it can be boolean or a package name/URL pointing to a recommended alternative. Defaults to false." @@ -621,7 +647,7 @@ }, "classmap": { "type": "array", - "description": "This is an array of directories that contain classes to be included in the class-map generation process." + "description": "This is an array of paths that contain classes to be included in the class-map generation process." }, "files": { "type": "array", @@ -650,6 +676,19 @@ "properties": { "type": { "type": "string", "enum": ["composer"] }, "url": { "type": "string" }, + "canonical": { "type": "boolean" }, + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, "options": { "type": "object", "additionalProperties": true @@ -664,6 +703,19 @@ "properties": { "type": { "type": "string", "enum": ["vcs", "github", "git", "gitlab", "git-bitbucket", "hg", "hg-bitbucket", "fossil", "perforce", "svn"] }, "url": { "type": "string" }, + "canonical": { "type": "boolean" }, + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, "no-api": { "type": "boolean" }, "secure-http": { "type": "boolean" }, "svn-cache-credentials": { "type": "boolean" }, @@ -684,6 +736,19 @@ "properties": { "type": { "type": "string", "enum": ["path"] }, "url": { "type": "string" }, + "canonical": { "type": "boolean" }, + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, "options": { "type": "object", "properties": { @@ -698,7 +763,20 @@ "required": ["type", "url"], "properties": { "type": { "type": "string", "enum": ["artifact"] }, - "url": { "type": "string" } + "url": { "type": "string" }, + "canonical": { "type": "boolean" }, + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } } }, "pear-repository": { @@ -707,6 +785,19 @@ "properties": { "type": { "type": "string", "enum": ["pear"] }, "url": { "type": "string" }, + "canonical": { "type": "boolean" }, + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, "vendor-alias": { "type": "string" } } }, @@ -715,6 +806,19 @@ "required": ["type", "package"], "properties": { "type": { "type": "string", "enum": ["package"] }, + "canonical": { "type": "boolean" }, + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, "package": { "oneOf": [ { "$ref": "#/definitions/inline-package" }, diff --git a/app/vendor/composer/composer/src/Composer/Autoload/AutoloadGenerator.php b/app/vendor/composer/composer/src/Composer/Autoload/AutoloadGenerator.php index 88d7046c6..47c888ab7 100644 --- a/app/vendor/composer/composer/src/Composer/Autoload/AutoloadGenerator.php +++ b/app/vendor/composer/composer/src/Composer/Autoload/AutoloadGenerator.php @@ -18,10 +18,16 @@ use Composer\IO\IOInterface; use Composer\Package\AliasPackage; use Composer\Package\PackageInterface; +use Composer\Package\RootPackageInterface; use Composer\Repository\InstalledRepositoryInterface; +use Composer\Repository\PlatformRepository; +use Composer\Semver\Constraint\Bound; +use Composer\Semver\Constraint\MatchAllConstraint; use Composer\Util\Filesystem; +use Composer\Util\Platform; use Composer\Script\ScriptEvents; use Composer\Util\PackageSorter; +use Composer\Json\JsonFile; /** * @author Igor Wiedler @@ -35,14 +41,14 @@ class AutoloadGenerator private $eventDispatcher; /** - * @var IOInterface + * @var ?IOInterface */ private $io; /** - * @var bool + * @var ?bool */ - private $devMode = false; + private $devMode = null; /** * @var bool @@ -54,11 +60,21 @@ class AutoloadGenerator */ private $apcu = false; + /** + * @var string|null + */ + private $apcuPrefix; + /** * @var bool */ private $runScripts = false; + /** + * @var bool|array + */ + private $ignorePlatformReqs = false; + public function __construct(EventDispatcher $eventDispatcher, IOInterface $io = null) { $this->eventDispatcher = $eventDispatcher; @@ -84,11 +100,13 @@ public function setClassMapAuthoritative($classMapAuthoritative) /** * Whether or not generated autoloader considers APCu caching. * - * @param bool $apcu + * @param bool $apcu + * @param string|null $apcuPrefix */ - public function setApcu($apcu) + public function setApcu($apcu, $apcuPrefix = null) { $this->apcu = (bool) $apcu; + $this->apcuPrefix = $apcuPrefix !== null ? (string) $apcuPrefix : $apcuPrefix; } /** @@ -101,13 +119,53 @@ public function setRunScripts($runScripts = true) $this->runScripts = (bool) $runScripts; } - public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsrPackages = false, $suffix = '') + /** + * Sets whether platform requirements should be ignored + * + * If this is set to true, the platform check file will not be generated + * If this is set to false, the platform check file will be generated with all requirements + * If this is set to string[], those packages will be ignored from the platform check file + * + * @param array|bool $ignorePlatformReqs + */ + public function setIgnorePlatformRequirements($ignorePlatformReqs) + { + if (is_array($ignorePlatformReqs)) { + $this->ignorePlatformReqs = array_filter($ignorePlatformReqs, function ($req) { + return PlatformRepository::isPlatformPackage($req); + }); + } else { + $this->ignorePlatformReqs = (bool) $ignorePlatformReqs; + } + } + + public function dump(Config $config, InstalledRepositoryInterface $localRepo, RootPackageInterface $rootPackage, InstallationManager $installationManager, $targetDir, $scanPsrPackages = false, $suffix = '') { if ($this->classMapAuthoritative) { // Force scanPsrPackages when classmap is authoritative $scanPsrPackages = true; } + + // auto-set devMode based on whether dev dependencies are installed or not + if (null === $this->devMode) { + // we assume no-dev mode if no vendor dir is present or it is too old to contain dev information + $this->devMode = false; + + $installedJson = new JsonFile($config->get('vendor-dir').'/composer/installed.json'); + if ($installedJson->exists()) { + $installedJson = $installedJson->read(); + if (isset($installedJson['dev'])) { + $this->devMode = $installedJson['dev']; + } + } + } + if ($this->runScripts) { + // set COMPOSER_DEV_MODE in case not set yet so it is available in the dump-autoload event listeners + if (!isset($_SERVER['COMPOSER_DEV_MODE'])) { + Platform::putEnv('COMPOSER_DEV_MODE', $this->devMode ? '1' : '0'); + } + $this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP, $this->devMode, array(), array( 'optimize' => (bool) $scanPsrPackages, )); @@ -157,8 +215,16 @@ public function dump(Config $config, InstalledRepositoryInterface $localRepo, Pa EOF; // Collect information from all packages. - $packageMap = $this->buildPackageMap($installationManager, $mainPackage, $localRepo->getCanonicalPackages()); - $autoloads = $this->parseAutoloads($packageMap, $mainPackage, $this->devMode === false); + $devPackageNames = $localRepo->getDevPackageNames(); + $packageMap = $this->buildPackageMap($installationManager, $rootPackage, $localRepo->getCanonicalPackages()); + if ($this->devMode) { + // if dev mode is enabled, then we do not filter any dev packages out so disable this entirely + $filteredDevPackages = false; + } else { + // if the list of dev package names is available we use that straight, otherwise pass true which means use legacy algo to figure them out + $filteredDevPackages = $devPackageNames ?: true; + } + $autoloads = $this->parseAutoloads($packageMap, $rootPackage, $filteredDevPackages); // Process the 'psr-0' base directories. foreach ($autoloads['psr-0'] as $namespace => $paths) { @@ -198,9 +264,9 @@ public function dump(Config $config, InstalledRepositoryInterface $localRepo, Pa // add custom psr-0 autoloading if the root package has a target dir $targetDirLoader = null; - $mainAutoload = $mainPackage->getAutoload(); - if ($mainPackage->getTargetDir() && !empty($mainAutoload['psr-0'])) { - $levels = substr_count($filesystem->normalizePath($mainPackage->getTargetDir()), '/') + 1; + $mainAutoload = $rootPackage->getAutoload(); + if ($rootPackage->getTargetDir() && !empty($mainAutoload['psr-0'])) { + $levels = substr_count($filesystem->normalizePath($rootPackage->getTargetDir()), '/') + 1; $prefixes = implode(', ', array_map(function ($prefix) { return var_export($prefix, true); }, array_keys($mainAutoload['psr-0']))); @@ -276,6 +342,7 @@ public static function autoload(\$class) ); } + $classMap['Composer\\InstalledVersions'] = "\$vendorDir . '/composer/InstalledVersions.php',\n"; ksort($classMap); foreach ($classMap as $class => $code) { $classmapFile .= ' '.var_export($class, true).' => '.$code; @@ -283,7 +350,7 @@ public static function autoload(\$class) $classmapFile .= ");\n"; if (!$suffix) { - if (!$config->get('autoloader-suffix') && is_readable($vendorPath.'/autoload.php')) { + if (!$config->get('autoloader-suffix') && Filesystem::isReadable($vendorPath.'/autoload.php')) { $content = file_get_contents($vendorPath.'/autoload.php'); if (preg_match('{ComposerAutoloaderInit([^:\s]+)::}', $content, $match)) { $suffix = $match[1]; @@ -295,27 +362,40 @@ public static function autoload(\$class) } } - $this->filePutContentsIfModified($targetDir.'/autoload_namespaces.php', $namespacesFile); - $this->filePutContentsIfModified($targetDir.'/autoload_psr4.php', $psr4File); - $this->filePutContentsIfModified($targetDir.'/autoload_classmap.php', $classmapFile); + $filesystem->filePutContentsIfModified($targetDir.'/autoload_namespaces.php', $namespacesFile); + $filesystem->filePutContentsIfModified($targetDir.'/autoload_psr4.php', $psr4File); + $filesystem->filePutContentsIfModified($targetDir.'/autoload_classmap.php', $classmapFile); $includePathFilePath = $targetDir.'/include_paths.php'; if ($includePathFileContents = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) { - $this->filePutContentsIfModified($includePathFilePath, $includePathFileContents); + $filesystem->filePutContentsIfModified($includePathFilePath, $includePathFileContents); } elseif (file_exists($includePathFilePath)) { unlink($includePathFilePath); } $includeFilesFilePath = $targetDir.'/autoload_files.php'; if ($includeFilesFileContents = $this->getIncludeFilesFile($autoloads['files'], $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) { - $this->filePutContentsIfModified($includeFilesFilePath, $includeFilesFileContents); + $filesystem->filePutContentsIfModified($includeFilesFilePath, $includeFilesFileContents); } elseif (file_exists($includeFilesFilePath)) { unlink($includeFilesFilePath); } - $this->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion)); - $this->filePutContentsIfModified($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix)); - $this->filePutContentsIfModified($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion)); + $filesystem->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion)); + $checkPlatform = $config->get('platform-check') && $this->ignorePlatformReqs !== true; + $platformCheckContent = null; + if ($checkPlatform) { + $platformCheckContent = $this->getPlatformCheck($packageMap, $this->ignorePlatformReqs ?: array(), $config->get('platform-check'), $devPackageNames); + if (null === $platformCheckContent) { + $checkPlatform = false; + } + } + if ($checkPlatform) { + $filesystem->filePutContentsIfModified($targetDir.'/platform_check.php', $platformCheckContent); + } elseif (file_exists($targetDir.'/platform_check.php')) { + unlink($targetDir.'/platform_check.php'); + } + $filesystem->filePutContentsIfModified($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix)); + $filesystem->filePutContentsIfModified($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion, $checkPlatform)); - $this->safeCopy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php'); - $this->safeCopy(__DIR__.'/../../../LICENSE', $targetDir.'/LICENSE'); + $filesystem->safeCopy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php'); + $filesystem->safeCopy(__DIR__.'/../../../LICENSE', $targetDir.'/LICENSE'); if ($this->runScripts) { $this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, array(), array( @@ -326,16 +406,6 @@ public static function autoload(\$class) return count($classMap); } - private function filePutContentsIfModified($path, $content) - { - $currentContent = @file_get_contents($path); - if (!$currentContent || ($currentContent != $content)) { - return file_put_contents($path, $content); - } - - return 0; - } - private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $excluded, $namespaceFilter, $autoloadType, array $classMap, array &$ambiguousClasses, array &$scannedFiles) { foreach ($this->generateClassMap($dir, $excluded, $namespaceFilter, $autoloadType, true, $scannedFiles) as $class => $path) { @@ -378,10 +448,13 @@ private function generateClassMap($dir, $excluded, $namespaceFilter, $autoloadTy return ClassMapGenerator::createMap($dir, $excluded, $showAmbiguousWarning ? $this->io : null, $namespaceFilter, $autoloadType, $scannedFiles); } - public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages) + /** + * @param RootPackageInterface $rootPackage + */ + public function buildPackageMap(InstallationManager $installationManager, PackageInterface $rootPackage, array $packages) { // build package => install path map - $packageMap = array(array($mainPackage, '')); + $packageMap = array(array($rootPackage, '')); foreach ($packages as $package) { if ($package instanceof AliasPackage) { @@ -423,26 +496,30 @@ protected function validatePackage(PackageInterface $package) /** * Compiles an ordered list of namespace => path mappings * - * @param array $packageMap array of array(package, installDir-relative-to-composer.json) - * @param PackageInterface $mainPackage root package instance - * @param bool $filterOutRequireDevPackages whether to filter out require-dev packages - * @return array array('psr-0' => array('Ns\\Foo' => array('installDir'))) + * @param array $packageMap array of array(package, installDir-relative-to-composer.json) + * @param RootPackageInterface $rootPackage root package instance + * @param bool|string[] $filteredDevPackages If an array, the list of packages that must be removed. If bool, whether to filter out require-dev packages + * @return array array('psr-0' => array('Ns\\Foo' => array('installDir'))) */ - public function parseAutoloads(array $packageMap, PackageInterface $mainPackage, $filterOutRequireDevPackages = false) + public function parseAutoloads(array $packageMap, PackageInterface $rootPackage, $filteredDevPackages = false) { - $mainPackageMap = array_shift($packageMap); - if ($filterOutRequireDevPackages) { - $packageMap = $this->filterPackageMap($packageMap, $mainPackage); + $rootPackageMap = array_shift($packageMap); + if (is_array($filteredDevPackages)) { + $packageMap = array_filter($packageMap, function ($item) use ($filteredDevPackages) { + return !in_array($item[0]->getName(), $filteredDevPackages, true); + }); + } elseif ($filteredDevPackages) { + $packageMap = $this->filterPackageMap($packageMap, $rootPackage); } $sortedPackageMap = $this->sortPackageMap($packageMap); - $sortedPackageMap[] = $mainPackageMap; - array_unshift($packageMap, $mainPackageMap); + $sortedPackageMap[] = $rootPackageMap; + array_unshift($packageMap, $rootPackageMap); - $psr0 = $this->parseAutoloadsType($packageMap, 'psr-0', $mainPackage); - $psr4 = $this->parseAutoloadsType($packageMap, 'psr-4', $mainPackage); - $classmap = $this->parseAutoloadsType(array_reverse($sortedPackageMap), 'classmap', $mainPackage); - $files = $this->parseAutoloadsType($sortedPackageMap, 'files', $mainPackage); - $exclude = $this->parseAutoloadsType($sortedPackageMap, 'exclude-from-classmap', $mainPackage); + $psr0 = $this->parseAutoloadsType($packageMap, 'psr-0', $rootPackage); + $psr4 = $this->parseAutoloadsType($packageMap, 'psr-4', $rootPackage); + $classmap = $this->parseAutoloadsType(array_reverse($sortedPackageMap), 'classmap', $rootPackage); + $files = $this->parseAutoloadsType($sortedPackageMap, 'files', $rootPackage); + $exclude = $this->parseAutoloadsType($sortedPackageMap, 'exclude-from-classmap', $rootPackage); krsort($psr0); krsort($psr4); @@ -462,9 +539,9 @@ public function parseAutoloads(array $packageMap, PackageInterface $mainPackage, * @param array $autoloads see parseAutoloads return value * @return ClassLoader */ - public function createLoader(array $autoloads) + public function createLoader(array $autoloads, $vendorDir = null) { - $loader = new ClassLoader(); + $loader = new ClassLoader($vendorDir); if (isset($autoloads['psr-0'])) { foreach ($autoloads['psr-0'] as $namespace => $path) { @@ -590,7 +667,159 @@ protected function getPathCode(Filesystem $filesystem, $basePath, $vendorPath, $ $baseDir = "'phar://' . " . $baseDir; } - return $baseDir . (($path !== false) ? var_export($path, true) : ""); + return $baseDir . var_export($path, true); + } + + protected function getPlatformCheck(array $packageMap, array $ignorePlatformReqs, $checkPlatform, array $devPackageNames) + { + $lowestPhpVersion = Bound::zero(); + $requiredExtensions = array(); + $extensionProviders = array(); + + foreach ($packageMap as $item) { + $package = $item[0]; + foreach (array_merge($package->getReplaces(), $package->getProvides()) as $link) { + if (preg_match('{^ext-(.+)$}iD', $link->getTarget(), $match)) { + $extensionProviders[$match[1]][] = $link->getConstraint() ?: new MatchAllConstraint(); + } + } + } + + foreach ($packageMap as $item) { + $package = $item[0]; + // skip dev dependencies platform requirements as platform-check really should only be a production safeguard + if (in_array($package->getName(), $devPackageNames, true)) { + continue; + } + + foreach ($package->getRequires() as $link) { + if (in_array($link->getTarget(), $ignorePlatformReqs, true)) { + continue; + } + + if ('php' === $link->getTarget() && ($constraint = $link->getConstraint())) { + if ($constraint->getLowerBound()->compareTo($lowestPhpVersion, '>')) { + $lowestPhpVersion = $constraint->getLowerBound(); + } + } + + if ($checkPlatform === true && preg_match('{^ext-(.+)$}iD', $link->getTarget(), $match)) { + // skip extension checks if they have a valid provider/replacer + if (isset($extensionProviders[$match[1]])) { + foreach ($extensionProviders[$match[1]] as $provided) { + if (!$link->getConstraint() || $provided->matches($link->getConstraint())) { + continue 2; + } + } + } + + if ($match[1] === 'zend-opcache') { + $match[1] = 'zend opcache'; + } + + $extension = var_export($match[1], true); + if ($match[1] === 'pcntl' || $match[1] === 'readline') { + $requiredExtensions[$extension] = "PHP_SAPI !== 'cli' || extension_loaded($extension) || \$missingExtensions[] = $extension;\n"; + } else { + $requiredExtensions[$extension] = "extension_loaded($extension) || \$missingExtensions[] = $extension;\n"; + } + } + } + } + + ksort($requiredExtensions); + + $formatToPhpVersionId = function (Bound $bound) { + if ($bound->isZero()) { + return 0; + } + + if ($bound->isPositiveInfinity()) { + return 99999; + } + + $version = str_replace('-', '.', $bound->getVersion()); + $chunks = array_map('intval', explode('.', $version)); + + return $chunks[0] * 10000 + $chunks[1] * 100 + $chunks[2]; + }; + + $formatToHumanReadable = function (Bound $bound) { + if ($bound->isZero()) { + return 0; + } + + if ($bound->isPositiveInfinity()) { + return 99999; + } + + $version = str_replace('-', '.', $bound->getVersion()); + $chunks = explode('.', $version); + $chunks = array_slice($chunks, 0, 3); + + return implode('.', $chunks); + }; + + $requiredPhp = ''; + $requiredPhpError = ''; + if (!$lowestPhpVersion->isZero()) { + $operator = $lowestPhpVersion->isInclusive() ? '>=' : '>'; + $requiredPhp = 'PHP_VERSION_ID '.$operator.' '.$formatToPhpVersionId($lowestPhpVersion); + $requiredPhpError = '"'.$operator.' '.$formatToHumanReadable($lowestPhpVersion).'"'; + } + + if ($requiredPhp) { + $requiredPhp = <<= $staticPhpVersion && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if (\$useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; + require __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInit$suffix::getInitializer(\$loader)); } else { @@ -704,9 +945,9 @@ public static function getLoader() } if ($this->apcu) { - $apcuPrefix = substr(base64_encode(md5(uniqid('', true), true)), 0, -3); + $apcuPrefix = var_export(($this->apcuPrefix !== null ? $this->apcuPrefix : substr(base64_encode(md5(uniqid('', true), true)), 0, -3)), true); $file .= <<setApcuPrefix('$apcuPrefix'); + \$loader->setApcuPrefix($apcuPrefix); APCU; } @@ -872,7 +1113,7 @@ public static function getInitializer(ClassLoader \$loader) INITIALIZER; } - protected function parseAutoloadsType(array $packageMap, $type, PackageInterface $mainPackage) + protected function parseAutoloadsType(array $packageMap, $type, RootPackageInterface $rootPackage) { $autoloads = array(); @@ -880,7 +1121,7 @@ protected function parseAutoloadsType(array $packageMap, $type, PackageInterface list($package, $installPath) = $item; $autoload = $package->getAutoload(); - if ($this->devMode && $package === $mainPackage) { + if ($this->devMode && $package === $rootPackage) { $autoload = array_merge_recursive($autoload, $package->getDevAutoload()); } @@ -888,15 +1129,15 @@ protected function parseAutoloadsType(array $packageMap, $type, PackageInterface if (!isset($autoload[$type]) || !is_array($autoload[$type])) { continue; } - if (null !== $package->getTargetDir() && $package !== $mainPackage) { + if (null !== $package->getTargetDir() && $package !== $rootPackage) { $installPath = substr($installPath, 0, -strlen('/'.$package->getTargetDir())); } foreach ($autoload[$type] as $namespace => $paths) { foreach ((array) $paths as $path) { - if (($type === 'files' || $type === 'classmap' || $type === 'exclude-from-classmap') && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) { + if (($type === 'files' || $type === 'classmap' || $type === 'exclude-from-classmap') && $package->getTargetDir() && !Filesystem::isReadable($installPath.'/'.$path)) { // remove target-dir from file paths of the root package - if ($package === $mainPackage) { + if ($package === $rootPackage) { $targetDir = str_replace('\\', '[\\\\/]', preg_quote(str_replace(array('/', '\\'), '', $package->getTargetDir()))); $path = ltrim(preg_replace('{^'.$targetDir.'}', '', ltrim($path, '\\/')), '\\/'); } else { @@ -910,8 +1151,7 @@ protected function parseAutoloadsType(array $packageMap, $type, PackageInterface $path = preg_replace('{/+}', '/', preg_quote(trim(strtr($path, '\\', '/'), '/'))); // add support for wildcards * and ** - $path = str_replace('\\*\\*', '.+?', $path); - $path = str_replace('\\*', '[^/]+?', $path); + $path = strtr($path, array('\\*\\*' => '.+?', '\\*' => '[^/]+?')); // add support for up-level relative paths $updir = null; @@ -941,7 +1181,8 @@ function ($matches) use (&$updir) { if ($type === 'files') { $autoloads[$this->getFileIdentifier($package, $path)] = $relativePath; continue; - } elseif ($type === 'classmap') { + } + if ($type === 'classmap') { $autoloads[] = $relativePath; continue; } @@ -962,11 +1203,11 @@ protected function getFileIdentifier(PackageInterface $package, $path) /** * Filters out dev-dependencies * - * @param array $packageMap - * @param PackageInterface $mainPackage + * @param array $packageMap + * @param RootPackageInterface $rootPackage * @return array */ - protected function filterPackageMap(array $packageMap, PackageInterface $mainPackage) + protected function filterPackageMap(array $packageMap, RootPackageInterface $rootPackage) { $packages = array(); $include = array(); @@ -995,7 +1236,7 @@ protected function filterPackageMap(array $packageMap, PackageInterface $mainPac } } }; - $add($mainPackage); + $add($rootPackage); return array_filter( $packageMap, @@ -1043,51 +1284,4 @@ protected function sortPackageMap(array $packageMap) return $sortedPackageMap; } - - /** - * Copy file using stream_copy_to_stream to work around https://bugs.php.net/bug.php?id=6463 - * - * @param string $source - * @param string $target - */ - protected function safeCopy($source, $target) - { - if (!file_exists($target) || !file_exists($source) || !$this->filesAreEqual($source, $target)) { - $source = fopen($source, 'r'); - $target = fopen($target, 'w+'); - - stream_copy_to_stream($source, $target); - fclose($source); - fclose($target); - } - } - - /** - * compare 2 files - * https://stackoverflow.com/questions/3060125/can-i-use-file-get-contents-to-compare-two-files - */ - private function filesAreEqual($a, $b) - { - // Check if filesize is different - if (filesize($a) !== filesize($b)) { - return false; - } - - // Check if content is different - $ah = fopen($a, 'rb'); - $bh = fopen($b, 'rb'); - - $result = true; - while (!feof($ah)) { - if (fread($ah, 8192) != fread($bh, 8192)) { - $result = false; - break; - } - } - - fclose($ah); - fclose($bh); - - return $result; - } } diff --git a/app/vendor/composer/composer/src/Composer/Autoload/ClassLoader.php b/app/vendor/composer/composer/src/Composer/Autoload/ClassLoader.php index 03b9bb9c4..0cd6055d1 100644 --- a/app/vendor/composer/composer/src/Composer/Autoload/ClassLoader.php +++ b/app/vendor/composer/composer/src/Composer/Autoload/ClassLoader.php @@ -37,26 +37,80 @@ * * @author Fabien Potencier * @author Jordi Boggiano - * @see http://www.php-fig.org/psr/psr-0/ - * @see http://www.php-fig.org/psr/psr-4/ + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ */ class ClassLoader { + /** @var ?string */ + private $vendorDir; + // PSR-4 + /** + * @var array[] + * @psalm-var array> + */ private $prefixLengthsPsr4 = array(); + /** + * @var array[] + * @psalm-var array> + */ private $prefixDirsPsr4 = array(); + /** + * @var array[] + * @psalm-var array + */ private $fallbackDirsPsr4 = array(); // PSR-0 + /** + * @var array[] + * @psalm-var array> + */ private $prefixesPsr0 = array(); + /** + * @var array[] + * @psalm-var array + */ private $fallbackDirsPsr0 = array(); + /** @var bool */ private $useIncludePath = false; + + /** + * @var string[] + * @psalm-var array + */ private $classMap = array(); + + /** @var bool */ private $classMapAuthoritative = false; + + /** + * @var bool[] + * @psalm-var array + */ private $missingClasses = array(); + + /** @var ?string */ private $apcuPrefix; + /** + * @var self[] + */ + private static $registeredLoaders = array(); + + /** + * @param ?string $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + } + + /** + * @return string[] + */ public function getPrefixes() { if (!empty($this->prefixesPsr0)) { @@ -66,28 +120,47 @@ public function getPrefixes() return array(); } + /** + * @return array[] + * @psalm-return array> + */ public function getPrefixesPsr4() { return $this->prefixDirsPsr4; } + /** + * @return array[] + * @psalm-return array + */ public function getFallbackDirs() { return $this->fallbackDirsPsr0; } + /** + * @return array[] + * @psalm-return array + */ public function getFallbackDirsPsr4() { return $this->fallbackDirsPsr4; } + /** + * @return string[] Array of classname => path + * @psalm-var array + */ public function getClassMap() { return $this->classMap; } /** - * @param array $classMap Class to filename map + * @param string[] $classMap Class to filename map + * @psalm-param array $classMap + * + * @return void */ public function addClassMap(array $classMap) { @@ -102,9 +175,11 @@ public function addClassMap(array $classMap) * Registers a set of PSR-0 directories for a given prefix, either * appending or prepending to the ones previously set for this prefix. * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 root directories - * @param bool $prepend Whether to prepend the directories + * @param string $prefix The prefix + * @param string[]|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void */ public function add($prefix, $paths, $prepend = false) { @@ -147,11 +222,13 @@ public function add($prefix, $paths, $prepend = false) * Registers a set of PSR-4 directories for a given namespace, either * appending or prepending to the ones previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories - * @param bool $prepend Whether to prepend the directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param string[]|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories * * @throws \InvalidArgumentException + * + * @return void */ public function addPsr4($prefix, $paths, $prepend = false) { @@ -195,8 +272,10 @@ public function addPsr4($prefix, $paths, $prepend = false) * Registers a set of PSR-0 directories for a given prefix, * replacing any others previously set for this prefix. * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 base directories + * @param string $prefix The prefix + * @param string[]|string $paths The PSR-0 base directories + * + * @return void */ public function set($prefix, $paths) { @@ -211,10 +290,12 @@ public function set($prefix, $paths) * Registers a set of PSR-4 directories for a given namespace, * replacing any others previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param string[]|string $paths The PSR-4 base directories * * @throws \InvalidArgumentException + * + * @return void */ public function setPsr4($prefix, $paths) { @@ -234,6 +315,8 @@ public function setPsr4($prefix, $paths) * Turns on searching the include path for class files. * * @param bool $useIncludePath + * + * @return void */ public function setUseIncludePath($useIncludePath) { @@ -256,6 +339,8 @@ public function getUseIncludePath() * that have not been registered with the class map. * * @param bool $classMapAuthoritative + * + * @return void */ public function setClassMapAuthoritative($classMapAuthoritative) { @@ -276,6 +361,8 @@ public function isClassMapAuthoritative() * APCu prefix to use to cache found/not-found classes, if the extension is enabled. * * @param string|null $apcuPrefix + * + * @return void */ public function setApcuPrefix($apcuPrefix) { @@ -296,25 +383,44 @@ public function getApcuPrefix() * Registers this instance as an autoloader. * * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void */ public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } } /** * Unregisters this instance as an autoloader. + * + * @return void */ public function unregister() { spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } } /** * Loads the given class or interface. * * @param string $class The name of the class - * @return bool|null True if loaded, null otherwise + * @return true|null True if loaded, null otherwise */ public function loadClass($class) { @@ -323,6 +429,8 @@ public function loadClass($class) return true; } + + return null; } /** @@ -367,6 +475,21 @@ public function findFile($class) return $file; } + /** + * Returns the currently registered loaders indexed by their corresponding vendor directories. + * + * @return self[] + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ private function findFileWithExtension($class, $ext) { // PSR-4 lookup @@ -438,6 +561,10 @@ private function findFileWithExtension($class, $ext) * Scope isolated include. * * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + * @private */ function includeFile($file) { diff --git a/app/vendor/composer/composer/src/Composer/Autoload/ClassMapGenerator.php b/app/vendor/composer/composer/src/Composer/Autoload/ClassMapGenerator.php index 0724d5dd6..1bb204362 100644 --- a/app/vendor/composer/composer/src/Composer/Autoload/ClassMapGenerator.php +++ b/app/vendor/composer/composer/src/Composer/Autoload/ClassMapGenerator.php @@ -33,8 +33,8 @@ class ClassMapGenerator /** * Generate a class map file * - * @param \Traversable $dirs Directories or a single path to search in - * @param string $file The name of the class map file + * @param \Traversable|array $dirs Directories or a single path to search in + * @param string $file The name of the class map file */ public static function dump($dirs, $file) { @@ -50,22 +50,22 @@ public static function dump($dirs, $file) /** * Iterate over all files in the given directory searching for classes * - * @param \Iterator|string $path The path to search in or an iterator - * @param string $excluded Regex that matches file paths to be excluded from the classmap - * @param IOInterface $io IO object - * @param string $namespace Optional namespace prefix to filter by - * @param string $autoloadType psr-0|psr-4 Optional autoload standard to use mapping rules + * @param \Traversable|string|array $path The path to search in or an iterator + * @param string $excluded Regex that matches file paths to be excluded from the classmap + * @param ?IOInterface $io IO object + * @param ?string $namespace Optional namespace prefix to filter by + * @param ?string $autoloadType psr-0|psr-4 Optional autoload standard to use mapping rules * * @throws \RuntimeException When the path is neither an existing file nor directory * @return array A class map array */ public static function createMap($path, $excluded = null, IOInterface $io = null, $namespace = null, $autoloadType = null, &$scannedFiles = array()) { + $basePath = $path; if (is_string($path)) { - $basePath = $path; if (is_file($path)) { $path = array(new \SplFileInfo($path)); - } elseif (is_dir($path)) { + } elseif (is_dir($path) || strpos($path, '*') !== false) { $path = Finder::create()->files()->followLinks()->name('/\.(php|inc|hh)$/')->in($path); } else { throw new \RuntimeException( @@ -113,10 +113,10 @@ public static function createMap($path, $excluded = null, IOInterface $io = null $classes = self::findClasses($filePath); if (null !== $autoloadType) { - list($classes, $validClasses) = self::filterByNamespace($classes, $filePath, $namespace, $autoloadType, $basePath, $io); + $classes = self::filterByNamespace($classes, $filePath, $namespace, $autoloadType, $basePath, $io); // if no valid class was found in the file then we do not mark it as scanned as it might still be matched by another rule later - if ($validClasses) { + if ($classes) { $scannedFiles[$realPath] = true; } } else { @@ -126,8 +126,7 @@ public static function createMap($path, $excluded = null, IOInterface $io = null foreach ($classes as $class) { // skip classes not within the given namespace prefix - // TODO enable in Composer v1.11 or 2.0 whichever comes first - if (/* null === $autoloadType && */ null !== $namespace && '' !== $namespace && 0 !== strpos($class, $namespace)) { + if (null === $autoloadType && null !== $namespace && '' !== $namespace && 0 !== strpos($class, $namespace)) { continue; } @@ -148,13 +147,13 @@ public static function createMap($path, $excluded = null, IOInterface $io = null /** * Remove classes which could not have been loaded by namespace autoloaders * - * @param array $classes found classes in given file - * @param string $filePath current file - * @param string $baseNamespace prefix of given autoload mapping - * @param string $namespaceType psr-0|psr-4 - * @param string $basePath root directory of given autoload mapping - * @param IOInterface $io IO object - * @return array valid classes + * @param array $classes found classes in given file + * @param string $filePath current file + * @param string $baseNamespace prefix of given autoload mapping + * @param string $namespaceType psr-0|psr-4 + * @param string $basePath root directory of given autoload mapping + * @param ?IOInterface $io IO object + * @return array valid classes */ private static function filterByNamespace($classes, $filePath, $baseNamespace, $namespaceType, $basePath, $io) { @@ -177,8 +176,7 @@ private static function filterByNamespace($classes, $filePath, $baseNamespace, $ $className = substr($class, $namespaceLength + 1); $subPath = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . str_replace('_', DIRECTORY_SEPARATOR, $className); - } - else { + } else { $subPath = str_replace('_', DIRECTORY_SEPARATOR, $class); } } elseif ('psr-4' === $namespaceType) { @@ -196,19 +194,15 @@ private static function filterByNamespace($classes, $filePath, $baseNamespace, $ // warn only if no valid classes, else silently skip invalid if (empty($validClasses)) { foreach ($rejectedClasses as $class) { - trigger_error( - "Class $class located in ".preg_replace('{^'.preg_quote(getcwd()).'}', '.', $filePath, 1)." does not comply with $namespaceType autoloading standard. It will not autoload anymore in Composer v2.0.", - E_USER_DEPRECATED - ); + if ($io) { + $io->writeError("Class $class located in ".preg_replace('{^'.preg_quote(getcwd()).'}', '.', $filePath, 1)." does not comply with $namespaceType autoloading standard. Skipping."); + } } - // TODO enable in Composer 2.0 - //return array(); + return array(); } - // TODO enable in Composer 2.0 & unskip test in AutoloadGeneratorTest::testPSRToClassMapIgnoresNonPSRClasses - //return $validClasses; - return array($classes, $validClasses); + return $validClasses; } /** @@ -220,10 +214,7 @@ private static function filterByNamespace($classes, $filePath, $baseNamespace, $ */ private static function findClasses($path) { - $extraTypes = PHP_VERSION_ID < 50400 ? '' : '|trait'; - if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.3', '>=')) { - $extraTypes .= '|enum'; - } + $extraTypes = self::getExtraTypes(); // Use @ here instead of Silencer to actively suppress 'unhelpful' output // @link https://github.com/composer/composer/pull/4886 @@ -231,7 +222,7 @@ private static function findClasses($path) if (!$contents) { if (!file_exists($path)) { $message = 'File at "%s" does not exist, check your classmap definitions'; - } elseif (!is_readable($path)) { + } elseif (!Filesystem::isReadable($path)) { $message = 'File at "%s" is not readable, check its permissions'; } elseif ('' === trim(file_get_contents($path))) { // The input file was really empty and thus contains no classes @@ -247,32 +238,14 @@ private static function findClasses($path) } // return early if there is no chance of matching anything in this file - if (!preg_match('{\b(?:class|interface'.$extraTypes.')\s}i', $contents)) { + preg_match_all('{\b(?:class|interface'.$extraTypes.')\s}i', $contents, $matches); + if (!$matches) { return array(); } - // strip heredocs/nowdocs - $contents = preg_replace('{<<<[ \t]*([\'"]?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)(?:\s*)\\2(?=\s+|[;,.)])}s', 'null', $contents); - // strip strings - $contents = preg_replace('{"[^"\\\\]*+(\\\\.[^"\\\\]*+)*+"|\'[^\'\\\\]*+(\\\\.[^\'\\\\]*+)*+\'}s', 'null', $contents); - // strip leading non-php code if needed - if (substr($contents, 0, 2) !== '(?:[^<]++|<(?!\?))*+<\?}s', '?>'); - if (false !== $pos && false === strpos(substr($contents, $pos), 'clean(); + unset($p); preg_match_all('{ (?: @@ -309,4 +282,18 @@ private static function findClasses($path) return $classes; } + + private static function getExtraTypes() + { + static $extraTypes = null; + if (null === $extraTypes) { + $extraTypes = PHP_VERSION_ID < 50400 ? '' : '|trait'; + if (PHP_VERSION_ID >= 80100 || (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.3', '>='))) { + $extraTypes .= '|enum'; + } + PhpFileCleaner::setTypeConfig(array_merge(array('class', 'interface'), array_filter(explode('|', $extraTypes)))); + } + + return $extraTypes; + } } diff --git a/app/vendor/composer/composer/src/Composer/Autoload/PhpFileCleaner.php b/app/vendor/composer/composer/src/Composer/Autoload/PhpFileCleaner.php new file mode 100644 index 000000000..9b0872aad --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Autoload/PhpFileCleaner.php @@ -0,0 +1,228 @@ + + * @internal + */ +class PhpFileCleaner +{ + /** @var array */ + private static $typeConfig; + /** @var string */ + private static $restPattern; + + /** + * @readonly + * @var string + */ + private $contents; + + /** + * @readonly + * @var int + */ + private $len; + + /** + * @readonly + * @var int + */ + private $maxMatches; + + /** @var int */ + private $index = 0; + + public static function setTypeConfig($types) + { + foreach ($types as $type) { + self::$typeConfig[$type[0]] = array( + 'name' => $type, + 'length' => \strlen($type), + 'pattern' => '{.\b(?])'.$type.'\s++[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*+}Ais', + ); + } + + self::$restPattern = '{[^?"\'contents = $contents; + $this->len = \strlen($this->contents); + $this->maxMatches = $maxMatches; + } + + public function clean() + { + $clean = ''; + + while ($this->index < $this->len) { + $this->skipToPhp(); + $clean .= 'index < $this->len) { + $char = $this->contents[$this->index]; + if ($char === '?' && $this->peek('>')) { + $clean .= '?>'; + $this->index += 2; + continue 2; + } + + if ($char === '"') { + $this->skipString('"'); + $clean .= 'null'; + continue; + } + + if ($char === "'") { + $this->skipString("'"); + $clean .= 'null'; + continue; + } + + if ($char === "<" && $this->peek('<') && $this->match('{<<<[ \t]*+([\'"]?)([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*+)\\1(?:\r\n|\n|\r)}A', $match)) { + $this->index += \strlen($match[0]); + $this->skipHeredoc($match[2]); + $clean .= 'null'; + continue; + } + + if ($char === '/') { + if ($this->peek('/')) { + $this->skipToNewline(); + continue; + } + if ($this->peek('*')) { + $this->skipComment(); + } + } + + if ($this->maxMatches === 1 && isset(self::$typeConfig[$char])) { + $type = self::$typeConfig[$char]; + if ( + \substr($this->contents, $this->index, $type['length']) === $type['name'] + && \preg_match($type['pattern'], $this->contents, $match, 0, $this->index - 1) + ) { + $clean .= $match[0]; + return $clean; + } + } + + $this->index += 1; + if ($this->match(self::$restPattern, $match)) { + $clean .= $char . $match[0]; + $this->index += \strlen($match[0]); + } else { + $clean .= $char; + } + } + } + + return $clean; + } + + private function skipToPhp() + { + while ($this->index < $this->len) { + if ($this->contents[$this->index] === '<' && $this->peek('?')) { + $this->index += 2; + break; + } + + $this->index += 1; + } + } + + private function skipString($delimiter) + { + $this->index += 1; + while ($this->index < $this->len) { + if ($this->contents[$this->index] === '\\' && ($this->peek('\\') || $this->peek($delimiter))) { + $this->index += 2; + continue; + } + if ($this->contents[$this->index] === $delimiter) { + $this->index += 1; + break; + } + $this->index += 1; + } + } + + private function skipComment() + { + $this->index += 2; + while ($this->index < $this->len) { + if ($this->contents[$this->index] === '*' && $this->peek('/')) { + $this->index += 2; + break; + } + + $this->index += 1; + } + } + + private function skipToNewline() + { + while ($this->index < $this->len) { + if ($this->contents[$this->index] === "\r" || $this->contents[$this->index] === "\n") { + return; + } + $this->index += 1; + } + } + + private function skipHeredoc($delimiter) + { + $firstDelimiterChar = $delimiter[0]; + $delimiterLength = \strlen($delimiter); + $delimiterPattern = '{'.preg_quote($delimiter).'(?![a-zA-Z0-9_\x80-\xff])}A'; + + while ($this->index < $this->len) { + // check if we find the delimiter after some spaces/tabs + switch ($this->contents[$this->index]) { + case "\t": + case " ": + $this->index += 1; + continue 2; + case $firstDelimiterChar: + if ( + \substr($this->contents, $this->index, $delimiterLength) === $delimiter + && $this->match($delimiterPattern) + ) { + $this->index += $delimiterLength; + return; + } + break; + } + + // skip the rest of the line + while ($this->index < $this->len) { + $this->skipToNewline(); + + // skip newlines + while ($this->index < $this->len && ($this->contents[$this->index] === "\r" || $this->contents[$this->index] === "\n")) { + $this->index += 1; + } + + break; + } + } + } + + private function peek($char) + { + return $this->index + 1 < $this->len && $this->contents[$this->index + 1] === $char; + } + + private function match($regex, array &$match = null) + { + if (\preg_match($regex, $this->contents, $match, 0, $this->index)) { + return true; + } + + return false; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Cache.php b/app/vendor/composer/composer/src/Composer/Cache.php index 069f59d5d..a3aaebf57 100644 --- a/app/vendor/composer/composer/src/Composer/Cache.php +++ b/app/vendor/composer/composer/src/Composer/Cache.php @@ -24,25 +24,28 @@ */ class Cache { - private static $cacheCollected = false; + private static $cacheCollected = null; private $io; private $root; private $enabled = true; - private $allowList; + private $allowlist; private $filesystem; + private $readOnly; /** * @param IOInterface $io * @param string $cacheDir location of the cache - * @param string $allowList List of characters that are allowed in path names (used in a regex character class) + * @param string $allowlist List of characters that are allowed in path names (used in a regex character class) * @param Filesystem $filesystem optional filesystem instance + * @param bool $readOnly whether the cache is in readOnly mode */ - public function __construct(IOInterface $io, $cacheDir, $allowList = 'a-z0-9.', Filesystem $filesystem = null) + public function __construct(IOInterface $io, $cacheDir, $allowlist = 'a-z0-9.', Filesystem $filesystem = null, $readOnly = false) { $this->io = $io; $this->root = rtrim($cacheDir, '/\\') . '/'; - $this->allowList = $allowList; + $this->allowlist = $allowlist; $this->filesystem = $filesystem ?: new Filesystem(); + $this->readOnly = (bool) $readOnly; if (!self::isUsable($cacheDir)) { $this->enabled = false; @@ -59,6 +62,22 @@ public function __construct(IOInterface $io, $cacheDir, $allowList = 'a-z0-9.', } } + /** + * @param bool $readOnly + */ + public function setReadOnly($readOnly) + { + $this->readOnly = (bool) $readOnly; + } + + /** + * @return bool + */ + public function isReadOnly() + { + return $this->readOnly; + } + public static function isUsable($path) { return !preg_match('{(^|[\\\\/])(\$null|nul|NUL|/dev/null)([\\\\/]|$)}', $path); @@ -77,7 +96,7 @@ public function getRoot() public function read($file) { if ($this->enabled) { - $file = preg_replace('{[^'.$this->allowList.']}i', '-', $file); + $file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file); if (file_exists($this->root . $file)) { $this->io->writeError('Reading '.$this->root . $file.' from cache', true, IOInterface::DEBUG); @@ -90,25 +109,26 @@ public function read($file) public function write($file, $contents) { - if ($this->enabled) { - $file = preg_replace('{[^'.$this->allowList.']}i', '-', $file); + if ($this->enabled && !$this->readOnly) { + $file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file); $this->io->writeError('Writing '.$this->root . $file.' into cache', true, IOInterface::DEBUG); + $tempFileName = $this->root . $file . uniqid('.', true) . '.tmp'; try { - return file_put_contents($this->root . $file, $contents); + return file_put_contents($tempFileName, $contents) !== false && rename($tempFileName, $this->root . $file); } catch (\ErrorException $e) { $this->io->writeError('Failed to write into cache: '.$e->getMessage().'', true, IOInterface::DEBUG); if (preg_match('{^file_put_contents\(\): Only ([0-9]+) of ([0-9]+) bytes written}', $e->getMessage(), $m)) { // Remove partial file. - unlink($this->root . $file); + unlink($tempFileName); $message = sprintf( 'Writing %1$s into cache failed after %2$u of %3$u bytes written, only %4$u bytes of free space available', - $this->root . $file, + $tempFileName, $m[1], $m[2], - @disk_free_space($this->root . dirname($file)) + @disk_free_space(dirname($tempFileName)) ); $this->io->writeError($message); @@ -128,8 +148,8 @@ public function write($file, $contents) */ public function copyFrom($file, $source) { - if ($this->enabled) { - $file = preg_replace('{[^'.$this->allowList.']}i', '-', $file); + if ($this->enabled && !$this->readOnly) { + $file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file); $this->filesystem->ensureDirectoryExists(dirname($this->root . $file)); if (!file_exists($source)) { @@ -150,7 +170,7 @@ public function copyFrom($file, $source) public function copyTo($file, $target) { if ($this->enabled) { - $file = preg_replace('{[^'.$this->allowList.']}i', '-', $file); + $file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file); if (file_exists($this->root . $file)) { try { touch($this->root . $file, filemtime($this->root . $file), time()); @@ -171,13 +191,26 @@ public function copyTo($file, $target) public function gcIsNecessary() { - return (!self::$cacheCollected && !mt_rand(0, 50)); + if (self::$cacheCollected) { + return false; + } + + self::$cacheCollected = true; + if (getenv('COMPOSER_TEST_SUITE')) { + return false; + } + + if (PHP_VERSION_ID > 70000) { + return !random_int(0, 50); + } + + return !mt_rand(0, 50); } public function remove($file) { if ($this->enabled) { - $file = preg_replace('{[^'.$this->allowList.']}i', '-', $file); + $file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file); if (file_exists($this->root . $file)) { return $this->filesystem->unlink($this->root . $file); } @@ -190,6 +223,7 @@ public function clear() { if ($this->enabled) { $this->filesystem->emptyDirectory($this->root); + return true; } @@ -229,7 +263,7 @@ public function gc($ttl, $maxSize) public function sha1($file) { if ($this->enabled) { - $file = preg_replace('{[^'.$this->allowList.']}i', '-', $file); + $file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file); if (file_exists($this->root . $file)) { return sha1_file($this->root . $file); } @@ -241,7 +275,7 @@ public function sha1($file) public function sha256($file) { if ($this->enabled) { - $file = preg_replace('{[^'.$this->allowList.']}i', '-', $file); + $file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file); if (file_exists($this->root . $file)) { return hash_file('sha256', $this->root . $file); } diff --git a/app/vendor/composer/composer/src/Composer/Command/AboutCommand.php b/app/vendor/composer/composer/src/Composer/Command/AboutCommand.php index bf1fa0f9c..931c20e55 100644 --- a/app/vendor/composer/composer/src/Composer/Command/AboutCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/AboutCommand.php @@ -12,6 +12,7 @@ namespace Composer\Command; +use Composer\Composer; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -24,7 +25,7 @@ protected function configure() { $this ->setName('about') - ->setDescription('Shows the short information about Composer.') + ->setDescription('Shows a short information about Composer.') ->setHelp( <<php composer.phar about @@ -35,9 +36,11 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { + $composerVersion = Composer::getVersion(); + $this->getIO()->write( <<Composer - Dependency Manager for PHP +Composer - Dependency Manager for PHP - version $composerVersion Composer is a dependency manager tracking local dependencies of your projects and libraries. See https://getcomposer.org/ for more information. EOT diff --git a/app/vendor/composer/composer/src/Composer/Command/ArchiveCommand.php b/app/vendor/composer/composer/src/Composer/Command/ArchiveCommand.php index bbe18a653..3acf4fd04 100644 --- a/app/vendor/composer/composer/src/Composer/Command/ArchiveCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/ArchiveCommand.php @@ -16,12 +16,15 @@ use Composer\IO\IOInterface; use Composer\Config; use Composer\Composer; +use Composer\Package\CompletePackageInterface; use Composer\Repository\CompositeRepository; use Composer\Repository\RepositoryFactory; use Composer\Script\ScriptEvents; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; use Composer\Util\Filesystem; +use Composer\Util\Loop; +use Composer\Util\ProcessExecutor; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -46,7 +49,7 @@ protected function configure() new InputOption('dir', null, InputOption::VALUE_REQUIRED, 'Write the archive to this directory'), new InputOption('file', null, InputOption::VALUE_REQUIRED, 'Write the archive with the given file name.' .' Note that the format will be appended.'), - new InputOption('ignore-filters', false, InputOption::VALUE_NONE, 'Ignore filters when saving package'), + new InputOption('ignore-filters', null, InputOption::VALUE_NONE, 'Ignore filters when saving package'), )) ->setHelp( <<getArchiveManager(); } else { $factory = new Factory; - $downloadManager = $factory->createDownloadManager($io, $config); - $archiveManager = $factory->createArchiveManager($config, $downloadManager); + $process = new ProcessExecutor(); + $httpDownloader = Factory::createHttpDownloader($io, $config); + $downloadManager = $factory->createDownloadManager($io, $config, $httpDownloader, $process); + $archiveManager = $factory->createArchiveManager($config, $downloadManager, new Loop($httpDownloader, $process)); } if ($packageName) { @@ -136,6 +141,9 @@ protected function archive(IOInterface $io, Config $config, $packageName = null, return 0; } + /** + * @return CompletePackageInterface|false + */ protected function selectPackage(IOInterface $io, $packageName, $version = null) { $io->writeError('Searching for the specified package.'); @@ -167,6 +175,10 @@ protected function selectPackage(IOInterface $io, $packageName, $version = null) return false; } + if (!$package instanceof CompletePackageInterface) { + throw new \LogicException('Expected a CompletePackageInterface instance but found '.get_class($package)); + } + return $package; } } diff --git a/app/vendor/composer/composer/src/Composer/Command/BaseCommand.php b/app/vendor/composer/composer/src/Composer/Command/BaseCommand.php index 888b2a7f2..b473fbe80 100644 --- a/app/vendor/composer/composer/src/Composer/Command/BaseCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/BaseCommand.php @@ -19,14 +19,20 @@ use Composer\IO\IOInterface; use Composer\IO\NullIO; use Composer\Plugin\PreCommandRunEvent; +use Composer\Package\Version\VersionParser; use Composer\Plugin\PluginEvents; +use Composer\Util\Platform; +use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Terminal; /** * Base class for Composer commands * + * @method Application getApplication() + * * @author Ryan Weaver * @author Konstantin Kudryashov */ @@ -46,7 +52,7 @@ abstract class BaseCommand extends Command * @param bool $required * @param bool|null $disablePlugins * @throws \RuntimeException - * @return Composer + * @return Composer|null */ public function getComposer($required = true, $disablePlugins = null) { @@ -55,6 +61,7 @@ public function getComposer($required = true, $disablePlugins = null) if ($application instanceof Application) { /* @var $application Application */ $this->composer = $application->getComposer($required, $disablePlugins); + /** @phpstan-ignore-next-line */ } elseif ($required) { throw new \RuntimeException( 'Could not create a Composer\Composer instance, you must inject '. @@ -103,8 +110,8 @@ public function getIO() if (null === $this->io) { $application = $this->getApplication(); if ($application instanceof Application) { - /* @var $application Application */ $this->io = $application->getIO(); + /** @phpstan-ignore-next-line */ } else { $this->io = new NullIO(); } @@ -171,11 +178,96 @@ protected function getPreferredInstallOptions(Config $config, InputInterface $in break; } + if ($input->hasOption('prefer-install') && $input->getOption('prefer-install')) { + if ($input->getOption('prefer-source')) { + throw new \InvalidArgumentException('--prefer-source can not be used together with --prefer-install'); + } + if ($input->getOption('prefer-dist')) { + throw new \InvalidArgumentException('--prefer-dist can not be used together with --prefer-install'); + } + switch ($input->getOption('prefer-install')) { + case 'dist': + $input->setOption('prefer-dist', true); + break; + case 'source': + $input->setOption('prefer-source', true); + break; + case 'auto': + $preferDist = false; + $preferSource = false; + break; + default: + throw new \UnexpectedValueException('--prefer-install accepts one of "dist", "source" or "auto", got '.$input->getOption('prefer-install')); + } + } + if ($input->getOption('prefer-source') || $input->getOption('prefer-dist') || ($keepVcsRequiresPreferSource && $input->hasOption('keep-vcs') && $input->getOption('keep-vcs'))) { $preferSource = $input->getOption('prefer-source') || ($keepVcsRequiresPreferSource && $input->hasOption('keep-vcs') && $input->getOption('keep-vcs')); - $preferDist = $input->getOption('prefer-dist'); + $preferDist = (bool) $input->getOption('prefer-dist'); } return array($preferSource, $preferDist); } + + protected function formatRequirements(array $requirements) + { + $requires = array(); + $requirements = $this->normalizeRequirements($requirements); + foreach ($requirements as $requirement) { + if (!isset($requirement['version'])) { + throw new \UnexpectedValueException('Option '.$requirement['name'] .' is missing a version constraint, use e.g. '.$requirement['name'].':^1.0'); + } + $requires[$requirement['name']] = $requirement['version']; + } + + return $requires; + } + + protected function normalizeRequirements(array $requirements) + { + $parser = new VersionParser(); + + return $parser->parseNameVersionPairs($requirements); + } + + protected function renderTable(array $table, OutputInterface $output) + { + $renderer = new Table($output); + $renderer->setStyle('compact'); + $rendererStyle = $renderer->getStyle(); + if (method_exists($rendererStyle, 'setVerticalBorderChars')) { + $rendererStyle->setVerticalBorderChars(''); + } else { + // TODO remove in composer 2.2 + // @phpstan-ignore-next-line + $rendererStyle->setVerticalBorderChar(''); + } + $rendererStyle->setCellRowContentFormat('%s '); + $renderer->setRows($table)->render(); + } + + protected function getTerminalWidth() + { + if (class_exists('Symfony\Component\Console\Terminal')) { + $terminal = new Terminal(); + $width = $terminal->getWidth(); + } else { + // For versions of Symfony console before 3.2 + // TODO remove in composer 2.2 + // @phpstan-ignore-next-line + list($width) = $this->getApplication()->getTerminalDimensions(); + } + if (null === $width) { + // In case the width is not detected, we're probably running the command + // outside of a real terminal, use space without a limit + $width = PHP_INT_MAX; + } + if (Platform::isWindows()) { + $width--; + } else { + $width = max(80, $width); + } + + return $width; + } } diff --git a/app/vendor/composer/composer/src/Composer/Command/BaseDependencyCommand.php b/app/vendor/composer/composer/src/Composer/Command/BaseDependencyCommand.php index 34de286f8..962c1b2ce 100644 --- a/app/vendor/composer/composer/src/Composer/Command/BaseDependencyCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/BaseDependencyCommand.php @@ -12,21 +12,20 @@ namespace Composer\Command; -use Composer\DependencyResolver\Pool; use Composer\Package\Link; use Composer\Package\PackageInterface; -use Composer\Repository\ArrayRepository; +use Composer\Package\RootPackage; +use Composer\Repository\InstalledArrayRepository; use Composer\Repository\CompositeRepository; +use Composer\Repository\RootPackageRepository; +use Composer\Repository\InstalledRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryFactory; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Composer\Package\Version\VersionParser; -use Symfony\Component\Console\Helper\Table; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** @@ -37,25 +36,13 @@ class BaseDependencyCommand extends BaseCommand { const ARGUMENT_PACKAGE = 'package'; - const ARGUMENT_CONSTRAINT = 'constraint'; + const ARGUMENT_CONSTRAINT = 'version'; const OPTION_RECURSIVE = 'recursive'; const OPTION_TREE = 'tree'; + /** @var ?string[] */ protected $colors; - /** - * Set common options and arguments. - */ - protected function configure() - { - $this->setDefinition(array( - new InputArgument(self::ARGUMENT_PACKAGE, InputArgument::REQUIRED, 'Package to inspect'), - new InputArgument(self::ARGUMENT_CONSTRAINT, InputArgument::OPTIONAL, 'Optional version constraint', '*'), - new InputOption(self::OPTION_RECURSIVE, 'r', InputOption::VALUE_NONE, 'Recursively resolves up to the root package'), - new InputOption(self::OPTION_TREE, 't', InputOption::VALUE_NONE, 'Prints the results as a nested tree'), - )); - } - /** * Execute the command. * @@ -71,35 +58,32 @@ protected function doExecute(InputInterface $input, OutputInterface $output, $in $commandEvent = new CommandEvent(PluginEvents::COMMAND, $this->getName(), $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); - // Prepare repositories and set up a pool $platformOverrides = $composer->getConfig()->get('platform') ?: array(); - $repository = new CompositeRepository(array( - new ArrayRepository(array($composer->getPackage())), + $installedRepo = new InstalledRepository(array( + new RootPackageRepository($composer->getPackage()), $composer->getRepositoryManager()->getLocalRepository(), new PlatformRepository(array(), $platformOverrides), )); - $pool = new Pool(); - $pool->addRepository($repository); // Parse package name and constraint list($needle, $textConstraint) = array_pad( explode(':', $input->getArgument(self::ARGUMENT_PACKAGE)), 2, - $input->getArgument(self::ARGUMENT_CONSTRAINT) + $input->hasArgument(self::ARGUMENT_CONSTRAINT) ? $input->getArgument(self::ARGUMENT_CONSTRAINT) : '*' ); // Find packages that are or provide the requested package first - $packages = $pool->whatProvides(strtolower($needle)); + $packages = $installedRepo->findPackagesWithReplacersAndProviders($needle); if (empty($packages)) { throw new \InvalidArgumentException(sprintf('Could not find package "%s" in your project', $needle)); } // If the version we ask for is not installed then we need to locate it in remote repos and add it. // This is needed for why-not to resolve conflicts from an uninstalled version against installed packages. - if (!$repository->findPackage($needle, $textConstraint)) { + if (!$installedRepo->findPackage($needle, $textConstraint)) { $defaultRepos = new CompositeRepository(RepositoryFactory::defaultRepos($this->getIO())); if ($match = $defaultRepos->findPackage($needle, $textConstraint)) { - $repository->addRepository(new ArrayRepository(array(clone $match))); + $installedRepo->addRepository(new InstalledArrayRepository(array(clone $match))); } } @@ -126,7 +110,7 @@ protected function doExecute(InputInterface $input, OutputInterface $output, $in $recursive = $renderTree || $input->getOption(self::OPTION_RECURSIVE); // Resolve dependencies - $results = $repository->getDependents($needles, $constraint, $inverted, $recursive); + $results = $installedRepo->getDependents($needles, $constraint, $inverted, $recursive); if (empty($results)) { $extra = (null !== $constraint) ? sprintf(' in versions %smatching %s', $inverted ? 'not ' : '', $textConstraint) : ''; $this->getIO()->writeError(sprintf( @@ -170,7 +154,7 @@ protected function printTable(OutputInterface $output, $results) continue; } $doubles[$unique] = true; - $version = (strpos($package->getPrettyVersion(), 'No version set') === 0) ? '-' : $package->getPrettyVersion(); + $version = $package->getPrettyVersion() === RootPackage::DEFAULT_PRETTY_VERSION ? '-' : $package->getPrettyVersion(); $rows[] = array($package->getPrettyName(), $version, $link->getDescription(), sprintf('%s (%s)', $link->getTarget(), $link->getPrettyConstraint())); if ($children) { $queue = array_merge($queue, $children); @@ -180,17 +164,7 @@ protected function printTable(OutputInterface $output, $results) $table = array_merge($rows, $table); } while (!empty($results)); - // Render table - $renderer = new Table($output); - $renderer->setStyle('compact'); - $rendererStyle = $renderer->getStyle(); - if (method_exists($rendererStyle, 'setVerticalBorderChars')) { - $rendererStyle->setVerticalBorderChars(''); - } else { - $rendererStyle->setVerticalBorderChar(''); - } - $rendererStyle->setCellRowContentFormat('%s '); - $renderer->setRows($table)->render(); + $this->renderTable($table, $output); } /** @@ -229,14 +203,14 @@ protected function printTree($results, $prefix = '', $level = 1) /** * @var PackageInterface $package * @var Link $link - * @var array|bool $children + * @var array|bool $children */ list($package, $link, $children) = $result; $color = $this->colors[$level % count($this->colors)]; $prevColor = $this->colors[($level - 1) % count($this->colors)]; $isLast = (++$idx == $count); - $versionText = (strpos($package->getPrettyVersion(), 'No version set') === 0) ? '' : $package->getPrettyVersion(); + $versionText = $package->getPrettyVersion() === RootPackage::DEFAULT_PRETTY_VERSION ? '' : $package->getPrettyVersion(); $packageText = rtrim(sprintf('<%s>%s %s', $color, $package->getPrettyName(), $versionText)); $linkText = sprintf('%s <%s>%s %s', $link->getDescription(), $prevColor, $link->getTarget(), $link->getPrettyConstraint()); $circularWarn = $children === false ? '(circular dependency aborted here)' : ''; diff --git a/app/vendor/composer/composer/src/Composer/Command/CheckPlatformReqsCommand.php b/app/vendor/composer/composer/src/Composer/Command/CheckPlatformReqsCommand.php index 622882ceb..2f68a65cb 100644 --- a/app/vendor/composer/composer/src/Composer/Command/CheckPlatformReqsCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/CheckPlatformReqsCommand.php @@ -13,13 +13,13 @@ namespace Composer\Command; use Composer\Package\Link; -use Composer\Package\PackageInterface; use Composer\Semver\Constraint\Constraint; -use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Composer\Repository\PlatformRepository; +use Composer\Repository\RootPackageRepository; +use Composer\Repository\InstalledRepository; class CheckPlatformReqsCommand extends BaseCommand { @@ -29,6 +29,7 @@ protected function configure() ->setDescription('Check that platform requirements are satisfied.') ->setDefinition(array( new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables checking of require-dev packages requirements.'), + new InputOption('lock', null, InputOption::VALUE_NONE, 'Checks requirements only from the lock file, not from installed packages.'), )) ->setHelp( <<getComposer(); - $requires = $composer->getPackage()->getRequires(); - if ($input->getOption('no-dev')) { - $dependencies = $composer->getLocker()->getLockedRepository(!$input->getOption('no-dev'))->getPackages(); + $requires = array(); + $removePackages = array(); + if ($input->getOption('lock')) { + $this->getIO()->writeError('Checking '.($input->getOption('no-dev') ? 'non-dev ' : '').'platform requirements using the lock file'); + $installedRepo = $composer->getLocker()->getLockedRepository(!$input->getOption('no-dev')); } else { - $dependencies = $composer->getRepositoryManager()->getLocalRepository()->getPackages(); + $installedRepo = $composer->getRepositoryManager()->getLocalRepository(); // fallback to lockfile if installed repo is empty - if (!$dependencies) { - $dependencies = $composer->getLocker()->getLockedRepository(true)->getPackages(); + if (!$installedRepo->getPackages()) { + $this->getIO()->writeError('No vendor dir present, checking '.($input->getOption('no-dev') ? 'non-dev ' : '').'platform requirements from the lock file'); + $installedRepo = $composer->getLocker()->getLockedRepository(!$input->getOption('no-dev')); + } else { + if ($input->getOption('no-dev')) { + $removePackages = $installedRepo->getDevPackageNames(); + } + + $this->getIO()->writeError('Checking '.($input->getOption('no-dev') ? 'non-dev ' : '').'platform requirements for packages in the vendor dir'); } + } + if (!$input->getOption('no-dev')) { $requires += $composer->getPackage()->getDevRequires(); } + foreach ($requires as $require => $link) { $requires[$require] = array($link); } - foreach ($dependencies as $package) { + $installedRepo = new InstalledRepository(array($installedRepo, new RootPackageRepository($composer->getPackage()))); + foreach ($installedRepo->getPackages() as $package) { + if (in_array($package->getName(), $removePackages, true)) { + continue; + } foreach ($package->getRequires() as $require => $link) { $requires[$require][] = $link; } @@ -69,62 +86,78 @@ protected function execute(InputInterface $input, OutputInterface $output) ksort($requires); - $platformRepo = new PlatformRepository(array(), array()); - $currentPlatformPackages = $platformRepo->getPackages(); - $currentPlatformPackageMap = array(); - - /** - * @var PackageInterface $currentPlatformPackage - */ - foreach ($currentPlatformPackages as $currentPlatformPackage) { - $currentPlatformPackageMap[$currentPlatformPackage->getName()] = $currentPlatformPackage; - } + $installedRepo->addRepository(new PlatformRepository(array(), array())); $results = array(); - $exitCode = 0; /** * @var Link[] $links */ foreach ($requires as $require => $links) { - if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $require)) { - if (isset($currentPlatformPackageMap[$require])) { - $pass = true; - $version = $currentPlatformPackageMap[$require]->getVersion(); - - foreach ($links as $link) { - if (!$link->getConstraint()->matches(new Constraint('=', $version))) { - $results[] = array( - $currentPlatformPackageMap[$require]->getPrettyName(), - $currentPlatformPackageMap[$require]->getPrettyVersion(), - $link, - 'failed', - ); - $pass = false; - - $exitCode = max($exitCode, 1); + if (PlatformRepository::isPlatformPackage($require)) { + $candidates = $installedRepo->findPackagesWithReplacersAndProviders($require); + if ($candidates) { + $reqResults = array(); + foreach ($candidates as $candidate) { + $candidateConstraint = null; + if ($candidate->getName() === $require) { + $candidateConstraint = new Constraint('=', $candidate->getVersion()); + $candidateConstraint->setPrettyString($candidate->getPrettyVersion()); + } else { + foreach (array_merge($candidate->getProvides(), $candidate->getReplaces()) as $link) { + if ($link->getTarget() === $require) { + $candidateConstraint = $link->getConstraint(); + break; + } + } + } + + // safety check for phpstan, but it should not be possible to get a candidate out of findPackagesWithReplacersAndProviders without a constraint matching $require + if (!$candidateConstraint) { + continue; + } + + foreach ($links as $link) { + if (!$link->getConstraint()->matches($candidateConstraint)) { + $reqResults[] = array( + $candidate->getName() === $require ? $candidate->getPrettyName() : $require, + $candidateConstraint->getPrettyString(), + $link, + 'failed'.($candidate->getName() === $require ? '' : ' provided by '.$candidate->getPrettyName().''), + ); + + // skip to next candidate + continue 2; + } } - } - if ($pass) { $results[] = array( - $currentPlatformPackageMap[$require]->getPrettyName(), - $currentPlatformPackageMap[$require]->getPrettyVersion(), + $candidate->getName() === $require ? $candidate->getPrettyName() : $require, + $candidateConstraint->getPrettyString(), null, - 'success', + 'success'.($candidate->getName() === $require ? '' : ' provided by '.$candidate->getPrettyName().''), ); + + // candidate matched, skip to next requirement + continue 2; } - } else { - $results[] = array( - $require, - 'n/a', - $links[0], - 'missing', - ); - - $exitCode = max($exitCode, 2); + + // show the first error from every failed candidate + $results = array_merge($results, $reqResults); + $exitCode = max($exitCode, 1); + + continue; } + + $results[] = array( + $require, + 'n/a', + $links[0], + 'missing', + ); + + $exitCode = max($exitCode, 2); } } @@ -135,7 +168,6 @@ protected function execute(InputInterface $input, OutputInterface $output) protected function printTable(OutputInterface $output, $results) { - $table = array(); $rows = array(); foreach ($results as $result) { /** @@ -149,18 +181,7 @@ protected function printTable(OutputInterface $output, $results) $status, ); } - $table = array_merge($rows, $table); - - // Render table - $renderer = new Table($output); - $renderer->setStyle('compact'); - $rendererStyle = $renderer->getStyle(); - if (method_exists($rendererStyle, 'setVerticalBorderChars')) { - $rendererStyle->setVerticalBorderChars(''); - } else { - $rendererStyle->setVerticalBorderChar(''); - } - $rendererStyle->setCellRowContentFormat('%s '); - $renderer->setRows($table)->render(); + + $this->renderTable($rows, $output); } } diff --git a/app/vendor/composer/composer/src/Composer/Command/ClearCacheCommand.php b/app/vendor/composer/composer/src/Composer/Command/ClearCacheCommand.php index 2f511641e..bdbdd80cf 100644 --- a/app/vendor/composer/composer/src/Composer/Command/ClearCacheCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/ClearCacheCommand.php @@ -59,6 +59,7 @@ protected function execute(InputInterface $input, OutputInterface $output) continue; } $cache = new Cache($io, $cachePath); + $cache->setReadOnly($config->get('cache-read-only')); if (!$cache->isEnabled()) { $io->writeError("Cache is not enabled ($key): $cachePath"); diff --git a/app/vendor/composer/composer/src/Composer/Command/ConfigCommand.php b/app/vendor/composer/composer/src/Composer/Command/ConfigCommand.php index be1743ba0..39b4604d3 100644 --- a/app/vendor/composer/composer/src/Composer/Command/ConfigCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/ConfigCommand.php @@ -12,6 +12,7 @@ namespace Composer\Command; +use Composer\Util\Filesystem; use Composer\Util\Platform; use Composer\Util\Silencer; use Symfony\Component\Console\Input\InputInterface; @@ -73,6 +74,9 @@ protected function configure() new InputOption('list', 'l', InputOption::VALUE_NONE, 'List configuration settings'), new InputOption('file', 'f', InputOption::VALUE_REQUIRED, 'If you want to choose a different composer.json or config.json'), new InputOption('absolute', null, InputOption::VALUE_NONE, 'Returns absolute paths when fetching *-dir config values instead of relative'), + new InputOption('json', 'j', InputOption::VALUE_NONE, 'JSON decode the setting value, to be used with extra.* keys'), + new InputOption('merge', 'm', InputOption::VALUE_NONE, 'Merge the setting value with the current value, to be used with extra.* keys in combination with --json'), + new InputOption('append', null, InputOption::VALUE_NONE, 'When adding a repository, append it (lowest priority) to the existing ones instead of prepending it (highest priority)'), new InputArgument('setting-key', null, 'Setting key'), new InputArgument('setting-value', InputArgument::IS_ARRAY, 'Setting value'), )) @@ -119,6 +123,10 @@ protected function configure() %command.full_name% extra.property value +Or to add a complex value you can use json with: + + %command.full_name% extra.property --json '{"foo":true, "bar": []}' + To edit the file in an external editor: %command.full_name% --editor @@ -236,7 +244,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } $settingKey = $input->getArgument('setting-key'); - if (!$settingKey) { + if (!$settingKey || !is_string($settingKey)) { return 0; } @@ -285,7 +293,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $value = $data; } elseif (isset($data['config'][$settingKey])) { $value = $this->config->get($settingKey, $input->getOption('absolute') ? 0 : Config::RELATIVE_PATHS); - } elseif (in_array($settingKey, $properties, true) && isset($rawData[$settingKey])) { + } elseif (isset($rawData[$settingKey]) && in_array($settingKey, $properties, true)) { $value = $rawData[$settingKey]; } else { throw new \RuntimeException($settingKey.' is not defined'); @@ -322,6 +330,14 @@ function ($val) { return $val; }, ), + 'gitlab-protocol' => array( + function ($val) { + return in_array($val, array('git', 'http', 'https'), true); + }, + function ($val) { + return $val; + }, + ), 'store-auths' => array( function ($val) { return in_array($val, array('true', 'false', 'prompt'), true); @@ -374,7 +390,7 @@ function ($val) { ), 'bin-compat' => array( function ($val) { - return in_array($val, array('auto', 'full')); + return in_array($val, array('auto', 'full', 'symlink')); }, function ($val) { return $val; @@ -404,7 +420,7 @@ function ($val) { 'secure-http' => array($booleanValidator, $booleanNormalizer), 'cafile' => array( function ($val) { - return file_exists($val) && is_readable($val); + return file_exists($val) && Filesystem::isReadable($val); }, function ($val) { return $val === 'null' ? null : $val; @@ -412,7 +428,7 @@ function ($val) { ), 'capath' => array( function ($val) { - return is_dir($val) && is_readable($val); + return is_dir($val) && Filesystem::isReadable($val); }, function ($val) { return $val === 'null' ? null : $val; @@ -421,6 +437,18 @@ function ($val) { 'github-expose-hostname' => array($booleanValidator, $booleanNormalizer), 'htaccess-protect' => array($booleanValidator, $booleanNormalizer), 'lock' => array($booleanValidator, $booleanNormalizer), + 'platform-check' => array( + function ($val) { + return in_array($val, array('php-only', 'true', 'false', '1', '0'), true); + }, + function ($val) { + if ('php-only' === $val) { + return 'php-only'; + } + + return $val !== 'false' && (bool) $val; + }, + ), ); $multiConfigValues = array( 'github-protocols' => array( @@ -558,7 +586,7 @@ function ($vals) { ), ); - if ($input->getOption('global') && (isset($uniqueProps[$settingKey]) || isset($multiProps[$settingKey]) || substr($settingKey, 0, 6) === 'extra.')) { + if ($input->getOption('global') && (isset($uniqueProps[$settingKey]) || isset($multiProps[$settingKey]) || strpos($settingKey, 'extra.') === 0)) { throw new \InvalidArgumentException('The '.$settingKey.' property can not be set in the global config.json file. Use `composer global config` to apply changes to the global composer.json'); } if ($input->getOption('unset') && (isset($uniqueProps[$settingKey]) || isset($multiProps[$settingKey]))) { @@ -589,7 +617,7 @@ function ($vals) { $this->configSource->addRepository($matches[1], array( 'type' => $values[0], 'url' => $values[1], - )); + ), $input->getOption('append')); return 0; } @@ -598,13 +626,13 @@ function ($vals) { $value = strtolower($values[0]); if (true === $booleanValidator($value)) { if (false === $booleanNormalizer($value)) { - $this->configSource->addRepository($matches[1], false); + $this->configSource->addRepository($matches[1], false, $input->getOption('append')); return 0; } } else { $value = JsonFile::parseJson($values[0]); - $this->configSource->addRepository($matches[1], $value); + $this->configSource->addRepository($matches[1], $value, $input->getOption('append')); return 0; } @@ -621,7 +649,21 @@ function ($vals) { return 0; } - $this->configSource->addProperty($settingKey, $values[0]); + $value = $values[0]; + if ($input->getOption('json')) { + $value = JsonFile::parseJson($value); + if ($input->getOption('merge')) { + $currentValue = $this->configFile->read(); + $bits = explode('.', $settingKey); + foreach ($bits as $bit) { + $currentValue = isset($currentValue[$bit]) ? $currentValue[$bit] : null; + } + if (is_array($currentValue)) { + $value = array_merge($currentValue, $value); + } + } + } + $this->configSource->addProperty($settingKey, $value); return 0; } @@ -681,6 +723,9 @@ function ($vals) { } $this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]); $this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], array('consumer-key' => $values[0], 'consumer-secret' => $values[1])); + } elseif ($matches[1] === 'gitlab-token' && 2 === count($values)) { + $this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]); + $this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], array('username' => $values[0], 'token' => $values[1])); } elseif (in_array($matches[1], array('github-oauth', 'gitlab-oauth', 'gitlab-token', 'bearer'), true)) { if (1 !== count($values)) { throw new \RuntimeException('Too many arguments, expected only one token'); diff --git a/app/vendor/composer/composer/src/Composer/Command/CreateProjectCommand.php b/app/vendor/composer/composer/src/Composer/Command/CreateProjectCommand.php index 1e52674ba..b64b01e18 100644 --- a/app/vendor/composer/composer/src/Composer/Command/CreateProjectCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/CreateProjectCommand.php @@ -16,18 +16,17 @@ use Composer\Factory; use Composer\Installer; use Composer\Installer\ProjectInstaller; -use Composer\Installer\InstallationManager; use Composer\Installer\SuggestedPackagesReporter; use Composer\IO\IOInterface; use Composer\Package\BasePackage; -use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Operation\InstallOperation; use Composer\Package\Version\VersionSelector; use Composer\Package\AliasPackage; use Composer\Repository\RepositoryFactory; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; -use Composer\Repository\InstalledFilesystemRepository; +use Composer\Repository\InstalledArrayRepository; +use Composer\Repository\RepositorySet; use Composer\Script\ScriptEvents; use Composer\Util\Silencer; use Symfony\Component\Console\Input\InputArgument; @@ -38,8 +37,9 @@ use Composer\Json\JsonFile; use Composer\Config\JsonConfigSource; use Composer\Util\Filesystem; +use Composer\Util\Platform; +use Composer\Util\ProcessExecutor; use Composer\Package\Version\VersionParser; -use Composer\EventDispatcher\EventDispatcher; /** * Install a package as new project into new directory. @@ -67,10 +67,11 @@ protected function configure() new InputArgument('version', InputArgument::OPTIONAL, 'Version, will default to latest'), new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum-stability allowed (unless a version is specified).'), new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), - new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), - new InputOption('repository', null, InputOption::VALUE_REQUIRED, 'Pick a different repository (as url or json config) to look for the package.'), + new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist (default behavior).'), + new InputOption('prefer-install', null, InputOption::VALUE_REQUIRED, 'Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).'), + new InputOption('repository', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Add custom repositories to look the package up, either by URL or using JSON arrays'), new InputOption('repository-url', null, InputOption::VALUE_REQUIRED, 'DEPRECATED: Use --repository instead.'), - new InputOption('add-repository', null, InputOption::VALUE_NONE, 'Add the repository option to the composer.json.'), + new InputOption('add-repository', null, InputOption::VALUE_NONE, 'Add the custom repository in the composer.json. If a lock file is present it will be deleted and an update will be run instead of install.'), new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'), new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'), @@ -80,7 +81,9 @@ protected function configure() new InputOption('keep-vcs', null, InputOption::VALUE_NONE, 'Whether to prevent deleting the vcs folder.'), new InputOption('remove-vcs', null, InputOption::VALUE_NONE, 'Whether to force deletion of the vcs folder without prompting.'), new InputOption('no-install', null, InputOption::VALUE_NONE, 'Whether to skip installation of the package dependencies.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'), + new InputOption('ask', null, InputOption::VALUE_NONE, 'Whether to ask for project directory.'), )) ->setHelp( <<setOption('no-plugins', true); } + if ($input->isInteractive() && $input->getOption('ask')) { + $parts = explode("/", strtolower($input->getArgument('package')), 2); + $input->setArgument('directory', $io->ask('New project directory ['.array_pop($parts).']: ')); + } + + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); + return $this->installProject( $io, $config, @@ -143,54 +153,64 @@ protected function execute(InputInterface $input, OutputInterface $output) $input->getOption('no-scripts'), $input->getOption('no-progress'), $input->getOption('no-install'), - $input->getOption('ignore-platform-reqs'), + $ignorePlatformReqs, !$input->getOption('no-secure-http'), $input->getOption('add-repository') ); } - public function installProject(IOInterface $io, Config $config, InputInterface $input, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repository = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false, $secureHttp = true, $addRepository = false) + public function installProject(IOInterface $io, Config $config, InputInterface $input, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositories = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false, $secureHttp = true, $addRepository = false) { $oldCwd = getcwd(); + if ($repositories !== null && !is_array($repositories)) { + $repositories = (array) $repositories; + } + // we need to manually load the configuration to pass the auth credentials to the io interface! $io->loadConfiguration($config); $this->suggestedPackagesReporter = new SuggestedPackagesReporter($io); if ($packageName !== null) { - $installedFromVcs = $this->installRootPackage($io, $config, $packageName, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repository, $disablePlugins, $noScripts, $noProgress, $ignorePlatformReqs, $secureHttp); + $installedFromVcs = $this->installRootPackage($io, $config, $packageName, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repositories, $disablePlugins, $noScripts, $noProgress, $ignorePlatformReqs, $secureHttp); } else { $installedFromVcs = false; } + if ($repositories !== null && $addRepository && is_file('composer.lock')) { + unlink('composer.lock'); + } + $composer = Factory::create($io, null, $disablePlugins); + $composer->getEventDispatcher()->setRunScripts(!$noScripts); // add the repository to the composer.json and use it for the install run later - if ($repository !== null && $addRepository) { - if ($composer->getLocker()->isLocked()) { - $io->writeError('Adding a repository when creating a project that provides a composer.lock file is not supported'); + if ($repositories !== null && $addRepository) { + foreach ($repositories as $index => $repo) { + $repoConfig = RepositoryFactory::configFromString($io, $composer->getConfig(), $repo, true); + $composerJsonRepositoriesConfig = $composer->getConfig()->getRepositories(); + $name = RepositoryFactory::generateRepositoryName($index, $repoConfig, $composerJsonRepositoriesConfig); + $configSource = new JsonConfigSource(new JsonFile('composer.json')); + + if ( + (isset($repoConfig['packagist']) && $repoConfig === array('packagist' => false)) + || (isset($repoConfig['packagist.org']) && $repoConfig === array('packagist.org' => false)) + ) { + $configSource->addRepository('packagist.org', false); + } else { + $configSource->addRepository($name, $repoConfig, false); + } - return false; + $composer = Factory::create($io, null, $disablePlugins); } - - $repoConfig = RepositoryFactory::configFromString($io, $composer->getConfig(), $repository, true); - $composerJsonRepositoriesConfig = $composer->getConfig()->getRepositories(); - $name = RepositoryFactory::generateRepositoryName(0, $repoConfig, $composerJsonRepositoriesConfig); - $configSource = new JsonConfigSource(new JsonFile('composer.json')); - $configSource->addRepository($name, $repoConfig); - - $composer = Factory::create($io, null, $disablePlugins); } - $composer->getDownloadManager()->setOutputProgress(!$noProgress); + $process = new ProcessExecutor($io); + $fs = new Filesystem($process); - $fs = new Filesystem(); - - if ($noScripts === false) { - // dispatch event - $composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_ROOT_PACKAGE_INSTALL, $installDevPackages); - } + // dispatch event + $composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_ROOT_PACKAGE_INSTALL, $installDevPackages); // use the new config including the newly installed project $config = $composer->getConfig(); @@ -198,17 +218,22 @@ public function installProject(IOInterface $io, Config $config, InputInterface $ // install dependencies of the created project if ($noInstall === false) { + $composer->getInstallationManager()->setOutputProgress(!$noProgress); + $installer = Installer::create($io, $composer); $installer->setPreferSource($preferSource) ->setPreferDist($preferDist) ->setDevMode($installDevPackages) - ->setRunScripts(!$noScripts) ->setIgnorePlatformRequirements($ignorePlatformReqs) ->setSuggestedPackagesReporter($this->suggestedPackagesReporter) ->setOptimizeAutoloader($config->get('optimize-autoloader')) ->setClassMapAuthoritative($config->get('classmap-authoritative')) ->setApcuAutoloader($config->get('apcu-autoloader')); + if (!$composer->getLocker()->isLocked()) { + $installer->setUpdate(true); + } + if ($disablePlugins) { $installer->disablePlugins(); } @@ -226,7 +251,7 @@ public function installProject(IOInterface $io, Config $config, InputInterface $ && ( $input->getOption('remove-vcs') || !$io->isInteractive() - || $io->askConfirmation('Do you want to remove the existing VCS (.git, .svn..) history? [Y,n]? ', true) + || $io->askConfirmation('Do you want to remove the existing VCS (.git, .svn..) history? [Y,n]? ') ) ) { $finder = new Finder(); @@ -263,10 +288,8 @@ public function installProject(IOInterface $io, Config $config, InputInterface $ } } - if ($noScripts === false) { - // dispatch event - $composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_CREATE_PROJECT_CMD, $installDevPackages); - } + // dispatch event + $composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_CREATE_PROJECT_CMD, $installDevPackages); chdir($oldCwd); $vendorComposerDir = $config->get('vendor-dir').'/composer'; @@ -281,22 +304,12 @@ public function installProject(IOInterface $io, Config $config, InputInterface $ return 0; } - protected function installRootPackage(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repository = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $ignorePlatformReqs = false, $secureHttp = true) + protected function installRootPackage(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, array $repositories = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $ignorePlatformReqs = false, $secureHttp = true) { if (!$secureHttp) { $config->merge(array('config' => array('secure-http' => false))); } - $composer = Factory::create($io, $config->all(), $disablePlugins); - $config = $composer->getConfig(); - $rm = $composer->getRepositoryManager(); - - if (null === $repository) { - $sourceRepo = new CompositeRepository(RepositoryFactory::defaultRepos($io, $config, $rm)); - } else { - $sourceRepo = RepositoryFactory::fromString($io, $config, $repository, true, $rm); - } - $parser = new VersionParser(); $requirements = $parser->parseNameVersionPairs(array($packageName)); $name = strtolower($requirements[0]['name']); @@ -310,7 +323,8 @@ protected function installRootPackage(IOInterface $io, Config $config, $packageN $directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts); } - $fs = new Filesystem(); + $process = new ProcessExecutor($io); + $fs = new Filesystem($process); if (!$fs->isAbsolutePath($directory)) { $directory = getcwd() . DIRECTORY_SEPARATOR . $directory; } @@ -320,13 +334,16 @@ protected function installRootPackage(IOInterface $io, Config $config, $packageN if (file_exists($directory)) { if (!is_dir($directory)) { throw new \InvalidArgumentException('Cannot create project directory at "'.$directory.'", it exists as a file.'); - } elseif (!$fs->isDirEmpty($directory)) { + } + if (!$fs->isDirEmpty($directory)) { throw new \InvalidArgumentException('Project directory "'.$directory.'" is not empty.'); } } if (null === $stability) { - if (preg_match('{^[^,\s]*?@('.implode('|', array_keys(BasePackage::$stabilities)).')$}i', $packageVersion, $match)) { + if (null === $packageVersion) { + $stability = 'stable'; + } elseif (preg_match('{^[^,\s]*?@('.implode('|', array_keys(BasePackage::$stabilities)).')$}i', $packageVersion, $match)) { $stability = $match[1]; } else { $stability = VersionParser::parseStability($packageVersion); @@ -339,28 +356,37 @@ protected function installRootPackage(IOInterface $io, Config $config, $packageN throw new \InvalidArgumentException('Invalid stability provided ('.$stability.'), must be one of: '.implode(', ', array_keys(BasePackage::$stabilities))); } - $pool = new Pool($stability); - $pool->addRepository($sourceRepo); - - $phpVersion = null; - $prettyPhpVersion = null; - if (!$ignorePlatformReqs) { - $platformOverrides = $config->get('platform') ?: array(); - // initialize $this->repos as it is used by the parent InitCommand - $platform = new PlatformRepository(array(), $platformOverrides); - $phpPackage = $platform->findPackage('php', '*'); - $phpVersion = $phpPackage->getVersion(); - $prettyPhpVersion = $phpPackage->getPrettyVersion(); + $composer = Factory::create($io, $config->all(), $disablePlugins); + $config = $composer->getConfig(); + $rm = $composer->getRepositoryManager(); + + $repositorySet = new RepositorySet($stability); + if (null === $repositories) { + $repositorySet->addRepository(new CompositeRepository(RepositoryFactory::defaultRepos($io, $config, $rm))); + } else { + foreach ($repositories as $repo) { + $repoConfig = RepositoryFactory::configFromString($io, $config, $repo, true); + if ( + (isset($repoConfig['packagist']) && $repoConfig === array('packagist' => false)) + || (isset($repoConfig['packagist.org']) && $repoConfig === array('packagist.org' => false)) + ) { + continue; + } + $repositorySet->addRepository(RepositoryFactory::createRepo($io, $config, $repoConfig, $rm)); + } } + $platformOverrides = $config->get('platform') ?: array(); + $platformRepo = new PlatformRepository(array(), $platformOverrides); + // find the latest version if there are multiple - $versionSelector = new VersionSelector($pool); - $package = $versionSelector->findBestCandidate($name, $packageVersion, $phpVersion, $stability); + $versionSelector = new VersionSelector($repositorySet, $platformRepo); + $package = $versionSelector->findBestCandidate($name, $packageVersion, $stability, $ignorePlatformReqs); if (!$package) { $errorMessage = "Could not find package $name with " . ($packageVersion ? "version $packageVersion" : "stability $stability"); - if ($phpVersion && $versionSelector->findBestCandidate($name, $packageVersion, null, $stability)) { - throw new \InvalidArgumentException($errorMessage .' in a version installable using your PHP version '.$prettyPhpVersion.'.'); + if (true !== $ignorePlatformReqs && $versionSelector->findBestCandidate($name, $packageVersion, $stability, true)) { + throw new \InvalidArgumentException($errorMessage .' in a version installable using your PHP version, PHP extensions and Composer version.'); } throw new \InvalidArgumentException($errorMessage .'.'); @@ -378,6 +404,22 @@ protected function installRootPackage(IOInterface $io, Config $config, $packageN }); } } + // handler Ctrl+C for Windows on PHP 7.4+ + if (function_exists('sapi_windows_set_ctrl_handler') && PHP_SAPI === 'cli') { + @mkdir($directory, 0777, true); + if ($realDir = realpath($directory)) { + sapi_windows_set_ctrl_handler(function () use ($realDir) { + $fs = new Filesystem(); + $fs->removeDirectory($realDir); + exit(130); + }); + } + } + + // avoid displaying 9999999-dev as version if default-branch was selected + if ($package instanceof AliasPackage && $package->getPrettyVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) { + $package = $package->getAliasOf(); + } $io->writeError('Installing ' . $package->getName() . ' (' . $package->getFullPrettyVersion(false) . ')'); @@ -391,13 +433,13 @@ protected function installRootPackage(IOInterface $io, Config $config, $packageN $dm = $composer->getDownloadManager(); $dm->setPreferSource($preferSource) - ->setPreferDist($preferDist) - ->setOutputProgress(!$noProgress); + ->setPreferDist($preferDist); - $projectInstaller = new ProjectInstaller($directory, $dm); + $projectInstaller = new ProjectInstaller($directory, $dm, $fs); $im = $composer->getInstallationManager(); + $im->setOutputProgress(!$noProgress); $im->addInstaller($projectInstaller); - $im->install(new InstalledFilesystemRepository(new JsonFile('php://memory')), new InstallOperation($package)); + $im->execute(new InstalledArrayRepository(), array(new InstallOperation($package))); $im->notifyInstalls($io); // collect suggestions @@ -408,8 +450,7 @@ protected function installRootPackage(IOInterface $io, Config $config, $packageN $io->writeError('Created project in ' . $directory . ''); chdir($directory); - $_SERVER['COMPOSER_ROOT_VERSION'] = $package->getPrettyVersion(); - putenv('COMPOSER_ROOT_VERSION='.$_SERVER['COMPOSER_ROOT_VERSION']); + Platform::putEnv('COMPOSER_ROOT_VERSION', $package->getPrettyVersion()); return $installedFromVcs; } diff --git a/app/vendor/composer/composer/src/Composer/Command/DependsCommand.php b/app/vendor/composer/composer/src/Composer/Command/DependsCommand.php index c350fde9b..c1ebe7eab 100644 --- a/app/vendor/composer/composer/src/Composer/Command/DependsCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/DependsCommand.php @@ -14,6 +14,8 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; /** * @author Niels Keurentjes @@ -25,12 +27,15 @@ class DependsCommand extends BaseDependencyCommand */ protected function configure() { - parent::configure(); - $this ->setName('depends') ->setAliases(array('why')) ->setDescription('Shows which packages cause the given package to be installed.') + ->setDefinition(array( + new InputArgument(self::ARGUMENT_PACKAGE, InputArgument::REQUIRED, 'Package to inspect'), + new InputOption(self::OPTION_RECURSIVE, 'r', InputOption::VALUE_NONE, 'Recursively resolves up to the root package'), + new InputOption(self::OPTION_TREE, 't', InputOption::VALUE_NONE, 'Prints the results as a nested tree'), + )) ->setHelp( << */ class DiagnoseCommand extends BaseCommand { - /** @var RemoteFilesystem */ - protected $rfs; + /** @var HttpDownloader */ + protected $httpDownloader; /** @var ProcessExecutor */ protected $process; @@ -86,7 +90,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $config->merge(array('config' => array('secure-http' => false))); $config->prohibitUrlByConfig('http://repo.packagist.org', new NullIO); - $this->rfs = Factory::createRemoteFilesystem($io, $config); + $this->httpDownloader = Factory::createHttpDownloader($io, $config); $this->process = new ProcessExecutor($io); $io->write('Checking platform settings: ', false); @@ -105,10 +109,6 @@ protected function execute(InputInterface $input, OutputInterface $output) if (!empty($opts['http']['proxy'])) { $io->write('Checking HTTP proxy: ', false); $this->outputResult($this->checkHttpProxy()); - $io->write('Checking HTTP proxy support for request_fulluri: ', false); - $this->outputResult($this->checkHttpProxyFullUriRequestParam()); - $io->write('Checking HTTPS proxy support for request_fulluri: ', false); - $this->outputResult($this->checkHttpsProxyFullUriRequestParam()); } if ($oauth = $config->get('github-oauth')) { @@ -148,7 +148,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $io->write('Checking disk free space: ', false); $this->outputResult($this->checkDiskSpace($config)); - if ('phar:' === substr(__FILE__, 0, 5)) { + if (strpos(__FILE__, 'phar:') === 0) { $io->write('Checking pubkeys: ', false); $this->outputResult($this->checkPubKeys($config)); @@ -156,13 +156,13 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->outputResult($this->checkVersion($config)); } - $io->write(sprintf('Composer version: %s', Composer::VERSION)); + $io->write(sprintf('Composer version: %s', Composer::getVersion())); $platformOverrides = $config->get('platform') ?: array(); $platformRepo = new PlatformRepository(array(), $platformOverrides); $phpPkg = $platformRepo->findPackage('php', '*'); $phpVersion = $phpPkg->getPrettyVersion(); - if (false !== strpos($phpPkg->getDescription(), 'overridden')) { + if ($phpPkg instanceof CompletePackageInterface && false !== strpos($phpPkg->getDescription(), 'overridden')) { $phpVersion .= ' - ' . $phpPkg->getDescription(); } @@ -173,6 +173,24 @@ protected function execute(InputInterface $input, OutputInterface $output) } $io->write('OpenSSL version: ' . (defined('OPENSSL_VERSION_TEXT') ? ''.OPENSSL_VERSION_TEXT.'' : 'missing')); + $io->write('cURL version: ' . $this->getCurlVersion()); + + $finder = new ExecutableFinder; + $hasSystemUnzip = (bool) $finder->find('unzip'); + $bin7zip = ''; + if ($hasSystem7zip = (bool) $finder->find('7z', null, array('C:\Program Files\7-Zip'))) { + $bin7zip = '7z'; + } + if (!Platform::isWindows() && !$hasSystem7zip && $hasSystem7zip = (bool) $finder->find('7zz')) { + $bin7zip = '7zz'; + } + + $io->write( + 'zip: ' . (extension_loaded('zip') ? 'extension present' : 'extension not loaded') + . ', ' . ($hasSystemUnzip ? 'unzip present' : 'unzip not available') + . ', ' . ($hasSystem7zip ? '7-Zip present ('.$bin7zip.')' : '7-Zip not available') + . (($hasSystem7zip || $hasSystemUnzip) && !function_exists('proc_open') ? ', proc_open is disabled or not present, unzip/7-z will not be usable' : '') + ); return $this->exitCode; } @@ -203,6 +221,10 @@ private function checkComposerSchema() private function checkGit() { + if (!function_exists('proc_open')) { + return 'proc_open is not available, git cannot be used'; + } + $this->process->execute('git config color.ui', $output); if (strtolower(trim($output)) === 'always') { return 'Your git color.ui setting is set to always, this is known to create issues. Use "git config --global color.ui true" to set it correctly.'; @@ -218,26 +240,25 @@ private function checkHttp($proto, Config $config) return $result; } - $disableTls = false; $result = array(); if ($proto === 'https' && $config->get('disable-tls') === true) { - $disableTls = true; - $result[] = 'Composer is configured to disable SSL/TLS protection. This will leave remote HTTPS requests vulnerable to Man-In-The-Middle attacks.'; - } - if ($proto === 'https' && !extension_loaded('openssl') && !$disableTls) { - $result[] = 'Composer is configured to use SSL/TLS protection but the openssl extension is not available.'; + $tlsWarning = 'Composer is configured to disable SSL/TLS protection. This will leave remote HTTPS requests vulnerable to Man-In-The-Middle attacks.'; } try { - $this->rfs->getContents('packagist.org', $proto . '://repo.packagist.org/packages.json', false); + $this->httpDownloader->get($proto . '://repo.packagist.org/packages.json'); } catch (TransportException $e) { - if (false !== strpos($e->getMessage(), 'cafile')) { - $result[] = '[' . get_class($e) . '] ' . $e->getMessage() . ''; - $result[] = 'Unable to locate a valid CA certificate file. You must set a valid \'cafile\' option.'; - $result[] = 'You can alternatively disable this error, at your own risk, by enabling the \'disable-tls\' option.'; - } else { - array_unshift($result, '[' . get_class($e) . '] ' . $e->getMessage()); + if ($hints = HttpDownloader::getExceptionHints($e)) { + foreach ($hints as $hint) { + $result[] = $hint; + } } + + $result[] = '[' . get_class($e) . '] ' . $e->getMessage() . ''; + } + + if (isset($tlsWarning)) { + $result[] = $tlsWarning; } if (count($result) > 0) { @@ -256,11 +277,11 @@ private function checkHttpProxy() $protocol = extension_loaded('openssl') ? 'https' : 'http'; try { - $json = json_decode($this->rfs->getContents('packagist.org', $protocol . '://repo.packagist.org/packages.json', false), true); + $json = $this->httpDownloader->get($protocol . '://repo.packagist.org/packages.json')->decodeJson(); $hash = reset($json['provider-includes']); $hash = $hash['sha256']; $path = str_replace('%hash%', $hash, key($json['provider-includes'])); - $provider = $this->rfs->getContents('packagist.org', $protocol . '://repo.packagist.org/'.$path, false); + $provider = $this->httpDownloader->get($protocol . '://repo.packagist.org/'.$path)->getBody(); if (hash('sha256', $provider) !== $hash) { return 'It seems that your proxy is modifying http traffic on the fly'; @@ -272,70 +293,6 @@ private function checkHttpProxy() return true; } - /** - * Due to various proxy servers configurations, some servers can't handle non-standard HTTP "http_proxy_request_fulluri" parameter, - * and will return error 500/501 (as not implemented), see discussion @ https://github.com/composer/composer/pull/1825. - * This method will test, if you need to disable this parameter via setting extra environment variable in your system. - * - * @return bool|string - */ - private function checkHttpProxyFullUriRequestParam() - { - $result = $this->checkConnectivity(); - if ($result !== true) { - return $result; - } - - $url = 'http://repo.packagist.org/packages.json'; - try { - $this->rfs->getContents('packagist.org', $url, false); - } catch (TransportException $e) { - try { - $this->rfs->getContents('packagist.org', $url, false, array('http' => array('request_fulluri' => false))); - } catch (TransportException $e) { - return 'Unable to assess the situation, maybe packagist.org is down ('.$e->getMessage().')'; - } - - return 'It seems there is a problem with your proxy server, try setting the "HTTP_PROXY_REQUEST_FULLURI" and "HTTPS_PROXY_REQUEST_FULLURI" environment variables to "false"'; - } - - return true; - } - - /** - * Due to various proxy servers configurations, some servers can't handle non-standard HTTP "http_proxy_request_fulluri" parameter, - * and will return error 500/501 (as not implemented), see discussion @ https://github.com/composer/composer/pull/1825. - * This method will test, if you need to disable this parameter via setting extra environment variable in your system. - * - * @return bool|string - */ - private function checkHttpsProxyFullUriRequestParam() - { - $result = $this->checkConnectivity(); - if ($result !== true) { - return $result; - } - - if (!extension_loaded('openssl')) { - return 'You need the openssl extension installed for this check'; - } - - $url = 'https://api.github.com/repos/Seldaek/jsonlint/zipball/1.0.0'; - try { - $this->rfs->getContents('github.com', $url, false); - } catch (TransportException $e) { - try { - $this->rfs->getContents('github.com', $url, false, array('http' => array('request_fulluri' => false))); - } catch (TransportException $e) { - return 'Unable to assess the situation, maybe github is down ('.$e->getMessage().')'; - } - - return 'It seems there is a problem with your proxy server, try setting the "HTTPS_PROXY_REQUEST_FULLURI" environment variable to "false"'; - } - - return true; - } - private function checkGithubOauth($domain, $token) { $result = $this->checkConnectivity(); @@ -347,9 +304,11 @@ private function checkGithubOauth($domain, $token) try { $url = $domain === 'github.com' ? 'https://api.'.$domain.'/' : 'https://'.$domain.'/api/v3/'; - return $this->rfs->getContents($domain, $url, false, array( + $this->httpDownloader->get($url, array( 'retry-auth-failure' => false, - )) ? true : 'Unexpected error'; + )); + + return true; } catch (\Exception $e) { if ($e instanceof TransportException && $e->getCode() === 401) { return 'The oauth token for '.$domain.' seems invalid, run "composer config --global --unset github-oauth.'.$domain.'" to remove it'; @@ -377,8 +336,7 @@ private function getGithubRateLimit($domain, $token = null) } $url = $domain === 'github.com' ? 'https://api.'.$domain.'/rate_limit' : 'https://'.$domain.'/api/rate_limit'; - $json = $this->rfs->getContents($domain, $url, false, array('retry-auth-failure' => false)); - $data = json_decode($json, true); + $data = $this->httpDownloader->get($url, array('retry-auth-failure' => false))->decodeJson(); return $data['resources']['core']; } @@ -431,7 +389,7 @@ private function checkVersion($config) return $result; } - $versionsUtil = new Versions($config, $this->rfs); + $versionsUtil = new Versions($config, $this->httpDownloader); try { $latest = $versionsUtil->getLatest(); } catch (\Exception $e) { @@ -445,8 +403,25 @@ private function checkVersion($config) return true; } + private function getCurlVersion() + { + if (extension_loaded('curl')) { + if (!HttpDownloader::isCurlEnabled()) { + return 'disabled via disable_functions, using php streams fallback, which reduces performance'; + } + + $version = curl_version(); + + return ''.$version['version'].' '. + 'libz '.(isset($version['libz_version']) ? $version['libz_version'] : 'missing').' '. + 'ssl '.(isset($version['ssl_version']) ? $version['ssl_version'] : 'missing').''; + } + + return 'missing, using php streams fallback, which reduces performance'; + } + /** - * @param bool|string|\Exception $result + * @param bool|string|string[]|\Exception $result */ private function outputResult($result) { @@ -578,7 +553,7 @@ private function checkPlatform() if (filter_var(ini_get('xdebug.profiler_enabled'), FILTER_VALIDATE_BOOLEAN)) { $warnings['xdebug_profile'] = true; - } elseif (extension_loaded('xdebug')) { + } elseif (XdebugHandler::isXdebugActive()) { $warnings['xdebug_loaded'] = true; } @@ -617,20 +592,6 @@ private function checkPlatform() $text .= "Install either of them or recompile php without --disable-iconv"; break; - case 'unicode': - $text = PHP_EOL."The detect_unicode setting must be disabled.".PHP_EOL; - $text .= "Add the following to the end of your `php.ini`:".PHP_EOL; - $text .= " detect_unicode = Off"; - $displayIniMessage = true; - break; - - case 'suhosin': - $text = PHP_EOL."The suhosin.executor.include.whitelist setting is incorrect.".PHP_EOL; - $text .= "Add the following to the end of your `php.ini` or suhosin.ini (Example path [for Debian]: /etc/php5/cli/conf.d/suhosin.ini):".PHP_EOL; - $text .= " suhosin.executor.include.whitelist = phar ".$current; - $displayIniMessage = true; - break; - case 'php': $text = PHP_EOL."Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.2 or higher."; break; @@ -653,6 +614,9 @@ private function checkPlatform() $text = PHP_EOL."The openssl extension is missing, which means that secure HTTPS transfers are impossible.".PHP_EOL; $text .= "If possible you should enable it or recompile php with --with-openssl"; break; + + default: + throw new \InvalidArgumentException(sprintf("DiagnoseCommand: Unknown error type \"%s\". Please report at https://github.com/composer/composer/issues/new.", $error)); } $out($text, 'error'); } @@ -717,6 +681,9 @@ private function checkPlatform() $text = "The Windows OneDrive folder is not supported on PHP versions below 7.2.23 and 7.3.10.".PHP_EOL; $text .= "Upgrade your PHP ({$current}) to use this location with Composer.".PHP_EOL; break; + + default: + throw new \InvalidArgumentException(sprintf("DiagnoseCommand: Unknown warning type \"%s\". Please report at https://github.com/composer/composer/issues/new.", $warning)); } $out($text, 'comment'); } @@ -729,17 +696,15 @@ private function checkPlatform() return !$warnings && !$errors ? true : $output; } - /** * Check if allow_url_fopen is ON * - * @return bool|string + * @return true|string */ private function checkConnectivity() { if (!ini_get('allow_url_fopen')) { - $result = 'Skipped because allow_url_fopen is missing.'; - return $result; + return 'Skipped because allow_url_fopen is missing.'; } return true; diff --git a/app/vendor/composer/composer/src/Composer/Command/DumpAutoloadCommand.php b/app/vendor/composer/composer/src/Composer/Command/DumpAutoloadCommand.php index 9627b2a88..c4b0b9926 100644 --- a/app/vendor/composer/composer/src/Composer/Command/DumpAutoloadCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/DumpAutoloadCommand.php @@ -34,7 +34,11 @@ protected function configure() new InputOption('optimize', 'o', InputOption::VALUE_NONE, 'Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize`.'), new InputOption('apcu', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), - new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables autoload-dev rules.'), + new InputOption('apcu-prefix', null, InputOption::VALUE_REQUIRED, 'Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu'), + new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables autoload-dev rules. Composer will by default infer this automatically according to the last install or update --no-dev state.'), + new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables autoload-dev rules. Composer will by default infer this automatically according to the last install or update --no-dev state.'), + new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'), )) ->setHelp( <<getComposer(); + $composer->getEventDispatcher()->setRunScripts(!$input->getOption('no-scripts')); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'dump-autoload', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); @@ -60,7 +65,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $optimize = $input->getOption('optimize') || $config->get('optimize-autoloader'); $authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative'); - $apcu = $input->getOption('apcu') || $config->get('apcu-autoloader'); + $apcuPrefix = $input->getOption('apcu-prefix'); + $apcu = $apcuPrefix !== null || $input->getOption('apcu') || $config->get('apcu-autoloader'); if ($authoritative) { $this->getIO()->write('Generating optimized autoload files (authoritative)'); @@ -70,11 +76,22 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->getIO()->write('Generating autoload files'); } + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); + $generator = $composer->getAutoloadGenerator(); - $generator->setDevMode(!$input->getOption('no-dev')); + if ($input->getOption('no-dev')) { + $generator->setDevMode(false); + } + if ($input->getOption('dev')) { + if ($input->getOption('no-dev')) { + throw new \InvalidArgumentException('You can not use both --no-dev and --dev as they conflict with each other.'); + } + $generator->setDevMode(true); + } $generator->setClassMapAuthoritative($authoritative); - $generator->setApcu($apcu); - $generator->setRunScripts(!$input->getOption('no-scripts')); + $generator->setRunScripts(true); + $generator->setApcu($apcu, $apcuPrefix); + $generator->setIgnorePlatformRequirements($ignorePlatformReqs); $numberOfClasses = $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize); if ($authoritative) { diff --git a/app/vendor/composer/composer/src/Composer/Command/ExecCommand.php b/app/vendor/composer/composer/src/Composer/Command/ExecCommand.php index fba9e7e8a..167a2be0d 100644 --- a/app/vendor/composer/composer/src/Composer/Command/ExecCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/ExecCommand.php @@ -88,9 +88,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $dispatcher = $composer->getEventDispatcher(); $dispatcher->addListener('__exec_command', $binary); - if ($output->getVerbosity() === OutputInterface::VERBOSITY_NORMAL) { - $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); - } // If the CWD was modified, we restore it to what it was initially, as it was // most likely modified by the global command, and we want exec to run in the local working directory diff --git a/app/vendor/composer/composer/src/Composer/Command/FundCommand.php b/app/vendor/composer/composer/src/Composer/Command/FundCommand.php index eb60ec577..edba9e806 100644 --- a/app/vendor/composer/composer/src/Composer/Command/FundCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/FundCommand.php @@ -12,9 +12,12 @@ namespace Composer\Command; -use Composer\Package\CompletePackageInterface; +use Composer\Json\JsonFile; use Composer\Package\AliasPackage; +use Composer\Package\BasePackage; +use Composer\Package\CompletePackageInterface; use Composer\Repository\CompositeRepository; +use Composer\Semver\Constraint\MatchAllConstraint; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -29,6 +32,9 @@ protected function configure() { $this->setName('fund') ->setDescription('Discover how to help fund the maintenance of your dependencies.') + ->setDefinition(array( + new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'), + )) ; } @@ -39,15 +45,38 @@ protected function execute(InputInterface $input, OutputInterface $output) $repo = $composer->getRepositoryManager()->getLocalRepository(); $remoteRepos = new CompositeRepository($composer->getRepositoryManager()->getRepositories()); $fundings = array(); + + $packagesToLoad = array(); foreach ($repo->getPackages() as $package) { if ($package instanceof AliasPackage) { continue; } - $latest = $remoteRepos->findPackage($package->getName(), 'dev-master'); - if ($latest instanceof CompletePackageInterface && $latest->getFunding()) { - $fundings = $this->insertFundingData($fundings, $latest); + $packagesToLoad[$package->getName()] = new MatchAllConstraint(); + } + + // load all packages dev versions in parallel + $result = $remoteRepos->loadPackages($packagesToLoad, array('dev' => BasePackage::STABILITY_DEV), array()); + + // collect funding data from default branches + foreach ($result['packages'] as $package) { + if ( + !$package instanceof AliasPackage + && $package instanceof CompletePackageInterface + && $package->isDefaultBranch() + && $package->getFunding() + && isset($packagesToLoad[$package->getName()]) + ) { + $fundings = $this->insertFundingData($fundings, $package); + unset($packagesToLoad[$package->getName()]); + } + } + + // collect funding from installed packages if none was found in the default branch above + foreach ($repo->getPackages() as $package) { + if ($package instanceof AliasPackage || !isset($packagesToLoad[$package->getName()])) { continue; } + if ($package instanceof CompletePackageInterface && $package->getFunding()) { $fundings = $this->insertFundingData($fundings, $package); } @@ -57,7 +86,14 @@ protected function execute(InputInterface $input, OutputInterface $output) $io = $this->getIO(); - if ($fundings) { + $format = $input->getOption('format'); + if (!in_array($format, array('text', 'json'))) { + $io->writeError(sprintf('Unsupported format "%s". See help for supported formats.', $format)); + + return 1; + } + + if ($fundings && $format === 'text') { $prev = null; $io->write('The following packages were found in your dependencies which publish funding information:'); @@ -80,6 +116,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $io->write(""); $io->write("Please consider following these links and sponsoring the work of package authors!"); $io->write("Thank you!"); + } elseif ($format === 'json') { + $io->write(JsonFile::encode($fundings)); } else { $io->write("No funding links were found in your package dependencies. This doesn't mean they don't need your support!"); } diff --git a/app/vendor/composer/composer/src/Composer/Command/GlobalCommand.php b/app/vendor/composer/composer/src/Composer/Command/GlobalCommand.php index 958643dee..c78ea7109 100644 --- a/app/vendor/composer/composer/src/Composer/Command/GlobalCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/GlobalCommand.php @@ -14,6 +14,7 @@ use Composer\Factory; use Composer\Util\Filesystem; +use Composer\Util\Platform; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\StringInput; @@ -58,6 +59,10 @@ protected function configure() public function run(InputInterface $input, OutputInterface $output) { + if (!method_exists($input, '__toString')) { + throw new \LogicException('Expected an Input instance that is stringable, got '.get_class($input)); + } + // extract real command name $tokens = preg_split('{\s+}', $input->__toString()); $args = array(); @@ -77,8 +82,7 @@ public function run(InputInterface $input, OutputInterface $output) // The COMPOSER env var should not apply to the global execution scope if (getenv('COMPOSER')) { - putenv('COMPOSER'); - unset($_SERVER['COMPOSER']); + Platform::clearEnv('COMPOSER'); } // change to global dir diff --git a/app/vendor/composer/composer/src/Composer/Command/HomeCommand.php b/app/vendor/composer/composer/src/Composer/Command/HomeCommand.php index b7d907066..1a228b871 100644 --- a/app/vendor/composer/composer/src/Composer/Command/HomeCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/HomeCommand.php @@ -14,7 +14,7 @@ use Composer\Package\CompletePackageInterface; use Composer\Repository\RepositoryInterface; -use Composer\Repository\ArrayRepository; +use Composer\Repository\RootPackageRepository; use Composer\Repository\RepositoryFactory; use Composer\Util\Platform; use Composer\Util\ProcessExecutor; @@ -129,7 +129,7 @@ private function openBrowser($url) $process = new ProcessExecutor($this->getIO()); if (Platform::isWindows()) { - return $process->execute('start "web" explorer "' . $url . '"', $output); + return $process->execute('start "web" explorer ' . $url, $output); } $linux = $process->execute('which xdg-open', $output); @@ -157,7 +157,7 @@ private function initializeRepos() if ($composer) { return array_merge( - array(new ArrayRepository(array($composer->getPackage()))), // root package + array(new RootPackageRepository($composer->getPackage())), // root package array($composer->getRepositoryManager()->getLocalRepository()), // installed packages $composer->getRepositoryManager()->getRepositories() // remotes ); diff --git a/app/vendor/composer/composer/src/Composer/Command/InitCommand.php b/app/vendor/composer/composer/src/Composer/Command/InitCommand.php index 1eacb2bff..c8888f07e 100644 --- a/app/vendor/composer/composer/src/Composer/Command/InitCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/InitCommand.php @@ -12,23 +12,30 @@ namespace Composer\Command; -use Composer\DependencyResolver\Pool; use Composer\Factory; use Composer\Json\JsonFile; +use Composer\Json\JsonValidationException; use Composer\Package\BasePackage; +use Composer\Package\CompletePackageInterface; use Composer\Package\Package; +use Composer\Package\PackageInterface; use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionSelector; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryFactory; +use Composer\Repository\RepositorySet; +use Composer\Util\Filesystem; use Composer\Util\ProcessExecutor; +use Composer\Semver\Constraint\Constraint; +use Composer\Util\Silencer; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\ExecutableFinder; use Symfony\Component\Process\Process; +use Symfony\Component\Console\Helper\FormatterHelper; /** * @author Justin Rainbow @@ -36,14 +43,14 @@ */ class InitCommand extends BaseCommand { - /** @var CompositeRepository */ + /** @var ?CompositeRepository */ protected $repos; - /** @var array */ + /** @var array */ private $gitConfig; - /** @var Pool[] */ - private $pools; + /** @var RepositorySet[] */ + private $repositorySets; /** * {@inheritdoc} @@ -65,6 +72,7 @@ protected function configure() new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum stability (empty or one of: '.implode(', ', array_keys(BasePackage::$stabilities)).')'), new InputOption('license', 'l', InputOption::VALUE_REQUIRED, 'License of package'), new InputOption('repository', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Add custom repositories, either by URL or using JSON arrays'), + new InputOption('autoload', 'a', InputOption::VALUE_REQUIRED, 'Add PSR-4 autoload mapping. Maps your package\'s namespace to the provided directory. (Expects a relative path, e.g. src/)'), )) ->setHelp( <<getIO(); - $allowList = array('name', 'description', 'author', 'type', 'homepage', 'require', 'require-dev', 'stability', 'license'); - $options = array_filter(array_intersect_key($input->getOptions(), array_flip($allowList))); + $allowlist = array('name', 'description', 'author', 'type', 'homepage', 'require', 'require-dev', 'stability', 'license', 'autoload'); + $options = array_filter(array_intersect_key($input->getOptions(), array_flip($allowlist))); + + if (isset($options['name']) && !preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}D', $options['name'])) { + throw new \InvalidArgumentException( + 'The package name '.$options['name'].' is invalid, it should be lowercase and have a vendor name, a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+' + ); + } if (isset($options['author'])) { $options['authors'] = $this->formatAuthors($options['author']); @@ -98,7 +112,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if ($repositories) { $config = Factory::createConfig($io); foreach ($repositories as $repo) { - $options['repositories'][] = RepositoryFactory::configFromString($io, $config, $repo); + $options['repositories'][] = RepositoryFactory::configFromString($io, $config, $repo, true); } } @@ -119,19 +133,58 @@ protected function execute(InputInterface $input, OutputInterface $output) } } + // --autoload - create autoload object + $autoloadPath = null; + if (isset($options['autoload'])) { + $autoloadPath = $options['autoload']; + $namespace = $this->namespaceFromPackageName($input->getOption('name')); + $options['autoload'] = (object) array( + 'psr-4' => array( + $namespace . '\\' => $autoloadPath, + ), + ); + } + $file = new JsonFile(Factory::getComposerFile()); - $json = $file->encode($options); + $json = JsonFile::encode($options); if ($input->isInteractive()) { $io->writeError(array('', $json, '')); - if (!$io->askConfirmation('Do you confirm generation [yes]? ', true)) { + if (!$io->askConfirmation('Do you confirm generation [yes]? ')) { $io->writeError('Command aborted'); return 1; } + } else { + if (json_encode($options) === '{"require":{}}') { + throw new \RuntimeException('You have to run this command in interactive mode, or specify at least some data using --name, --require, etc.'); + } + + $io->writeError('Writing '.$file->getPath()); } $file->write($options); + try { + $file->validateSchema(JsonFile::LAX_SCHEMA); + } catch (JsonValidationException $e) { + $io->writeError('Schema validation error, aborting'); + $errors = ' - ' . implode(PHP_EOL . ' - ', $e->getErrors()); + $io->writeError($e->getMessage() . ':' . PHP_EOL . $errors); + Silencer::call('unlink', $file->getPath()); + + return 1; + } + + // --autoload - Create src folder + if ($autoloadPath) { + $filesystem = new Filesystem(); + $filesystem->ensureDirectoryExists($autoloadPath); + + // dump-autoload only for projects without added dependencies. + if (!$this->hasDependencies($options)) { + $this->runDumpAutoloadCommand($output); + } + } if ($input->isInteractive() && is_dir('.git')) { $ignoreFile = realpath('.gitignore'); @@ -143,15 +196,23 @@ protected function execute(InputInterface $input, OutputInterface $output) if (!$this->hasVendorIgnore($ignoreFile)) { $question = 'Would you like the vendor directory added to your .gitignore [yes]? '; - if ($io->askConfirmation($question, true)) { + if ($io->askConfirmation($question)) { $this->addVendorIgnore($ignoreFile); } } } $question = 'Would you like to install dependencies now [yes]? '; - if ($input->isInteractive() && $this->hasDependencies($options) && $io->askConfirmation($question, true)) { - $this->installDependencies($output); + if ($input->isInteractive() && $this->hasDependencies($options) && $io->askConfirmation($question)) { + $this->updateDependencies($output); + } + + // --autoload - Show post-install configuration info + if ($autoloadPath) { + $namespace = $this->namespaceFromPackageName($input->getOption('name')); + + $io->writeError('PSR-4 autoloading configured. Use "namespace '.$namespace.';" in '.$autoloadPath); + $io->writeError('Include the Composer autoloader with: require \'vendor/autoload.php\';'); } return 0; @@ -164,6 +225,7 @@ protected function interact(InputInterface $input, OutputInterface $output) { $git = $this->getGitConfig(); $io = $this->getIO(); + /** @var FormatterHelper $formatter */ $formatter = $this->getHelperSet()->get('formatter'); // initialize repos if configured @@ -173,7 +235,7 @@ protected function interact(InputInterface $input, OutputInterface $output) $repos = array(new PlatformRepository); $createDefaultPackagistRepo = true; foreach ($repositories as $repo) { - $repoConfig = RepositoryFactory::configFromString($io, $config, $repo); + $repoConfig = RepositoryFactory::configFromString($io, $config, $repo, true); if ( (isset($repoConfig['packagist']) && $repoConfig === array('packagist' => false)) || (isset($repoConfig['packagist.org']) && $repoConfig === array('packagist.org' => false)) @@ -229,12 +291,6 @@ protected function interact(InputInterface $input, OutputInterface $output) $name .= '/' . $name; } $name = strtolower($name); - } else { - if (!preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}D', $name)) { - throw new \InvalidArgumentException( - 'The package name '.$name.' is invalid, it should be lowercase and have a vendor name, a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+' - ); - } } $name = $io->askAndValidate( @@ -277,7 +333,7 @@ function ($value) use ($name) { $author_email = $git['user.email']; } - if (isset($author_name) && isset($author_email)) { + if (isset($author_name, $author_email)) { $author = sprintf('%s <%s>', $author_name, $author_email); } } @@ -345,23 +401,61 @@ function ($value) use ($minimumStability) { // prepare to resolve dependencies $repos = $this->getRepos(); $preferredStability = $minimumStability ?: 'stable'; - $phpVersion = $repos->findPackage('php', '*')->getPrettyVersion(); + $platformRepo = null; + if ($repos instanceof CompositeRepository) { + foreach ($repos->getRepositories() as $candidateRepo) { + if ($candidateRepo instanceof PlatformRepository) { + $platformRepo = $candidateRepo; + break; + } + } + } $question = 'Would you like to define your dependencies (require) interactively [yes]? '; $require = $input->getOption('require'); $requirements = array(); - if ($require || $io->askConfirmation($question, true)) { - $requirements = $this->determineRequirements($input, $output, $require, $phpVersion, $preferredStability); + if ($require || $io->askConfirmation($question)) { + $requirements = $this->determineRequirements($input, $output, $require, $platformRepo, $preferredStability); } $input->setOption('require', $requirements); $question = 'Would you like to define your dev dependencies (require-dev) interactively [yes]? '; $requireDev = $input->getOption('require-dev'); $devRequirements = array(); - if ($requireDev || $io->askConfirmation($question, true)) { - $devRequirements = $this->determineRequirements($input, $output, $requireDev, $phpVersion, $preferredStability); + if ($requireDev || $io->askConfirmation($question)) { + $devRequirements = $this->determineRequirements($input, $output, $requireDev, $platformRepo, $preferredStability); } $input->setOption('require-dev', $devRequirements); + + // --autoload - input and validation + $autoload = $input->getOption('autoload') ?: 'src/'; + $namespace = $this->namespaceFromPackageName($input->getOption('name')); + $autoload = $io->askAndValidate( + 'Add PSR-4 autoload mapping? Maps namespace "'.$namespace.'" to the entered relative path. ['.$autoload.', n to skip]: ', + function ($value) use ($autoload) { + if (null === $value) { + return $autoload; + } + + if ($value === 'n' || $value === 'no') { + return; + } + + $value = $value ?: $autoload; + + if (!preg_match('{^[^/][A-Za-z0-9\-_/]+/$}', $value)) { + throw new \InvalidArgumentException(sprintf( + 'The src folder name "%s" is invalid. Please add a relative path with tailing forward slash. [A-Za-z0-9_-/]+/', + $value + )); + } + + return $value; + }, + null, + $autoload + ); + $input->setOption('autoload', $autoload); } /** @@ -403,7 +497,7 @@ protected function getRepos() return $this->repos; } - final protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), $phpVersion = null, $preferredStability = 'stable', $checkProvidedVersions = true, $fixed = false) + final protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), PlatformRepository $platformRepo = null, $preferredStability = 'stable', $checkProvidedVersions = true, $fixed = false) { if ($requires) { $requires = $this->normalizeRequirements($requires); @@ -413,7 +507,7 @@ final protected function determineRequirements(InputInterface $input, OutputInte foreach ($requires as $requirement) { if (!isset($requirement['version'])) { // determine the best version automatically - list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $phpVersion, $preferredStability, null, null, $fixed); + list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $platformRepo, $preferredStability, null, null, $fixed); $requirement['version'] = $version; // replace package name from packagist.org @@ -426,7 +520,7 @@ final protected function determineRequirements(InputInterface $input, OutputInte )); } else { // check that the specified version/constraint exists before we proceed - list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $phpVersion, $preferredStability, $checkProvidedVersions ? $requirement['version'] : null, 'dev', $fixed); + list($name) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $platformRepo, $preferredStability, $checkProvidedVersions ? $requirement['version'] : null, 'dev', $fixed); // replace package name from packagist.org $requirement['name'] = $name; @@ -547,7 +641,7 @@ final protected function determineRequirements(InputInterface $input, OutputInte ); if (false === $constraint) { - list($name, $constraint) = $this->findBestVersionAndNameForPackage($input, $package, $phpVersion, $preferredStability); + list(, $constraint) = $this->findBestVersionAndNameForPackage($input, $package, $platformRepo, $preferredStability); $io->writeError(sprintf( 'Using version %s for %s', @@ -574,15 +668,32 @@ protected function formatAuthors($author) return array($this->parseAuthorString($author)); } - protected function formatRequirements(array $requirements) + /** + * Extract namespace from package's vendor name. + * + * new_projects.acme-extra/package-name becomes "NewProjectsAcmeExtra\PackageName" + * + * @param string $packageName + * + * @return string|null + */ + public function namespaceFromPackageName($packageName) { - $requires = array(); - $requirements = $this->normalizeRequirements($requirements); - foreach ($requirements as $requirement) { - $requires[$requirement['name']] = $requirement['version']; + if (!$packageName || strpos($packageName, '/') === false) { + return null; } - return $requires; + $namespace = array_map( + function ($part) { + $part = preg_replace('/[^a-z0-9]/i', ' ', $part); + $part = ucwords($part); + + return str_replace(' ', '', $part); + }, + explode('/', $packageName) + ); + + return join('\\', $namespace); } protected function getGitConfig() @@ -594,10 +705,11 @@ protected function getGitConfig() $finder = new ExecutableFinder(); $gitBin = $finder->find('git'); - // TODO in v3 always call with an array + // TODO in v2.3 always call with an array if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) { $cmd = new Process(array($gitBin, 'config', '-l')); } else { + // @phpstan-ignore-next-line $cmd = new Process(sprintf('%s config -l', ProcessExecutor::escape($gitBin))); } $cmd->run(); @@ -649,20 +761,13 @@ protected function hasVendorIgnore($ignoreFile, $vendor = 'vendor') return false; } - protected function normalizeRequirements(array $requirements) - { - $parser = new VersionParser(); - - return $parser->parseNameVersionPairs($requirements); - } - protected function addVendorIgnore($ignoreFile, $vendor = '/vendor/') { $contents = ""; if (file_exists($ignoreFile)) { $contents = file_get_contents($ignoreFile); - if ("\n" !== substr($contents, 0, -1)) { + if (strpos($contents, "\n") !== 0) { $contents .= "\n"; } } @@ -685,16 +790,16 @@ protected function isValidEmail($email) return false !== filter_var($email, FILTER_VALIDATE_EMAIL); } - private function getPool(InputInterface $input, $minimumStability = null) + private function getRepositorySet(InputInterface $input, $minimumStability = null) { $key = $minimumStability ?: 'default'; - if (!isset($this->pools[$key])) { - $this->pools[$key] = $pool = new Pool($minimumStability ?: $this->getMinimumStability($input)); - $pool->addRepository($this->getRepos()); + if (!isset($this->repositorySets[$key])) { + $this->repositorySets[$key] = $repositorySet = new RepositorySet($minimumStability ?: $this->getMinimumStability($input)); + $repositorySet->addRepository($this->getRepos()); } - return $this->pools[$key]; + return $this->repositorySets[$key]; } private function getMinimumStability(InputInterface $input) @@ -704,7 +809,7 @@ private function getMinimumStability(InputInterface $input) } $file = Factory::getComposerFile(); - if (is_file($file) && is_readable($file) && is_array($composer = json_decode(file_get_contents($file), true))) { + if (is_file($file) && Filesystem::isReadable($file) && is_array($composer = json_decode(file_get_contents($file), true))) { if (!empty($composer['minimum-stability'])) { return VersionParser::normalizeStability($composer['minimum-stability']); } @@ -720,7 +825,7 @@ private function getMinimumStability(InputInterface $input) * * @param InputInterface $input * @param string $name - * @param string|null $phpVersion + * @param PlatformRepository|null $platformRepo * @param string $preferredStability * @param string|null $requiredVersion * @param string $minimumStability @@ -728,61 +833,86 @@ private function getMinimumStability(InputInterface $input) * @throws \InvalidArgumentException * @return array name version */ - private function findBestVersionAndNameForPackage(InputInterface $input, $name, $phpVersion, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null, $fixed = null) + private function findBestVersionAndNameForPackage(InputInterface $input, $name, PlatformRepository $platformRepo = null, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null, $fixed = null) { - // find the latest version allowed in this pool - $versionSelector = new VersionSelector($this->getPool($input, $minimumStability)); - $ignorePlatformReqs = $input->hasOption('ignore-platform-reqs') && $input->getOption('ignore-platform-reqs'); - - // ignore phpVersion if platform requirements are ignored - if ($ignorePlatformReqs) { - $phpVersion = null; + // handle ignore-platform-reqs flag if present + $ignorePlatformReqs = false; + if ($input->hasOption('ignore-platform-reqs') && $input->hasOption('ignore-platform-req')) { + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); } - $package = $versionSelector->findBestCandidate($name, $requiredVersion, $phpVersion, $preferredStability); + // find the latest version allowed in this repo set + $versionSelector = new VersionSelector($this->getRepositorySet($input, $minimumStability), $platformRepo); + $effectiveMinimumStability = $minimumStability ?: $this->getMinimumStability($input); + + $package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $ignorePlatformReqs); if (!$package) { // platform packages can not be found in the pool in versions other than the local platform's has // so if platform reqs are ignored we just take the user's word for it - if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($name, $ignorePlatformReqs))) && PlatformRepository::isPlatformPackage($name)) { return array($name, $requiredVersion ?: '*'); } // Check whether the PHP version was the problem - if ($phpVersion && $versionSelector->findBestCandidate($name, $requiredVersion, null, $preferredStability)) { + if (true !== $ignorePlatformReqs && ($candidate = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, true))) { throw new \InvalidArgumentException(sprintf( - 'Package %s at version %s has a PHP requirement incompatible with your PHP version (%s)', + 'Package %s%s has a PHP requirement incompatible with your PHP version, PHP extensions and Composer version' . $this->getPlatformExceptionDetails($candidate, $platformRepo), $name, - $requiredVersion, - $phpVersion + $requiredVersion ? ' at version '.$requiredVersion : '' + )); + } + // Check whether the minimum stability was the problem but the package exists + if ($package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $ignorePlatformReqs, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES)) { + // we must first verify if a valid package would be found in a lower priority repository + if ($allReposPackage = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $ignorePlatformReqs, RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) { + throw new \InvalidArgumentException( + 'Package '.$name.' exists in '.$allReposPackage->getRepository()->getRepoName().' and '.$package->getRepository()->getRepoName().' which has a higher repository priority. The packages with higher priority do not match your minimum-stability and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.' + ); + } + + throw new \InvalidArgumentException(sprintf( + 'Could not find a version of package %s matching your minimum-stability (%s). Require it with an explicit version constraint allowing its desired stability.', + $name, + $effectiveMinimumStability )); } // Check whether the required version was the problem - if ($requiredVersion && $versionSelector->findBestCandidate($name, null, $phpVersion, $preferredStability)) { + if ($requiredVersion && $package = $versionSelector->findBestCandidate($name, null, $preferredStability, $ignorePlatformReqs)) { + // we must first verify if a valid package would be found in a lower priority repository + if ($allReposPackage = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, false, RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) { + throw new \InvalidArgumentException( + 'Package '.$name.' exists in '.$allReposPackage->getRepository()->getRepoName().' and '.$package->getRepository()->getRepoName().' which has a higher repository priority. The packages with higher priority do not match your constraint and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.' + ); + } + throw new \InvalidArgumentException(sprintf( - 'Could not find package %s in a version matching %s', + 'Could not find package %s in a version matching "%s" and a stability matching "'.$effectiveMinimumStability.'".', $name, $requiredVersion )); } - // Check whether the PHP version was the problem - if ($phpVersion && $versionSelector->findBestCandidate($name)) { + // Check whether the PHP version was the problem for all versions + if (true !== $ignorePlatformReqs && ($candidate = $versionSelector->findBestCandidate($name, null, $preferredStability, true, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES))) { + $additional = ''; + if (false === $versionSelector->findBestCandidate($name, null, $preferredStability, true)) { + $additional = PHP_EOL.PHP_EOL.'Additionally, the package was only found with a stability of "'.$candidate->getStability().'" while your minimum stability is "'.$effectiveMinimumStability.'".'; + } + throw new \InvalidArgumentException(sprintf( - 'Could not find package %s in any version matching your PHP version (%s)', + 'Could not find package %s in any version matching your PHP version, PHP extensions and Composer version' . $this->getPlatformExceptionDetails($candidate, $platformRepo) . '%s', $name, - $phpVersion + $additional )); } // Check for similar names/typos $similar = $this->findSimilar($name); if ($similar) { - // Check whether the minimum stability was the problem but the package exists - if ($requiredVersion === null && in_array($name, $similar, true)) { + if (in_array($name, $similar, true)) { throw new \InvalidArgumentException(sprintf( - 'Could not find a version of package %s matching your minimum-stability (%s). Require it with an explicit version constraint allowing its desired stability.', - $name, - $this->getMinimumStability($input) + "Could not find package %s. It was however found via repository search, which indicates a consistency issue with the repository.", + $name )); } @@ -796,7 +926,7 @@ private function findBestVersionAndNameForPackage(InputInterface $input, $name, throw new \InvalidArgumentException(sprintf( 'Could not find a matching version of package %s. Check the package spelling, your version constraint and that the package is available in a stability which matches your minimum-stability (%s).', $name, - $this->getMinimumStability($input) + $effectiveMinimumStability )); } @@ -806,6 +936,39 @@ private function findBestVersionAndNameForPackage(InputInterface $input, $name, ); } + private function getPlatformExceptionDetails(PackageInterface $candidate, PlatformRepository $platformRepo = null) + { + $details = array(); + if (!$platformRepo) { + return ''; + } + + foreach ($candidate->getRequires() as $link) { + if (!PlatformRepository::isPlatformPackage($link->getTarget())) { + continue; + } + $platformPkg = $platformRepo->findPackage($link->getTarget(), '*'); + if (!$platformPkg) { + $details[] = $candidate->getPrettyName().' '.$candidate->getPrettyVersion().' requires '.$link->getTarget().' '.$link->getPrettyConstraint().' but it is not present.'; + continue; + } + if (!$link->getConstraint()->matches(new Constraint('==', $platformPkg->getVersion()))) { + $platformPkgVersion = $platformPkg->getPrettyVersion(); + $platformExtra = $platformPkg->getExtra(); + if (isset($platformExtra['config.platform']) && $platformPkg instanceof CompletePackageInterface) { + $platformPkgVersion .= ' ('.$platformPkg->getDescription().')'; + } + $details[] = $candidate->getPrettyName().' '.$candidate->getPrettyVersion().' requires '.$link->getTarget().' '.$link->getPrettyConstraint().' which does not match your installed version '.$platformPkgVersion.'.'; + } + } + + if (!$details) { + return ''; + } + + return ':'.PHP_EOL.' - ' . implode(PHP_EOL.' - ', $details); + } + private function findSimilar($package) { try { @@ -830,15 +993,26 @@ private function findSimilar($package) return array_keys(array_slice($similarPackages, 0, 5)); } - private function installDependencies($output) + private function updateDependencies($output) { try { - $installCommand = $this->getApplication()->find('install'); - $installCommand->run(new ArrayInput(array()), $output); + $updateCommand = $this->getApplication()->find('update'); + $this->getApplication()->resetComposer(); + $updateCommand->run(new ArrayInput(array()), $output); } catch (\Exception $e) { - $this->getIO()->writeError('Could not install dependencies. Run `composer install` to see more information.'); + $this->getIO()->writeError('Could not update dependencies. Run `composer update` to see more information.'); } + } + private function runDumpAutoloadCommand($output) + { + try { + $command = $this->getApplication()->find('dump-autoload'); + $this->getApplication()->resetComposer(); + $command->run(new ArrayInput(array()), $output); + } catch (\Exception $e) { + $this->getIO()->writeError('Could not run dump-autoload.'); + } } private function hasDependencies($options) diff --git a/app/vendor/composer/composer/src/Composer/Command/InstallCommand.php b/app/vendor/composer/composer/src/Composer/Command/InstallCommand.php index 32fb1bdc6..eda9c25cf 100644 --- a/app/vendor/composer/composer/src/Composer/Command/InstallCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/InstallCommand.php @@ -15,6 +15,7 @@ use Composer\Installer; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; +use Composer\Util\HttpDownloader; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; @@ -36,20 +37,23 @@ protected function configure() ->setDescription('Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json.') ->setDefinition(array( new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), - new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), + new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist (default behavior).'), + new InputOption('prefer-install', null, InputOption::VALUE_REQUIRED, 'Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).'), new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), - new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'), + new InputOption('dev', null, InputOption::VALUE_NONE, 'DEPRECATED: Enables installation of require-dev packages (enabled by default, only present for BC).'), + new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'DEPRECATED: This flag does not exist anymore.'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'), - new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'), new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), - new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'), + new InputOption('no-install', null, InputOption::VALUE_NONE, 'Do not use, only defined here to catch misuse of the install command.'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('apcu-autoloader-prefix', null, InputOption::VALUE_REQUIRED, 'Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader'), + new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'), new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'), )) ->setHelp( @@ -70,23 +74,31 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $io = $this->getIO(); + if ($input->getOption('dev')) { + $io->writeError('You are using the deprecated option "--dev". It has no effect and will break in Composer 3.'); + } + if ($input->getOption('no-suggest')) { + $io->writeError('You are using the deprecated option "--no-suggest". It has no effect and will break in Composer 3.'); + } + if ($args = $input->getArgument('packages')) { $io->writeError('Invalid argument '.implode(' ', $args).'. Use "composer require '.implode(' ', $args).'" instead to add packages to your composer.json.'); return 1; } - if ($input->getOption('no-custom-installers')) { - $io->writeError('You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.'); - $input->setOption('no-plugins', true); - } + if ($input->getOption('no-install')) { + $io->writeError('Invalid option "--no-install". Use "composer update --no-install" instead if you are trying to update the composer.lock file.'); - if ($input->getOption('dev')) { - $io->writeError('You are using the deprecated option "dev". Dev packages are installed by default now.'); + return 1; } $composer = $this->getComposer(true, $input->getOption('no-plugins')); - $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); + $composer->getEventDispatcher()->setRunScripts(!$input->getOption('no-scripts')); + + if ((!$composer->getLocker() || !$composer->getLocker()->isLocked()) && !HttpDownloader::isCurlEnabled()) { + $io->writeError('Composer is operating significantly slower than normal because you do not have the PHP curl extension enabled.'); + } $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'install', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); @@ -98,7 +110,12 @@ protected function execute(InputInterface $input, OutputInterface $output) $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader'); $authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative'); - $apcu = $input->getOption('apcu-autoloader') || $config->get('apcu-autoloader'); + $apcuPrefix = $input->getOption('apcu-autoloader-prefix'); + $apcu = $apcuPrefix !== null || $input->getOption('apcu-autoloader') || $config->get('apcu-autoloader'); + + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); + + $composer->getInstallationManager()->setOutputProgress(!$input->getOption('no-progress')); $install ->setDryRun($input->getOption('dry-run')) @@ -107,12 +124,10 @@ protected function execute(InputInterface $input, OutputInterface $output) ->setPreferDist($preferDist) ->setDevMode(!$input->getOption('no-dev')) ->setDumpAutoloader(!$input->getOption('no-autoloader')) - ->setRunScripts(!$input->getOption('no-scripts')) - ->setSkipSuggest($input->getOption('no-suggest')) ->setOptimizeAutoloader($optimize) ->setClassMapAuthoritative($authoritative) - ->setApcuAutoloader($apcu) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) + ->setApcuAutoloader($apcu, $apcuPrefix) + ->setIgnorePlatformRequirements($ignorePlatformReqs) ; if ($input->getOption('no-plugins')) { diff --git a/app/vendor/composer/composer/src/Composer/Command/LicensesCommand.php b/app/vendor/composer/composer/src/Composer/Command/LicensesCommand.php index 85cb64a7f..b4544cf50 100644 --- a/app/vendor/composer/composer/src/Composer/Command/LicensesCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/LicensesCommand.php @@ -21,6 +21,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; /** * @author Benoît Merlet @@ -33,7 +34,7 @@ protected function configure() ->setName('licenses') ->setDescription('Shows information about licenses of dependencies.') ->setDefinition(array( - new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'), + new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text, json or summary', 'text'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables search in require-dev packages.'), )) ->setHelp( @@ -80,6 +81,8 @@ protected function execute(InputInterface $input, OutputInterface $output) if (method_exists($tableStyle, 'setVerticalBorderChars')) { $tableStyle->setVerticalBorderChars(''); } else { + // TODO remove in composer 2.2 + // @phpstan-ignore-next-line $tableStyle->setVerticalBorderChar(''); } $tableStyle->setCellRowContentFormat('%s '); @@ -111,6 +114,31 @@ protected function execute(InputInterface $input, OutputInterface $output) ))); break; + case 'summary': + $usedLicenses = array(); + foreach ($packages as $package) { + $license = $package->getLicense(); + $licenseName = $license[0]; + if (!isset($usedLicenses[$licenseName])) { + $usedLicenses[$licenseName] = 0; + } + $usedLicenses[$licenseName]++; + } + + // Sort licenses so that the most used license will appear first + arsort($usedLicenses, SORT_NUMERIC); + + $rows = array(); + foreach ($usedLicenses as $usedLicense => $numberOfDependencies) { + $rows[] = array($usedLicense, $numberOfDependencies); + } + + $symfonyIo = new SymfonyStyle($input, $output); + $symfonyIo->table( + array('License', 'Number of dependencies'), + $rows + ); + break; default: throw new \RuntimeException(sprintf('Unsupported format "%s". See help for supported formats.', $format)); } diff --git a/app/vendor/composer/composer/src/Composer/Command/OutdatedCommand.php b/app/vendor/composer/composer/src/Composer/Command/OutdatedCommand.php index 599087246..ace1631d8 100644 --- a/app/vendor/composer/composer/src/Composer/Command/OutdatedCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/OutdatedCommand.php @@ -32,11 +32,13 @@ protected function configure() new InputArgument('package', InputArgument::OPTIONAL, 'Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.'), new InputOption('outdated', 'o', InputOption::VALUE_NONE, 'Show only packages that are outdated (this is the default, but present here for compat with `show`'), new InputOption('all', 'a', InputOption::VALUE_NONE, 'Show all installed packages with their latest versions'), + new InputOption('locked', null, InputOption::VALUE_NONE, 'Shows updates for packages from the lock file, regardless of what is currently in vendor dir'), new InputOption('direct', 'D', InputOption::VALUE_NONE, 'Shows only packages that are directly required by the root package'), new InputOption('strict', null, InputOption::VALUE_NONE, 'Return a non-zero exit code when there are outdated packages'), new InputOption('minor-only', 'm', InputOption::VALUE_NONE, 'Show only packages that have minor SemVer-compatible updates. Use with the --outdated option.'), new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'), new InputOption('ignore', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore specified package(s). Use it with the --outdated option if you don\'t want to be informed about new versions of some packages.'), + new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables search in require-dev packages.'), )) ->setHelp( <<getOption('minor-only')) { $args['--minor-only'] = true; } + if ($input->getOption('locked')) { + $args['--locked'] = true; + } + if ($input->getOption('no-dev')) { + $args['--no-dev'] = true; + } $args['--format'] = $input->getOption('format'); $args['--ignore'] = $input->getOption('ignore'); diff --git a/app/vendor/composer/composer/src/Composer/Command/ProhibitsCommand.php b/app/vendor/composer/composer/src/Composer/Command/ProhibitsCommand.php index 1e18e5e23..9931a881b 100644 --- a/app/vendor/composer/composer/src/Composer/Command/ProhibitsCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/ProhibitsCommand.php @@ -14,6 +14,8 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; /** * @author Niels Keurentjes @@ -25,12 +27,16 @@ class ProhibitsCommand extends BaseDependencyCommand */ protected function configure() { - parent::configure(); - $this ->setName('prohibits') ->setAliases(array('why-not')) ->setDescription('Shows which packages prevent the given package from being installed.') + ->setDefinition(array( + new InputArgument(self::ARGUMENT_PACKAGE, InputArgument::REQUIRED, 'Package to inspect'), + new InputArgument(self::ARGUMENT_CONSTRAINT, InputArgument::REQUIRED, 'Version constraint, which version you expected to be installed'), + new InputOption(self::OPTION_RECURSIVE, 'r', InputOption::VALUE_NONE, 'Recursively resolves up to the root package'), + new InputOption(self::OPTION_TREE, 't', InputOption::VALUE_NONE, 'Prints the results as a nested tree'), + )) ->setHelp( << + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Command; + +use Composer\DependencyResolver\Operation\InstallOperation; +use Composer\DependencyResolver\Operation\UninstallOperation; +use Composer\DependencyResolver\Transaction; +use Composer\Package\AliasPackage; +use Composer\Package\BasePackage; +use Composer\Plugin\CommandEvent; +use Composer\Plugin\PluginEvents; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Jordi Boggiano + */ +class ReinstallCommand extends BaseCommand +{ + protected function configure() + { + $this + ->setName('reinstall') + ->setDescription('Uninstalls and reinstalls the given package names') + ->setDefinition(array( + new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), + new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist (default behavior).'), + new InputOption('prefer-install', null, InputOption::VALUE_REQUIRED, 'Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).'), + new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'), + new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), + new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), + new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), + new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), + new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), + new InputOption('apcu-autoloader-prefix', null, InputOption::VALUE_REQUIRED, 'Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader'), + new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'), + new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'List of package names to reinstall, can include a wildcard (*) to match any substring.'), + )) + ->setHelp( + <<reinstall command looks up installed packages by name, +uninstalls them and reinstalls them. This lets you do a clean install +of a package if you messed with its files, or if you wish to change +the installation type using --prefer-install. + +php composer.phar reinstall acme/foo "acme/bar-*" + +Read more at https://getcomposer.org/doc/03-cli.md#reinstall +EOT + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = $this->getIO(); + + $composer = $this->getComposer(true, $input->getOption('no-plugins')); + $composer->getEventDispatcher()->setRunScripts(!$input->getOption('no-scripts')); + + $localRepo = $composer->getRepositoryManager()->getLocalRepository(); + $packagesToReinstall = array(); + $packageNamesToReinstall = array(); + foreach ($input->getArgument('packages') as $pattern) { + $patternRegexp = BasePackage::packageNameToRegexp($pattern); + $matched = false; + foreach ($localRepo->getCanonicalPackages() as $package) { + if (preg_match($patternRegexp, $package->getName())) { + $matched = true; + $packagesToReinstall[] = $package; + $packageNamesToReinstall[] = $package->getName(); + } + } + + if (!$matched) { + $io->writeError('Pattern "' . $pattern . '" does not match any currently installed packages.'); + } + } + + if (!$packagesToReinstall) { + $io->writeError('Found no packages to reinstall, aborting.'); + + return 1; + } + + $uninstallOperations = array(); + foreach ($packagesToReinstall as $package) { + $uninstallOperations[] = new UninstallOperation($package); + } + + // make sure we have a list of install operations ordered by dependency/plugins + $presentPackages = $localRepo->getPackages(); + $resultPackages = $presentPackages; + foreach ($presentPackages as $index => $package) { + if (in_array($package->getName(), $packageNamesToReinstall, true)) { + unset($presentPackages[$index]); + } + } + $transaction = new Transaction($presentPackages, $resultPackages); + $installOperations = $transaction->getOperations(); + + // reverse-sort the uninstalls based on the install order + $installOrder = array(); + foreach ($installOperations as $index => $op) { + if ($op instanceof InstallOperation && !$op->getPackage() instanceof AliasPackage) { + $installOrder[$op->getPackage()->getName()] = $index; + } + } + usort($uninstallOperations, function ($a, $b) use ($installOrder) { + return $installOrder[$b->getPackage()->getName()] - $installOrder[$a->getPackage()->getName()]; + }); + + $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'reinstall', $input, $output); + $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); + + $config = $composer->getConfig(); + list($preferSource, $preferDist) = $this->getPreferredInstallOptions($config, $input); + + $installationManager = $composer->getInstallationManager(); + $downloadManager = $composer->getDownloadManager(); + $package = $composer->getPackage(); + + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); + + $installationManager->setOutputProgress(!$input->getOption('no-progress')); + if ($input->getOption('no-plugins')) { + $installationManager->disablePlugins(); + } + + $downloadManager->setPreferSource($preferSource); + $downloadManager->setPreferDist($preferDist); + + $installationManager->execute($localRepo, $uninstallOperations, true); + $installationManager->execute($localRepo, $installOperations, true); + + if (!$input->getOption('no-autoloader')) { + $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader'); + $authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative'); + $apcuPrefix = $input->getOption('apcu-autoloader-prefix'); + $apcu = $apcuPrefix !== null || $input->getOption('apcu-autoloader') || $config->get('apcu-autoloader'); + + $generator = $composer->getAutoloadGenerator(); + $generator->setClassMapAuthoritative($authoritative); + $generator->setApcu($apcu, $apcuPrefix); + $generator->setIgnorePlatformRequirements($ignorePlatformReqs); + $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize); + } + + return 0; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Command/RemoveCommand.php b/app/vendor/composer/composer/src/Composer/Command/RemoveCommand.php index e6a2fbfb3..3cd865126 100644 --- a/app/vendor/composer/composer/src/Composer/Command/RemoveCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/RemoveCommand.php @@ -13,6 +13,7 @@ namespace Composer\Command; use Composer\Config\JsonConfigSource; +use Composer\DependencyResolver\Request; use Composer\Installer; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; @@ -38,16 +39,23 @@ protected function configure() ->setDefinition(array( new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'Packages that should be removed.'), new InputOption('dev', null, InputOption::VALUE_NONE, 'Removes a package from the require-dev section.'), + new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), - new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'), + new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies (implies --no-install).'), + new InputOption('no-install', null, InputOption::VALUE_NONE, 'Skip the install step after updating the composer.lock file.'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'), - new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies. (Deprecrated, is now default behavior)'), + new InputOption('update-with-dependencies', 'w', InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies. (Deprecrated, is now default behavior)'), + new InputOption('update-with-all-dependencies', 'W', InputOption::VALUE_NONE, 'Allows all inherited dependencies to be updated, including those that are root requirements.'), + new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-all-dependencies'), new InputOption('no-update-with-dependencies', null, InputOption::VALUE_NONE, 'Does not allow inherited dependencies to be updated with explicit dependencies.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('unused', null, InputOption::VALUE_NONE, 'Remove all packages which are locked but not required by any other package.'), + new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), + new InputOption('apcu-autoloader-prefix', null, InputOption::VALUE_REQUIRED, 'Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader'), )) ->setHelp( <<getOption('unused')) { + $composer = $this->getComposer(); + $locker = $composer->getLocker(); + if (!$locker->isLocked()) { + throw new \UnexpectedValueException('A valid composer.lock file is required to run this command with --unused'); + } + + $lockedPackages = $locker->getLockedRepository()->getPackages(); + + $required = array(); + foreach (array_merge($composer->getPackage()->getRequires(), $composer->getPackage()->getDevRequires()) as $link) { + $required[$link->getTarget()] = true; + } + + do { + $found = false; + foreach ($lockedPackages as $index => $package) { + foreach ($package->getNames() as $name) { + if (isset($required[$name])) { + foreach ($package->getRequires() as $link) { + $required[$link->getTarget()] = true; + } + $found = true; + unset($lockedPackages[$index]); + break; + } + } + } + } while ($found); + + $unused = array(); + foreach ($lockedPackages as $package) { + $unused[] = $package->getName(); + } + $input->setArgument('packages', array_merge($input->getArgument('packages'), $unused)); + + if (!$input->getArgument('packages')) { + $this->getIO()->writeError('No unused packages to remove'); + $this->setCode(function () { + return 0; + }); + } + } + } + protected function execute(InputInterface $input, OutputInterface $output) { $packages = $input->getArgument('packages'); @@ -92,26 +147,44 @@ protected function execute(InputInterface $input, OutputInterface $output) } } + $dryRun = $input->getOption('dry-run'); + $toRemove = array(); foreach ($packages as $package) { if (isset($composer[$type][$package])) { - $json->removeLink($type, $composer[$type][$package]); + if ($dryRun) { + $toRemove[$type][] = $composer[$type][$package]; + } else { + $json->removeLink($type, $composer[$type][$package]); + } } elseif (isset($composer[$altType][$package])) { $io->writeError('' . $composer[$altType][$package] . ' could not be found in ' . $type . ' but it is present in ' . $altType . ''); if ($io->isInteractive()) { - if ($io->askConfirmation('Do you want to remove it from ' . $altType . ' [yes]? ', true)) { - $json->removeLink($altType, $composer[$altType][$package]); + if ($io->askConfirmation('Do you want to remove it from ' . $altType . ' [yes]? ')) { + if ($dryRun) { + $toRemove[$altType][] = $composer[$altType][$package]; + } else { + $json->removeLink($altType, $composer[$altType][$package]); + } } } } elseif (isset($composer[$type]) && $matches = preg_grep(BasePackage::packageNameToRegexp($package), array_keys($composer[$type]))) { foreach ($matches as $matchedPackage) { - $json->removeLink($type, $matchedPackage); + if ($dryRun) { + $toRemove[$type][] = $matchedPackage; + } else { + $json->removeLink($type, $matchedPackage); + } } } elseif (isset($composer[$altType]) && $matches = preg_grep(BasePackage::packageNameToRegexp($package), array_keys($composer[$altType]))) { foreach ($matches as $matchedPackage) { $io->writeError('' . $matchedPackage . ' could not be found in ' . $type . ' but it is present in ' . $altType . ''); if ($io->isInteractive()) { - if ($io->askConfirmation('Do you want to remove it from ' . $altType . ' [yes]? ', true)) { - $json->removeLink($altType, $matchedPackage); + if ($io->askConfirmation('Do you want to remove it from ' . $altType . ' [yes]? ')) { + if ($dryRun) { + $toRemove[$altType][] = $matchedPackage; + } else { + $json->removeLink($altType, $matchedPackage); + } } } } @@ -120,6 +193,8 @@ protected function execute(InputInterface $input, OutputInterface $output) } } + $io->writeError(''.$file.' has been updated'); + if ($input->getOption('no-update')) { return 0; } @@ -127,37 +202,85 @@ protected function execute(InputInterface $input, OutputInterface $output) // Update packages $this->resetComposer(); $composer = $this->getComposer(true, $input->getOption('no-plugins')); - $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); + $composer->getEventDispatcher()->setRunScripts(!$input->getOption('no-scripts')); + + if ($dryRun) { + $rootPackage = $composer->getPackage(); + $links = array( + 'require' => $rootPackage->getRequires(), + 'require-dev' => $rootPackage->getDevRequires(), + ); + foreach ($toRemove as $type => $names) { + foreach ($names as $name) { + unset($links[$type][$name]); + } + } + $rootPackage->setRequires($links['require']); + $rootPackage->setDevRequires($links['require-dev']); + } $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'remove', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); + $composer->getInstallationManager()->setOutputProgress(!$input->getOption('no-progress')); + $install = Installer::create($io, $composer); $updateDevMode = !$input->getOption('update-no-dev'); $optimize = $input->getOption('optimize-autoloader') || $composer->getConfig()->get('optimize-autoloader'); $authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()->get('classmap-authoritative'); - $apcu = $input->getOption('apcu-autoloader') || $composer->getConfig()->get('apcu-autoloader'); + $apcuPrefix = $input->getOption('apcu-autoloader-prefix'); + $apcu = $apcuPrefix !== null || $input->getOption('apcu-autoloader') || $composer->getConfig()->get('apcu-autoloader'); + + $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; + $flags = ''; + if ($input->getOption('update-with-all-dependencies') || $input->getOption('with-all-dependencies')) { + $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS; + $flags .= ' --with-all-dependencies'; + } elseif ($input->getOption('no-update-with-dependencies')) { + $updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED; + $flags .= ' --with-dependencies'; + } + + $io->writeError('Running composer update '.implode(' ', $packages).$flags.''); + + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); $install ->setVerbose($input->getOption('verbose')) ->setDevMode($updateDevMode) ->setOptimizeAutoloader($optimize) ->setClassMapAuthoritative($authoritative) - ->setApcuAutoloader($apcu) + ->setApcuAutoloader($apcu, $apcuPrefix) ->setUpdate(true) - ->setUpdateAllowList($packages) - ->setAllowListTransitiveDependencies(!$input->getOption('no-update-with-dependencies')) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) - ->setRunScripts(!$input->getOption('no-scripts')) + ->setInstall(!$input->getOption('no-install')) + ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) + ->setIgnorePlatformRequirements($ignorePlatformReqs) + ->setDryRun($dryRun) ; + // if no lock is present, we do not do a partial update as + // this is not supported by the Installer + if ($composer->getLocker()->isLocked()) { + $install->setUpdateAllowList($packages); + } + $status = $install->run(); if ($status !== 0) { $io->writeError("\n".'Removal failed, reverting '.$file.' to its original content.'); file_put_contents($jsonFile->getPath(), $composerBackup); } + if (!$dryRun) { + foreach ($packages as $package) { + if ($composer->getRepositoryManager()->getLocalRepository()->findPackages($package)) { + $io->writeError('Removal failed, '.$package.' is still present, it may be required by another package. See `composer why '.$package.'`.'); + + return 2; + } + } + } + return $status; } } diff --git a/app/vendor/composer/composer/src/Composer/Command/RequireCommand.php b/app/vendor/composer/composer/src/Composer/Command/RequireCommand.php index 039250766..578e2a208 100644 --- a/app/vendor/composer/composer/src/Composer/Command/RequireCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/RequireCommand.php @@ -12,6 +12,8 @@ namespace Composer\Command; +use Composer\DependencyResolver\Request; +use Composer\Util\Filesystem; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; @@ -21,6 +23,8 @@ use Composer\Json\JsonFile; use Composer\Json\JsonManipulator; use Composer\Package\Version\VersionParser; +use Composer\Package\Loader\ArrayLoader; +use Composer\Package\BasePackage; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; use Composer\Repository\CompositeRepository; @@ -34,10 +38,20 @@ */ class RequireCommand extends InitCommand { + /** @var bool */ private $newlyCreated; + /** @var bool */ + private $firstRequire; + /** @var JsonFile */ private $json; + /** @var string */ private $file; + /** @var string */ private $composerBackup; + /** @var string file name */ + private $lock; + /** @var ?string contents before modification if the lock file exists */ + private $lockBackup; protected function configure() { @@ -47,23 +61,30 @@ protected function configure() ->setDefinition(array( new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Optional package name can also include a version constraint, e.g. foo/bar or foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'), new InputOption('dev', null, InputOption::VALUE_NONE, 'Add requirement to require-dev.'), + new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), - new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), + new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist (default behavior).'), + new InputOption('prefer-install', null, InputOption::VALUE_REQUIRED, 'Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).'), new InputOption('fixed', null, InputOption::VALUE_NONE, 'Write fixed version to the composer.json.'), + new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'DEPRECATED: This flag does not exist anymore.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), - new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'), - new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'), + new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies (implies --no-install).'), + new InputOption('no-install', null, InputOption::VALUE_NONE, 'Skip the install step after updating the composer.lock file.'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'), - new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated, except those that are root requirements.'), - new InputOption('update-with-all-dependencies', null, InputOption::VALUE_NONE, 'Allows all inherited dependencies to be updated, including those that are root requirements.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('update-with-dependencies', 'w', InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated, except those that are root requirements.'), + new InputOption('update-with-all-dependencies', 'W', InputOption::VALUE_NONE, 'Allows all inherited dependencies to be updated, including those that are root requirements.'), + new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-dependencies'), + new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-all-dependencies'), + new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'), new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'), new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'), new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages when adding/updating a new dependency'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), + new InputOption('apcu-autoloader-prefix', null, InputOption::VALUE_REQUIRED, 'Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader'), )) ->setHelp( <<file = Factory::getComposerFile(); $io = $this->getIO(); + if ($input->getOption('no-suggest')) { + $io->writeError('You are using the deprecated option "--no-suggest". It has no effect and will break in Composer 3.'); + } + $this->newlyCreated = !file_exists($this->file); if ($this->newlyCreated && !file_put_contents($this->file, "{\n}\n")) { $io->writeError(''.$this->file.' could not be created.'); return 1; } - // check for readability by reading the file as is_readable can not be trusted on network-mounts - // see https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926 - if (!is_readable($this->file) && false === Silencer::call('file_get_contents', $this->file)) { + if (!Filesystem::isReadable($this->file)) { $io->writeError(''.$this->file.' is not readable.'); return 1; @@ -113,7 +136,9 @@ protected function execute(InputInterface $input, OutputInterface $output) } $this->json = new JsonFile($this->file); + $this->lock = Factory::getLockFile($this->file); $this->composerBackup = file_get_contents($this->json->getPath()); + $this->lockBackup = file_exists($this->lock) ? file_get_contents($this->lock) : null; // check for writability by writing to the file as is_writable can not be trusted on network-mounts // see https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926 @@ -148,7 +173,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $platformOverrides = $composer->getConfig()->get('platform') ?: array(); // initialize $this->repos as it is used by the parent InitCommand $this->repos = new CompositeRepository(array_merge( - array(new PlatformRepository(array(), $platformOverrides)), + array($platformRepo = new PlatformRepository(array(), $platformOverrides)), $repos )); @@ -158,12 +183,19 @@ protected function execute(InputInterface $input, OutputInterface $output) $preferredStability = $composer->getPackage()->getMinimumStability(); } - $phpVersion = $this->repos->findPackage('php', '*')->getPrettyVersion(); try { - $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion, $preferredStability, !$input->getOption('no-update'), $input->getOption('fixed')); + $requirements = $this->determineRequirements( + $input, + $output, + $input->getArgument('packages'), + $platformRepo, + $preferredStability, + !$input->getOption('no-update'), + $input->getOption('fixed') + ); } catch (\Exception $e) { if ($this->newlyCreated) { - throw new \RuntimeException('No composer.json present in the current directory, this may be the cause of the following exception.', 0, $e); + throw new \RuntimeException('No composer.json present in the current directory ('.$this->file.'), this may be the cause of the following exception.', 0, $e); } throw $e; @@ -184,13 +216,47 @@ protected function execute(InputInterface $input, OutputInterface $output) $versionParser->parseConstraints($constraint); } + $inconsistentRequireKeys = $this->getInconsistentRequireKeys($requirements, $requireKey); + if (count($inconsistentRequireKeys) > 0) { + foreach ($inconsistentRequireKeys as $package) { + $io->warning(sprintf( + '%s is currently present in the %s key and you ran the command %s the --dev flag, which would move it to the %s key.', + $package, + $removeKey, + $input->getOption('dev') ? 'with' : 'without', + $requireKey + )); + } + + if ($io->isInteractive()) { + if (!$io->askConfirmation(sprintf('Do you want to move %s? [no]? ', count($inconsistentRequireKeys) > 1 ? 'these requirements' : 'this requirement'), false)) { + if (!$io->askConfirmation(sprintf('Do you want to re-run the command %s --dev? [yes]? ', $input->getOption('dev') ? 'without' : 'with'), true)) { + return 0; + } + + list($requireKey, $removeKey) = array($removeKey, $requireKey); + } + } + } + $sortPackages = $input->getOption('sort-packages') || $composer->getConfig()->get('sort-packages'); - if (!$this->updateFileCleanly($this->json, $requirements, $requireKey, $removeKey, $sortPackages)) { + $this->firstRequire = $this->newlyCreated; + if (!$this->firstRequire) { + $composerDefinition = $this->json->read(); + if (empty($composerDefinition['require']) && empty($composerDefinition['require-dev'])) { + $this->firstRequire = true; + } + } + + if (!$input->getOption('dry-run') && !$this->updateFileCleanly($this->json, $requirements, $requireKey, $removeKey, $sortPackages)) { $composerDefinition = $this->json->read(); foreach ($requirements as $package => $version) { $composerDefinition[$requireKey][$package] = $version; unset($composerDefinition[$removeKey][$package]); + if (isset($composerDefinition[$removeKey]) && count($composerDefinition[$removeKey]) === 0) { + unset($composerDefinition[$removeKey]); + } } $this->json->write($composerDefinition); } @@ -202,49 +268,123 @@ protected function execute(InputInterface $input, OutputInterface $output) } try { - return $this->doUpdate($input, $output, $io, $requirements); + return $this->doUpdate($input, $output, $io, $requirements, $requireKey, $removeKey); } catch (\Exception $e) { $this->revertComposerFile(false); throw $e; } } - private function doUpdate(InputInterface $input, OutputInterface $output, IOInterface $io, array $requirements) + private function getInconsistentRequireKeys(array $newRequirements, $requireKey) + { + $requireKeys = $this->getPackagesByRequireKey(); + $inconsistentRequirements = array(); + foreach ($requireKeys as $package => $packageRequireKey) { + if (!isset($newRequirements[$package])) { + continue; + } + if ($requireKey !== $packageRequireKey) { + $inconsistentRequirements[] = $package; + } + } + + return $inconsistentRequirements; + } + + private function getPackagesByRequireKey() + { + $composerDefinition = $this->json->read(); + $require = array(); + $requireDev = array(); + + if (isset($composerDefinition['require'])) { + $require = $composerDefinition['require']; + } + + if (isset($composerDefinition['require-dev'])) { + $requireDev = $composerDefinition['require-dev']; + } + + return array_merge( + array_fill_keys(array_keys($require), 'require'), + array_fill_keys(array_keys($requireDev), 'require-dev') + ); + } + + private function doUpdate(InputInterface $input, OutputInterface $output, IOInterface $io, array $requirements, $requireKey, $removeKey) { // Update packages $this->resetComposer(); $composer = $this->getComposer(true, $input->getOption('no-plugins')); - $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); + $composer->getEventDispatcher()->setRunScripts(!$input->getOption('no-scripts')); + + if ($input->getOption('dry-run')) { + $rootPackage = $composer->getPackage(); + $links = array( + 'require' => $rootPackage->getRequires(), + 'require-dev' => $rootPackage->getDevRequires(), + ); + $loader = new ArrayLoader(); + $newLinks = $loader->parseLinks($rootPackage->getName(), $rootPackage->getPrettyVersion(), BasePackage::$supportedLinkTypes[$requireKey]['method'], $requirements); + $links[$requireKey] = array_merge($links[$requireKey], $newLinks); + foreach ($requirements as $package => $constraint) { + unset($links[$removeKey][$package]); + } + $rootPackage->setRequires($links['require']); + $rootPackage->setDevRequires($links['require-dev']); + } $updateDevMode = !$input->getOption('update-no-dev'); $optimize = $input->getOption('optimize-autoloader') || $composer->getConfig()->get('optimize-autoloader'); $authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()->get('classmap-authoritative'); - $apcu = $input->getOption('apcu-autoloader') || $composer->getConfig()->get('apcu-autoloader'); + $apcuPrefix = $input->getOption('apcu-autoloader-prefix'); + $apcu = $apcuPrefix !== null || $input->getOption('apcu-autoloader') || $composer->getConfig()->get('apcu-autoloader'); + + $updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED; + $flags = ''; + if ($input->getOption('update-with-all-dependencies') || $input->getOption('with-all-dependencies')) { + $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS; + $flags .= ' --with-all-dependencies'; + } elseif ($input->getOption('update-with-dependencies') || $input->getOption('with-dependencies')) { + $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; + $flags .= ' --with-dependencies'; + } + + $io->writeError('Running composer update '.implode(' ', array_keys($requirements)).$flags.''); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); + $composer->getInstallationManager()->setOutputProgress(!$input->getOption('no-progress')); + $install = Installer::create($io, $composer); + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); + list($preferSource, $preferDist) = $this->getPreferredInstallOptions($composer->getConfig(), $input); + $install + ->setDryRun($input->getOption('dry-run')) ->setVerbose($input->getOption('verbose')) - ->setPreferSource($input->getOption('prefer-source')) - ->setPreferDist($input->getOption('prefer-dist')) + ->setPreferSource($preferSource) + ->setPreferDist($preferDist) ->setDevMode($updateDevMode) - ->setRunScripts(!$input->getOption('no-scripts')) - ->setSkipSuggest($input->getOption('no-suggest')) ->setOptimizeAutoloader($optimize) ->setClassMapAuthoritative($authoritative) - ->setApcuAutoloader($apcu) + ->setApcuAutoloader($apcu, $apcuPrefix) ->setUpdate(true) - ->setUpdateAllowList(array_keys($requirements)) - ->setAllowListTransitiveDependencies($input->getOption('update-with-dependencies')) - ->setAllowListAllDependencies($input->getOption('update-with-all-dependencies')) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) + ->setInstall(!$input->getOption('no-install')) + ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) + ->setIgnorePlatformRequirements($ignorePlatformReqs) ->setPreferStable($input->getOption('prefer-stable')) ->setPreferLowest($input->getOption('prefer-lowest')) ; + // if no lock is present, or the file is brand new, we do not do a + // partial update as this is not supported by the Installer + if (!$this->firstRequire && $composer->getLocker()->isLocked()) { + $install->setUpdateAllowList(array_keys($requirements)); + } + $status = $install->run(); if ($status !== 0) { $this->revertComposerFile(false); @@ -268,6 +408,8 @@ private function updateFileCleanly($json, array $new, $requireKey, $removeKey, $ } } + $manipulator->removeMainKeyIfEmpty($removeKey); + file_put_contents($json->getPath(), $manipulator->getContents()); return true; @@ -285,9 +427,19 @@ public function revertComposerFile($hardExit = true) if ($this->newlyCreated) { $io->writeError("\n".'Installation failed, deleting '.$this->file.'.'); unlink($this->json->getPath()); + if (file_exists($this->lock)) { + unlink($this->lock); + } } else { - $io->writeError("\n".'Installation failed, reverting '.$this->file.' to its original content.'); + $msg = ' to its '; + if ($this->lockBackup) { + $msg = ' and '.$this->lock.' to their '; + } + $io->writeError("\n".'Installation failed, reverting '.$this->file.$msg.'original content.'); file_put_contents($this->json->getPath(), $this->composerBackup); + if ($this->lockBackup) { + file_put_contents($this->lock, $this->lockBackup); + } } if ($hardExit) { diff --git a/app/vendor/composer/composer/src/Composer/Command/RunScriptCommand.php b/app/vendor/composer/composer/src/Composer/Command/RunScriptCommand.php index 1d1b6be76..81b625786 100644 --- a/app/vendor/composer/composer/src/Composer/Command/RunScriptCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/RunScriptCommand.php @@ -15,11 +15,11 @@ use Composer\Script\Event as ScriptEvent; use Composer\Script\ScriptEvents; use Composer\Util\ProcessExecutor; +use Composer\Util\Platform; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Helper\Table; /** * @author Fabien Potencier @@ -27,7 +27,7 @@ class RunScriptCommand extends BaseCommand { /** - * @var array Array with command events + * @var string[] Array with command events */ protected $scriptEvents = array( ScriptEvents::PRE_INSTALL_CMD, @@ -74,7 +74,8 @@ protected function execute(InputInterface $input, OutputInterface $output) { if ($input->getOption('list')) { return $this->listScripts($output); - } elseif (!$input->getArgument('script')) { + } + if (!$input->getArgument('script')) { throw new \RuntimeException('Missing required argument "script"'); } @@ -103,6 +104,8 @@ protected function execute(InputInterface $input, OutputInterface $output) ProcessExecutor::setTimeout((int) $timeout); } + Platform::putEnv('COMPOSER_DEV_MODE', $devMode ? '1' : '0'); + return $composer->getEventDispatcher()->dispatchScript($script, $devMode, $args); } @@ -130,16 +133,7 @@ protected function listScripts(OutputInterface $output) $table[] = array(' '.$name, $description); } - $renderer = new Table($output); - $renderer->setStyle('compact'); - $rendererStyle = $renderer->getStyle(); - if (method_exists($rendererStyle, 'setVerticalBorderChars')) { - $rendererStyle->setVerticalBorderChars(''); - } else { - $rendererStyle->setVerticalBorderChar(''); - } - $rendererStyle->setCellRowContentFormat('%s '); - $renderer->setRows($table)->render(); + $this->renderTable($table, $output); return 0; } diff --git a/app/vendor/composer/composer/src/Composer/Command/ScriptAliasCommand.php b/app/vendor/composer/composer/src/Composer/Command/ScriptAliasCommand.php index 455f7420c..63f0f9e6a 100644 --- a/app/vendor/composer/composer/src/Composer/Command/ScriptAliasCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/ScriptAliasCommand.php @@ -22,7 +22,9 @@ */ class ScriptAliasCommand extends BaseCommand { + /** @var string */ private $script; + /** @var string */ private $description; public function __construct($script, $description) diff --git a/app/vendor/composer/composer/src/Composer/Command/SearchCommand.php b/app/vendor/composer/composer/src/Composer/Command/SearchCommand.php index 0e8aa60e4..8b51d530a 100644 --- a/app/vendor/composer/composer/src/Composer/Command/SearchCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/SearchCommand.php @@ -13,6 +13,7 @@ namespace Composer\Command; use Composer\Factory; +use Composer\Json\JsonFile; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; @@ -28,12 +29,6 @@ */ class SearchCommand extends BaseCommand { - protected $matches; - protected $lowMatches = array(); - protected $tokens; - protected $output; - protected $onlyName; - protected function configure() { $this @@ -42,6 +37,7 @@ protected function configure() ->setDefinition(array( new InputOption('only-name', 'N', InputOption::VALUE_NONE, 'Search only in name'), new InputOption('type', 't', InputOption::VALUE_REQUIRED, 'Search for a specific package type'), + new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'), new InputArgument('tokens', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'tokens to search for'), )) ->setHelp( @@ -60,6 +56,14 @@ protected function execute(InputInterface $input, OutputInterface $output) // init repos $platformRepo = new PlatformRepository; $io = $this->getIO(); + + $format = $input->getOption('format'); + if (!in_array($format, array('text', 'json'))) { + $io->writeError(sprintf('Unsupported format "%s". See help for supported formats.', $format)); + + return 1; + } + if (!($composer = $this->getComposer(false))) { $composer = Factory::create($this->getIO(), array(), $input->hasParameterOption('--no-plugins')); } @@ -76,8 +80,25 @@ protected function execute(InputInterface $input, OutputInterface $output) $flags = $onlyName ? RepositoryInterface::SEARCH_NAME : RepositoryInterface::SEARCH_FULLTEXT; $results = $repos->search(implode(' ', $input->getArgument('tokens')), $flags, $type); - foreach ($results as $result) { - $io->write($result['name'] . (isset($result['description']) ? ' '. $result['description'] : '')); + if ($results && $format === 'text') { + $width = $this->getTerminalWidth(); + + $nameLength = 0; + foreach ($results as $result) { + $nameLength = max(strlen($result['name']), $nameLength); + } + $nameLength += 1; + foreach ($results as $result) { + $description = isset($result['description']) ? $result['description'] : ''; + $remaining = $width - $nameLength - 2; + if (strlen($description) > $remaining) { + $description = substr($description, 0, $remaining - 3) . '...'; + } + + $io->write(str_pad($result['name'], $nameLength, ' ') . $description); + } + } elseif ($format === 'json') { + $io->write(JsonFile::encode($results)); } return 0; diff --git a/app/vendor/composer/composer/src/Composer/Command/SelfUpdateCommand.php b/app/vendor/composer/composer/src/Composer/Command/SelfUpdateCommand.php index 686a48b02..70e65d252 100644 --- a/app/vendor/composer/composer/src/Composer/Command/SelfUpdateCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/SelfUpdateCommand.php @@ -21,6 +21,7 @@ use Composer\SelfUpdate\Versions; use Composer\IO\IOInterface; use Composer\Downloader\FilesystemException; +use Composer\Downloader\TransportException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; @@ -80,9 +81,9 @@ protected function execute(InputInterface $input, OutputInterface $output) } $io = $this->getIO(); - $remoteFilesystem = Factory::createRemoteFilesystem($io, $config); + $httpDownloader = Factory::createHttpDownloader($io, $config); - $versionsUtil = new Versions($config, $remoteFilesystem); + $versionsUtil = new Versions($config, $httpDownloader); // switch channel if requested $requestedChannel = null; @@ -124,8 +125,8 @@ protected function execute(InputInterface $input, OutputInterface $output) if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) { $composeUser = posix_getpwuid(posix_geteuid()); $homeOwner = posix_getpwuid(fileowner($home)); - if (isset($composeUser['name']) && isset($homeOwner['name']) && $composeUser['name'] !== $homeOwner['name']) { - $io->writeError('You are running composer as "'.$composeUser['name'].'", while "'.$home.'" is owned by "'.$homeOwner['name'].'"'); + if (isset($composeUser['name'], $homeOwner['name']) && $composeUser['name'] !== $homeOwner['name']) { + $io->writeError('You are running Composer as "'.$composeUser['name'].'", while "'.$home.'" is owned by "'.$homeOwner['name'].'"'); } } @@ -166,7 +167,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } } - if ($requestedChannel && is_numeric($requestedChannel) && substr($latestStable['version'], 0, 1) !== $requestedChannel) { + if ($requestedChannel && is_numeric($requestedChannel) && strpos($latestStable['version'], $requestedChannel) !== 0) { $io->writeError('Warning: You forced the install of '.$latestVersion.' via --'.$requestedChannel.', but '.$latestStable['version'].' is the latest stable version. Updating to it via composer self-update --stable is recommended.'); } @@ -184,7 +185,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (Composer::VERSION === $updateVersion) { $io->writeError( sprintf( - 'You are already using composer version %s (%s channel).', + 'You are already using the latest available Composer version %s (%s channel).', $updateVersion, $channelString ) @@ -209,11 +210,18 @@ protected function execute(InputInterface $input, OutputInterface $output) $updatingToTag = !preg_match('{^[0-9a-f]{40}$}', $updateVersion); - $io->write(sprintf("Updating to version %s (%s channel).", $updateVersion, $channelString)); + $io->write(sprintf("Upgrading to version %s (%s channel).", $updateVersion, $channelString)); $remoteFilename = $baseUrl . ($updatingToTag ? "/download/{$updateVersion}/composer.phar" : '/composer.phar'); - $signature = $remoteFilesystem->getContents(self::HOMEPAGE, $remoteFilename.'.sig', false); + try { + $signature = $httpDownloader->get($remoteFilename.'.sig')->getBody(); + } catch (TransportException $e) { + if ($e->getStatusCode() === 404) { + throw new \InvalidArgumentException('Version "'.$updateVersion.'" could not be found.', 0, $e); + } + throw $e; + } $io->writeError(' ', false); - $remoteFilesystem->copy(self::HOMEPAGE, $remoteFilename, $tempFilename, !$input->getOption('no-progress')); + $httpDownloader->copy($remoteFilename, $tempFilename); $io->writeError(''); if (!file_exists($tempFilename) || !$signature) { @@ -373,7 +381,7 @@ protected function rollback(OutputInterface $output, $rollbackDir, $localFilenam if (!is_file($oldFile)) { throw new FilesystemException('Composer rollback failed: "'.$oldFile.'" could not be found'); } - if (!is_readable($oldFile)) { + if (!Filesystem::isReadable($oldFile)) { throw new FilesystemException('Composer rollback failed: "'.$oldFile.'" could not be read'); } @@ -389,11 +397,11 @@ protected function rollback(OutputInterface $output, $rollbackDir, $localFilenam /** * Checks if the downloaded/rollback phar is valid then moves it * - * @param string $localFilename The composer.phar location - * @param string $newFilename The downloaded or backup phar - * @param string $backupTarget The filename to use for the backup - * @throws \FilesystemException If the file cannot be moved - * @return bool Whether the phar is valid and has been moved + * @param string $localFilename The composer.phar location + * @param string $newFilename The downloaded or backup phar + * @param string $backupTarget The filename to use for the backup + * @throws FilesystemException If the file cannot be moved + * @return bool Whether the phar is valid and has been moved */ protected function setLocalPhar($localFilename, $newFilename, $backupTarget = null) { @@ -417,7 +425,14 @@ protected function setLocalPhar($localFilename, $newFilename, $backupTarget = nu } try { - rename($newFilename, $localFilename); + if (Platform::isWindows()) { + // use copy to apply permissions from the destination directory + // as rename uses source permissions and may block other users + copy($newFilename, $localFilename); + @unlink($newFilename); + } else { + rename($newFilename, $localFilename); + } return true; } catch (\Exception $e) { @@ -464,26 +479,24 @@ protected function getLastBackupVersion($rollbackDir) protected function getOldInstallationFinder($rollbackDir) { - $finder = Finder::create() + return Finder::create() ->depth(0) ->files() ->name('*' . self::OLD_INSTALL_EXT) ->in($rollbackDir); - - return $finder; } /** * Validates the downloaded/backup phar file * - * @param string $pharFile The downloaded or backup phar - * @param null|string $error Set by method on failure + * @param string $pharFile The downloaded or backup phar + * @param null|string $error Set by method on failure * * Code taken from getcomposer.org/installer. Any changes should be made * there and replicated here * - * @return bool If the operation succeeded * @throws \Exception + * @return bool If the operation succeeded */ protected function validatePhar($pharFile, &$error) { @@ -528,11 +541,11 @@ protected function isWindowsNonAdminUser() /** * Invokes a UAC prompt to update composer.phar as an admin * - * Uses a .vbs script to elevate and run the cmd.exe move command. + * Uses a .vbs script to elevate and run the cmd.exe copy command. * - * @param string $localFilename The composer.phar location - * @param string $newFilename The downloaded or backup phar - * @return bool Whether composer.phar has been updated + * @param string $localFilename The composer.phar location + * @param string $newFilename The downloaded or backup phar + * @return bool Whether composer.phar has been updated */ protected function tryAsWindowsAdmin($localFilename, $newFilename) { @@ -554,13 +567,13 @@ protected function tryAsWindowsAdmin($localFilename, $newFilename) $checksum = hash_file('sha256', $newFilename); - // cmd's internal move is fussy about backslashes + // cmd's internal copy is fussy about backslashes $source = str_replace('/', '\\', $newFilename); $destination = str_replace('/', '\\', $localFilename); $vbs = <<writeError('Operation succeeded.'); + @unlink($newFilename); } else { - $io->writeError('Operation failed (file not written). '.$helpMessage.''); - }; + $io->writeError('Operation failed.'.$helpMessage.''); + } return $result; } diff --git a/app/vendor/composer/composer/src/Composer/Command/ShowCommand.php b/app/vendor/composer/composer/src/Composer/Command/ShowCommand.php index 40d4f5981..75fb8ffdc 100644 --- a/app/vendor/composer/composer/src/Composer/Command/ShowCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/ShowCommand.php @@ -14,31 +14,34 @@ use Composer\Composer; use Composer\DependencyResolver\DefaultPolicy; -use Composer\DependencyResolver\Pool; use Composer\Json\JsonFile; use Composer\Package\BasePackage; use Composer\Package\CompletePackageInterface; +use Composer\Package\Link; +use Composer\Package\AliasPackage; use Composer\Package\PackageInterface; use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionSelector; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; -use Composer\Repository\ArrayRepository; +use Composer\Repository\InstalledArrayRepository; use Composer\Repository\ComposerRepository; use Composer\Repository\CompositeRepository; +use Composer\Repository\FilterRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryFactory; +use Composer\Repository\InstalledRepository; use Composer\Repository\RepositoryInterface; +use Composer\Repository\RepositorySet; +use Composer\Repository\RootPackageRepository; use Composer\Semver\Constraint\ConstraintInterface; use Composer\Semver\Semver; use Composer\Spdx\SpdxLicenses; -use Composer\Util\Platform; use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Terminal; /** * @author Robert Schönthal @@ -50,10 +53,11 @@ class ShowCommand extends BaseCommand { /** @var VersionParser */ protected $versionParser; + /** @var string[] */ protected $colors; - /** @var Pool */ - private $pool; + /** @var ?RepositorySet */ + private $repositorySet; protected function configure() { @@ -65,6 +69,7 @@ protected function configure() new InputArgument('package', InputArgument::OPTIONAL, 'Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.'), new InputArgument('version', InputArgument::OPTIONAL, 'Version or version constraint to inspect'), new InputOption('all', null, InputOption::VALUE_NONE, 'List all packages'), + new InputOption('locked', null, InputOption::VALUE_NONE, 'List all locked packages'), new InputOption('installed', 'i', InputOption::VALUE_NONE, 'List installed packages only (enabled by default, only present for BC).'), new InputOption('platform', 'p', InputOption::VALUE_NONE, 'List platform packages only'), new InputOption('available', 'a', InputOption::VALUE_NONE, 'List available packages only'), @@ -79,6 +84,7 @@ protected function configure() new InputOption('direct', 'D', InputOption::VALUE_NONE, 'Shows only packages that are directly required by the root package'), new InputOption('strict', null, InputOption::VALUE_NONE, 'Return a non-zero exit code when there are outdated packages'), new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'), + new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables search in require-dev packages.'), )) ->setHelp( <<getConfig()->get('platform') ?: array(); } $platformRepo = new PlatformRepository(array(), $platformOverrides); - $phpVersion = $platformRepo->findPackage('php', '*')->getVersion(); + $lockedRepo = null; if ($input->getOption('self')) { $package = $this->getComposer()->getPackage(); if ($input->getOption('name-only')) { $io->write($package->getName()); + return 0; } - $repos = $installedRepo = new ArrayRepository(array($package)); + $repos = $installedRepo = new InstalledRepository(array(new RootPackageRepository($package))); } elseif ($input->getOption('platform')) { - $repos = $installedRepo = $platformRepo; + $repos = $installedRepo = new InstalledRepository(array($platformRepo)); } elseif ($input->getOption('available')) { - $installedRepo = $platformRepo; + $installedRepo = new InstalledRepository(array($platformRepo)); if ($composer) { $repos = new CompositeRepository($composer->getRepositoryManager()->getRepositories()); + $installedRepo->addRepository($composer->getRepositoryManager()->getLocalRepository()); } else { $defaultRepos = RepositoryFactory::defaultRepos($io); $repos = new CompositeRepository($defaultRepos); @@ -170,16 +178,41 @@ protected function execute(InputInterface $input, OutputInterface $output) } } elseif ($input->getOption('all') && $composer) { $localRepo = $composer->getRepositoryManager()->getLocalRepository(); - $installedRepo = new CompositeRepository(array($localRepo, $platformRepo)); - $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories())); + $locker = $composer->getLocker(); + if ($locker->isLocked()) { + $lockedRepo = $locker->getLockedRepository(true); + $installedRepo = new InstalledRepository(array($lockedRepo, $localRepo, $platformRepo)); + } else { + $installedRepo = new InstalledRepository(array($localRepo, $platformRepo)); + } + $repos = new CompositeRepository(array_merge(array(new FilterRepository($installedRepo, array('canonical' => false))), $composer->getRepositoryManager()->getRepositories())); } elseif ($input->getOption('all')) { $defaultRepos = RepositoryFactory::defaultRepos($io); $io->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos))); - $installedRepo = $platformRepo; + $installedRepo = new InstalledRepository(array($platformRepo)); $repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos)); + } elseif ($input->getOption('locked')) { + if (!$composer || !$composer->getLocker()->isLocked()) { + throw new \UnexpectedValueException('A valid composer.json and composer.lock files is required to run this command with --locked'); + } + $locker = $composer->getLocker(); + $lockedRepo = $locker->getLockedRepository(!$input->getOption('no-dev')); + $repos = $installedRepo = new InstalledRepository(array($lockedRepo)); } else { - $repos = $installedRepo = $this->getComposer()->getRepositoryManager()->getLocalRepository(); - $rootPkg = $this->getComposer()->getPackage(); + // --installed / default case + if (!$composer) { + $composer = $this->getComposer(); + } + $rootPkg = $composer->getPackage(); + $repos = $installedRepo = new InstalledRepository(array($composer->getRepositoryManager()->getLocalRepository())); + + if ($input->getOption('no-dev')) { + $packages = $this->filterRequiredPackages($installedRepo, $rootPkg); + $repos = $installedRepo = new InstalledRepository(array(new InstalledArrayRepository(array_map(function ($pkg) { + return clone $pkg; + }, $packages)))); + } + if (!$installedRepo->getPackages() && ($rootPkg->getRequires() || $rootPkg->getDevRequires())) { $io->writeError('No dependencies installed. Try running composer install or update.'); } @@ -205,7 +238,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (empty($package)) { $options = $input->getOptions(); if (!isset($options['working-dir']) || !file_exists('composer.json')) { - if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $input->getArgument('package')) && !$input->getOption('platform')) { + if (PlatformRepository::isPlatformPackage($input->getArgument('package')) && !$input->getOption('platform')) { throw new \InvalidArgumentException('Package ' . $packageFilter . ' not found, try using --platform (-p) to show platform packages.'); } throw new \InvalidArgumentException('Package ' . $packageFilter . ' not found'); @@ -231,9 +264,15 @@ protected function execute(InputInterface $input, OutputInterface $output) } else { $latestPackage = null; if ($input->getOption('latest')) { - $latestPackage = $this->findLatestPackage($package, $composer, $phpVersion); + $latestPackage = $this->findLatestPackage($package, $composer, $platformRepo, $input->getOption('minor-only')); } - if ($input->getOption('outdated') && $input->getOption('strict') && $latestPackage && $latestPackage->getFullPrettyVersion() !== $package->getFullPrettyVersion() && !$latestPackage->isAbandoned()) { + if ( + $input->getOption('outdated') + && $input->getOption('strict') + && $latestPackage + && $latestPackage->getFullPrettyVersion() !== $package->getFullPrettyVersion() + && (!$latestPackage instanceof CompletePackageInterface || !$latestPackage->isAbandoned()) + ) { $exitCode = 1; } if ($input->getOption('path')) { @@ -274,16 +313,11 @@ protected function execute(InputInterface $input, OutputInterface $output) return 0; } - if ($repos instanceof CompositeRepository) { - $repos = $repos->getRepositories(); - } elseif (!is_array($repos)) { - $repos = array($repos); - } - // list packages $packages = array(); + $packageFilterRegex = null; if (null !== $packageFilter) { - $packageFilter = '{^'.str_replace('\\*', '.*?', preg_quote($packageFilter)).'$}i'; + $packageFilterRegex = '{^'.str_replace('\\*', '.*?', preg_quote($packageFilter)).'$}i'; } $packageListFilter = array(); @@ -291,45 +325,24 @@ protected function execute(InputInterface $input, OutputInterface $output) $packageListFilter = $this->getRootRequires(); } - if (class_exists('Symfony\Component\Console\Terminal')) { - $terminal = new Terminal(); - $width = $terminal->getWidth(); - } else { - // For versions of Symfony console before 3.2 - list($width) = $this->getApplication()->getTerminalDimensions(); - } - if (null === $width) { - // In case the width is not detected, we're probably running the command - // outside of a real terminal, use space without a limit - $width = PHP_INT_MAX; - } - if (Platform::isWindows()) { - $width--; - } else { - $width = max(80, $width); - } - if ($input->getOption('path') && null === $composer) { $io->writeError('No composer.json found in the current directory, disabling "path" option'); $input->setOption('path', false); } - foreach ($repos as $repo) { + foreach ($repos->getRepositories() as $repo) { if ($repo === $platformRepo) { $type = 'platform'; - } elseif ( - $repo === $installedRepo - || ($installedRepo instanceof CompositeRepository && in_array($repo, $installedRepo->getRepositories(), true)) - ) { + } elseif ($lockedRepo !== null && $repo === $lockedRepo) { + $type = 'locked'; + } elseif ($repo === $installedRepo || in_array($repo, $installedRepo->getRepositories(), true)) { $type = 'installed'; } else { $type = 'available'; } - if ($repo instanceof ComposerRepository && $repo->hasProviders()) { - foreach ($repo->getProviderNames() as $name) { - if (!$packageFilter || preg_match($packageFilter, $name)) { - $packages[$type][$name] = $name; - } + if ($repo instanceof ComposerRepository) { + foreach ($repo->getPackageNames($packageFilter) as $name) { + $packages[$type][$name] = $name; } } else { foreach ($repo->getPackages() as $package) { @@ -337,7 +350,10 @@ protected function execute(InputInterface $input, OutputInterface $output) || !is_object($packages[$type][$package->getName()]) || version_compare($packages[$type][$package->getName()]->getVersion(), $package->getVersion(), '<') ) { - if (!$packageFilter || preg_match($packageFilter, $package->getName())) { + while ($package instanceof AliasPackage) { + $package = $package->getAliasOf(); + } + if (!$packageFilterRegex || preg_match($packageFilterRegex, $package->getName())) { if (!$packageListFilter || in_array($package->getName(), $packageListFilter, true)) { $packages[$type][$package->getName()] = $package; } @@ -352,11 +368,12 @@ protected function execute(InputInterface $input, OutputInterface $output) $showMinorOnly = $input->getOption('minor-only'); $ignoredPackages = array_map('strtolower', $input->getOption('ignore')); $indent = $showAllTypes ? ' ' : ''; + /** @var PackageInterface[] $latestPackages */ $latestPackages = array(); $exitCode = 0; $viewData = array(); $viewMetaData = array(); - foreach (array('platform' => true, 'available' => false, 'installed' => true) as $type => $showVersion) { + foreach (array('platform' => true, 'locked' => true, 'available' => false, 'installed' => true) as $type => $showVersion) { if (isset($packages[$type])) { ksort($packages[$type]); @@ -365,7 +382,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if ($showLatest && $showVersion) { foreach ($packages[$type] as $package) { if (is_object($package)) { - $latestPackage = $this->findLatestPackage($package, $composer, $phpVersion, $showMinorOnly); + $latestPackage = $this->findLatestPackage($package, $composer, $platformRepo, $showMinorOnly); if ($latestPackage === false) { continue; } @@ -392,11 +409,13 @@ protected function execute(InputInterface $input, OutputInterface $output) } // Determine if Composer is checking outdated dependencies and if current package should trigger non-default exit code - $packageIsUpToDate = $latestPackage && $latestPackage->getFullPrettyVersion() === $package->getFullPrettyVersion() && !$latestPackage->isAbandoned(); + $packageIsUpToDate = $latestPackage && $latestPackage->getFullPrettyVersion() === $package->getFullPrettyVersion() && (!$latestPackage instanceof CompletePackageInterface || !$latestPackage->isAbandoned()); $packageIsIgnored = \in_array($package->getPrettyName(), $ignoredPackages, true); if ($input->getOption('outdated') && ($packageIsUpToDate || $packageIsIgnored)) { continue; - } elseif ($input->getOption('outdated') || $input->getOption('strict')) { + } + + if ($input->getOption('outdated') || $input->getOption('strict')) { $hasOutdatedPackages = true; } @@ -418,7 +437,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $packageViewData['path'] = strtok(realpath($composer->getInstallationManager()->getInstallPath($package)), "\r\n"); } - if ($latestPackage && $latestPackage->isAbandoned()) { + if ($latestPackage instanceof CompletePackageInterface && $latestPackage->isAbandoned()) { $replacement = is_string($latestPackage->getReplacementPackage()) ? 'Use ' . $latestPackage->getReplacementPackage() . ' instead' : 'No replacement was suggested'; @@ -450,6 +469,26 @@ protected function execute(InputInterface $input, OutputInterface $output) if ('json' === $format) { $io->write(JsonFile::encode($viewData)); } else { + if ($input->getOption('latest') && array_filter($viewData)) { + if (!$io->isDecorated()) { + $io->writeError('Legend:'); + $io->writeError('! patch or minor release available - update recommended'); + $io->writeError('~ major release available - update possible'); + if (!$input->getOption('outdated')) { + $io->writeError('= up to date version'); + } + } else { + $io->writeError('Color legend:'); + $io->writeError('- patch or minor release available - update recommended'); + $io->writeError('- major release available - update possible'); + if (!$input->getOption('outdated')) { + $io->writeError('- up to date version'); + } + } + } + + $width = $this->getTerminalWidth(); + foreach ($viewData as $type => $packages) { $nameLength = $viewMetaData[$type]['nameLength']; $versionLength = $viewMetaData[$type]['versionLength']; @@ -532,32 +571,32 @@ protected function getVersionStyle(PackageInterface $latestPackage, PackageInter /** * finds a package by name and version if provided * - * @param RepositoryInterface $installedRepo + * @param InstalledRepository $installedRepo * @param RepositoryInterface $repos * @param string $name * @param ConstraintInterface|string $version * @throws \InvalidArgumentException * @return array array(CompletePackageInterface, array of versions) */ - protected function getPackage(RepositoryInterface $installedRepo, RepositoryInterface $repos, $name, $version = null) + protected function getPackage(InstalledRepository $installedRepo, RepositoryInterface $repos, $name, $version = null) { $name = strtolower($name); $constraint = is_string($version) ? $this->versionParser->parseConstraints($version) : $version; $policy = new DefaultPolicy(); - $pool = new Pool('dev'); - $pool->addRepository($repos); + $repositorySet = new RepositorySet('dev'); + $repositorySet->allowInstalledRepositories(); + $repositorySet->addRepository($repos); $matchedPackage = null; $versions = array(); + if (PlatformRepository::isPlatformPackage($name)) { + $pool = $repositorySet->createPoolWithAllPackages(); + } else { + $pool = $repositorySet->createPoolForPackage($name); + } $matches = $pool->whatProvides($name, $constraint); foreach ($matches as $index => $package) { - // skip providers/replacers - if ($package->getName() !== $name) { - unset($matches[$index]); - continue; - } - // select an exact match if it is in the installed repo and no specific version was required if (null === $version && $installedRepo->hasPackage($package)) { $matchedPackage = $package; @@ -568,7 +607,7 @@ protected function getPackage(RepositoryInterface $installedRepo, RepositoryInte } // select preferred package according to policy rules - if (!$matchedPackage && $matches && $preferred = $policy->selectPreferredPackages($pool, array(), $matches)) { + if (!$matchedPackage && $matches && $preferred = $policy->selectPreferredPackages($pool, $matches)) { $matchedPackage = $pool->literalToPackage($preferred[0]); } @@ -580,16 +619,16 @@ protected function getPackage(RepositoryInterface $installedRepo, RepositoryInte * * @param CompletePackageInterface $package * @param array $versions - * @param RepositoryInterface $installedRepo + * @param InstalledRepository $installedRepo * @param PackageInterface|null $latestPackage */ - protected function printPackageInfo(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, PackageInterface $latestPackage = null) + protected function printPackageInfo(CompletePackageInterface $package, array $versions, InstalledRepository $installedRepo, PackageInterface $latestPackage = null) { $io = $this->getIO(); $this->printMeta($package, $versions, $installedRepo, $latestPackage ?: null); - $this->printLinks($package, 'requires'); - $this->printLinks($package, 'devRequires', 'requires (dev)'); + $this->printLinks($package, Link::TYPE_REQUIRE); + $this->printLinks($package, Link::TYPE_DEV_REQUIRE, 'requires (dev)'); if ($package->getSuggests()) { $io->write("\nsuggests"); @@ -598,9 +637,9 @@ protected function printPackageInfo(CompletePackageInterface $package, array $ve } } - $this->printLinks($package, 'provides'); - $this->printLinks($package, 'conflicts'); - $this->printLinks($package, 'replaces'); + $this->printLinks($package, Link::TYPE_PROVIDE); + $this->printLinks($package, Link::TYPE_CONFLICT); + $this->printLinks($package, Link::TYPE_REPLACE); } /** @@ -608,10 +647,10 @@ protected function printPackageInfo(CompletePackageInterface $package, array $ve * * @param CompletePackageInterface $package * @param array $versions - * @param RepositoryInterface $installedRepo + * @param InstalledRepository $installedRepo * @param PackageInterface|null $latestPackage */ - protected function printMeta(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, PackageInterface $latestPackage = null) + protected function printMeta(CompletePackageInterface $package, array $versions, InstalledRepository $installedRepo, PackageInterface $latestPackage = null) { $io = $this->getIO(); $io->write('name : ' . $package->getPrettyName()); @@ -634,7 +673,7 @@ protected function printMeta(CompletePackageInterface $package, array $versions, } $io->write('names : ' . implode(', ', $package->getNames())); - if ($latestPackage->isAbandoned()) { + if ($latestPackage instanceof CompletePackageInterface && $latestPackage->isAbandoned()) { $replacement = ($latestPackage->getReplacementPackage() !== null) ? ' The author suggests using the ' . $latestPackage->getReplacementPackage(). ' package instead.' : null; @@ -656,11 +695,7 @@ protected function printMeta(CompletePackageInterface $package, array $versions, foreach ($package->getAutoload() as $type => $autoloads) { $io->write('' . $type . ''); - if ($type === 'psr-0') { - foreach ($autoloads as $name => $path) { - $io->write(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.'))); - } - } elseif ($type === 'psr-4') { + if ($type === 'psr-0' || $type === 'psr-4') { foreach ($autoloads as $name => $path) { $io->write(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.'))); } @@ -680,19 +715,21 @@ protected function printMeta(CompletePackageInterface $package, array $versions, * * @param CompletePackageInterface $package * @param array $versions - * @param RepositoryInterface $installedRepo + * @param InstalledRepository $installedRepo */ - protected function printVersions(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo) + protected function printVersions(CompletePackageInterface $package, array $versions, InstalledRepository $installedRepo) { - uasort($versions, 'version_compare'); - $versions = array_keys(array_reverse($versions)); + $versions = array_keys($versions); + $versions = Semver::rsort($versions); // highlight installed version - if ($installedRepo->hasPackage($package)) { - $installedVersion = $package->getPrettyVersion(); - $key = array_search($installedVersion, $versions); - if (false !== $key) { - $versions[$key] = '* ' . $installedVersion . ''; + if ($installedPackages = $installedRepo->findPackages($package->getName())) { + foreach ($installedPackages as $installedPackage) { + $installedVersion = $installedPackage->getPrettyVersion(); + $key = array_search($installedVersion, $versions); + if (false !== $key) { + $versions[$key] = '* ' . $installedVersion . ''; + } } } @@ -756,10 +793,10 @@ protected function printLicenses(CompletePackageInterface $package) * * @param CompletePackageInterface $package * @param array $versions - * @param RepositoryInterface $installedRepo + * @param InstalledRepository $installedRepo * @param PackageInterface|null $latestPackage */ - protected function printPackageInfoAsJson(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, PackageInterface $latestPackage = null) + protected function printPackageInfoAsJson(CompletePackageInterface $package, array $versions, InstalledRepository $installedRepo, PackageInterface $latestPackage = null) { $json = array( 'name' => $package->getPrettyName(), @@ -767,7 +804,7 @@ protected function printPackageInfoAsJson(CompletePackageInterface $package, arr 'keywords' => $package->getKeywords() ?: array(), 'type' => $package->getType(), 'homepage' => $package->getHomepage(), - 'names' => $package->getNames() + 'names' => $package->getNames(), ); $json = $this->appendVersions($json, $versions); @@ -783,7 +820,7 @@ protected function printPackageInfoAsJson(CompletePackageInterface $package, arr $json['source'] = array( 'type' => $package->getSourceType(), 'url' => $package->getSourceUrl(), - 'reference' => $package->getSourceReference() + 'reference' => $package->getSourceReference(), ); } @@ -791,7 +828,7 @@ protected function printPackageInfoAsJson(CompletePackageInterface $package, arr $json['dist'] = array( 'type' => $package->getDistType(), 'url' => $package->getDistUrl(), - 'reference' => $package->getDistReference() + 'reference' => $package->getDistReference(), ); } @@ -802,7 +839,7 @@ protected function printPackageInfoAsJson(CompletePackageInterface $package, arr } } - if ($latestPackage->isAbandoned()) { + if ($latestPackage instanceof CompletePackageInterface && $latestPackage->isAbandoned()) { $json['replacement'] = $latestPackage->getReplacementPackage(); } @@ -849,7 +886,7 @@ private function appendLicenses($json, CompletePackageInterface $package) return array( 'name' => $license[0], 'osi' => $licenseId, - 'url' => $license[2] + 'url' => $license[2], ); }, $licenses); } @@ -888,7 +925,7 @@ private function appendAutoload($json, CompletePackageInterface $package) private function appendLinks($json, CompletePackageInterface $package) { - foreach (array('requires', 'devRequires', 'provides', 'conflicts', 'replaces') as $linkType) { + foreach (Link::$TYPES as $linkType) { $json = $this->appendLink($json, $package, $linkType); } @@ -979,15 +1016,15 @@ protected function displayPackageTree(array $arrayTree) /** * Generate the package tree * - * @param PackageInterface $package - * @param RepositoryInterface $installedRepo - * @param RepositoryInterface $distantRepos + * @param PackageInterface $package + * @param InstalledRepository $installedRepo + * @param RepositoryInterface $remoteRepos * @return array */ protected function generatePackageTree( PackageInterface $package, - RepositoryInterface $installedRepo, - RepositoryInterface $distantRepos + InstalledRepository $installedRepo, + RepositoryInterface $remoteRepos ) { $requires = $package->getRequires(); ksort($requires); @@ -1000,7 +1037,7 @@ protected function generatePackageTree( 'version' => $require->getPrettyConstraint(), ); - $deepChildren = $this->addTree($requireName, $require, $installedRepo, $distantRepos, $packagesInTree); + $deepChildren = $this->addTree($requireName, $require, $installedRepo, $remoteRepos, $packagesInTree); if ($deepChildren) { $treeChildDesc['requires'] = $deepChildren; @@ -1011,7 +1048,7 @@ protected function generatePackageTree( $tree = array( 'name' => $package->getPrettyName(), 'version' => $package->getPrettyVersion(), - 'description' => $package->getDescription(), + 'description' => $package instanceof CompletePackageInterface ? $package->getDescription() : '', ); if ($children) { @@ -1024,10 +1061,10 @@ protected function generatePackageTree( /** * Display a package tree * - * @param PackageInterface|string $package - * @param array $packagesInTree - * @param string $previousTreeBar - * @param int $level + * @param array|string $package + * @param array $packagesInTree + * @param string $previousTreeBar + * @param int $level */ protected function displayTree( $package, @@ -1036,7 +1073,7 @@ protected function displayTree( $level = 1 ) { $previousTreeBar = str_replace('├', '│', $previousTreeBar); - if (isset($package['requires'])) { + if (is_array($package) && isset($package['requires'])) { $requires = $package['requires']; $treeBar = $previousTreeBar . ' ├'; $i = 0; @@ -1077,26 +1114,26 @@ protected function displayTree( /** * Display a package tree * - * @param string $name - * @param PackageInterface|string $package - * @param RepositoryInterface $installedRepo - * @param RepositoryInterface $distantRepos - * @param array $packagesInTree + * @param string $name + * @param Link $link + * @param InstalledRepository $installedRepo + * @param RepositoryInterface $remoteRepos + * @param array $packagesInTree * @return array */ protected function addTree( $name, - $package, - RepositoryInterface $installedRepo, - RepositoryInterface $distantRepos, + Link $link, + InstalledRepository $installedRepo, + RepositoryInterface $remoteRepos, array $packagesInTree ) { $children = array(); - list($package, $versions) = $this->getPackage( + list($package) = $this->getPackage( $installedRepo, - $distantRepos, + $remoteRepos, $name, - $package->getPrettyConstraint() === 'self.version' ? $package->getConstraint() : $package->getPrettyConstraint() + $link->getPrettyConstraint() === 'self.version' ? $link->getConstraint() : $link->getPrettyConstraint() ); if (is_object($package)) { $requires = $package->getRequires(); @@ -1111,7 +1148,7 @@ protected function addTree( if (!in_array($requireName, $currentTree, true)) { $currentTree[] = $requireName; - $deepChildren = $this->addTree($requireName, $require, $installedRepo, $distantRepos, $currentTree); + $deepChildren = $this->addTree($requireName, $require, $installedRepo, $remoteRepos, $currentTree); if ($deepChildren) { $treeChildDesc['requires'] = $deepChildren; } @@ -1164,18 +1201,18 @@ private function writeTreeLine($line) /** * Given a package, this finds the latest package matching it * - * @param PackageInterface $package - * @param Composer $composer - * @param string $phpVersion - * @param bool $minorOnly + * @param PackageInterface $package + * @param Composer $composer + * @param PlatformRepository $platformRepo + * @param bool $minorOnly * - * @return PackageInterface|null + * @return PackageInterface|false */ - private function findLatestPackage(PackageInterface $package, Composer $composer, $phpVersion, $minorOnly = false) + private function findLatestPackage(PackageInterface $package, Composer $composer, PlatformRepository $platformRepo, $minorOnly = false) { - // find the latest version allowed in this pool + // find the latest version allowed in this repo set $name = $package->getName(); - $versionSelector = new VersionSelector($this->getPool($composer)); + $versionSelector = new VersionSelector($this->getRepositorySet($composer), $platformRepo); $stability = $composer->getPackage()->getMinimumStability(); $flags = $composer->getPackage()->getStabilityFlags(); if (isset($flags[$name])) { @@ -1196,16 +1233,51 @@ private function findLatestPackage(PackageInterface $package, Composer $composer $targetVersion = '^' . $package->getVersion(); } - return $versionSelector->findBestCandidate($name, $targetVersion, $phpVersion, $bestStability); + $candidate = $versionSelector->findBestCandidate($name, $targetVersion, $bestStability); + while ($candidate instanceof AliasPackage) { + $candidate = $candidate->getAliasOf(); + } + + return $candidate; } - private function getPool(Composer $composer) + /** + * @return RepositorySet + */ + private function getRepositorySet(Composer $composer) { - if (!$this->pool) { - $this->pool = new Pool($composer->getPackage()->getMinimumStability(), $composer->getPackage()->getStabilityFlags()); - $this->pool->addRepository(new CompositeRepository($composer->getRepositoryManager()->getRepositories())); + if (!$this->repositorySet) { + $this->repositorySet = new RepositorySet($composer->getPackage()->getMinimumStability(), $composer->getPackage()->getStabilityFlags()); + $this->repositorySet->addRepository(new CompositeRepository($composer->getRepositoryManager()->getRepositories())); + } + + return $this->repositorySet; + } + + /** + * Find package requires and child requires + * + * @param RepositoryInterface $repo + * @param PackageInterface $package + * @param array $bucket + * @return array + */ + private function filterRequiredPackages(RepositoryInterface $repo, PackageInterface $package, $bucket = array()) + { + $requires = $package->getRequires(); + + foreach ($repo->getPackages() as $candidate) { + foreach ($candidate->getNames() as $name) { + if (isset($requires[$name])) { + if (!in_array($candidate, $bucket, true)) { + $bucket[] = $candidate; + $bucket = $this->filterRequiredPackages($repo, $candidate, $bucket); + } + break; + } + } } - return $this->pool; + return $bucket; } } diff --git a/app/vendor/composer/composer/src/Composer/Command/StatusCommand.php b/app/vendor/composer/composer/src/Composer/Command/StatusCommand.php index 6e07ed5d9..ede91ac13 100644 --- a/app/vendor/composer/composer/src/Composer/Command/StatusCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/StatusCommand.php @@ -73,7 +73,7 @@ protected function execute(InputInterface $input, OutputInterface $output) // Dispatch pre-status-command $composer->getEventDispatcher()->dispatchScript(ScriptEvents::PRE_STATUS_CMD, true); - $exitCode = $this->doExecute($input, $output); + $exitCode = $this->doExecute($input); // Dispatch post-status-command $composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_STATUS_CMD, true); @@ -82,11 +82,10 @@ protected function execute(InputInterface $input, OutputInterface $output) } /** - * @param InputInterface $input - * @param OutputInterface $output + * @param InputInterface $input * @return int */ - private function doExecute(InputInterface $input, OutputInterface $output) + private function doExecute(InputInterface $input) { // init repos $composer = $this->getComposer(); @@ -107,7 +106,7 @@ private function doExecute(InputInterface $input, OutputInterface $output) // list packages foreach ($installedRepo->getCanonicalPackages() as $package) { - $downloader = $dm->getDownloaderForInstalledPackage($package); + $downloader = $dm->getDownloaderForPackage($package); $targetDir = $im->getInstallPath($package); if ($downloader instanceof ChangeReportInterface) { @@ -121,7 +120,7 @@ private function doExecute(InputInterface $input, OutputInterface $output) } if ($downloader instanceof VcsCapableDownloaderInterface) { - if ($currentRef = $downloader->getVcsReference($package, $targetDir)) { + if ($downloader->getVcsReference($package, $targetDir)) { switch ($package->getInstallationSource()) { case 'source': $previousRef = $package->getSourceReference(); diff --git a/app/vendor/composer/composer/src/Composer/Command/SuggestsCommand.php b/app/vendor/composer/composer/src/Composer/Command/SuggestsCommand.php index 0769f62f9..cb1f4e9a6 100644 --- a/app/vendor/composer/composer/src/Composer/Command/SuggestsCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/SuggestsCommand.php @@ -13,6 +13,9 @@ namespace Composer\Command; use Composer\Repository\PlatformRepository; +use Composer\Repository\RootPackageRepository; +use Composer\Repository\InstalledRepository; +use Composer\Installer\SuggestedPackagesReporter; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -26,8 +29,10 @@ protected function configure() ->setName('suggests') ->setDescription('Shows package suggestions.') ->setDefinition(array( - new InputOption('by-package', null, InputOption::VALUE_NONE, 'Groups output by suggesting package'), + new InputOption('by-package', null, InputOption::VALUE_NONE, 'Groups output by suggesting package (default)'), new InputOption('by-suggestion', null, InputOption::VALUE_NONE, 'Groups output by suggested package'), + new InputOption('all', 'a', InputOption::VALUE_NONE, 'Show suggestions from all dependencies, including transitive ones'), + new InputOption('list', null, InputOption::VALUE_NONE, 'Show only list of suggested package names'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Exclude suggestions from require-dev packages'), new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that you want to list suggestions from.'), )) @@ -36,118 +41,63 @@ protected function configure() The %command.name% command shows a sorted list of suggested packages. -Enabling -v implies --by-package --by-suggestion, showing both lists. - Read more at https://getcomposer.org/doc/03-cli.md#suggests EOT ) ; } + /** + * {@inheritDoc} + */ protected function execute(InputInterface $input, OutputInterface $output) { - $lock = $this->getComposer()->getLocker()->getLockData(); - - if (empty($lock)) { - throw new \RuntimeException('Lockfile seems to be empty?'); + $composer = $this->getComposer(); + + $installedRepos = array( + new RootPackageRepository(clone $composer->getPackage()), + ); + + $locker = $composer->getLocker(); + if ($locker->isLocked()) { + $installedRepos[] = new PlatformRepository(array(), $locker->getPlatformOverrides()); + $installedRepos[] = $locker->getLockedRepository(!$input->getOption('no-dev')); + } else { + $installedRepos[] = new PlatformRepository(array(), $composer->getConfig()->get('platform') ?: array()); + $installedRepos[] = $composer->getRepositoryManager()->getLocalRepository(); } - $packages = $lock['packages']; - - if (!$input->getOption('no-dev')) { - $packages += $lock['packages-dev']; - } + $installedRepo = new InstalledRepository($installedRepos); + $reporter = new SuggestedPackagesReporter($this->getIO()); $filter = $input->getArgument('packages'); - - // First assemble lookup list of packages that are installed, replaced or provided - $installed = array(); + $packages = $installedRepo->getPackages(); + $packages[] = $composer->getPackage(); foreach ($packages as $package) { - $installed[] = $package['name']; - - if (!empty($package['provide'])) { - $installed = array_merge($installed, array_keys($package['provide'])); + if (!empty($filter) && !in_array($package->getName(), $filter)) { + continue; } - if (!empty($package['replace'])) { - $installed = array_merge($installed, array_keys($package['replace'])); - } + $reporter->addSuggestionsFromPackage($package); } - // Undub and sort the install list into a sorted lookup array - $installed = array_flip($installed); - ksort($installed); - - // Init platform repo - $platform = new PlatformRepository(array(), $this->getComposer()->getConfig()->get('platform') ?: array()); + // Determine output mode, default is by-package + $mode = SuggestedPackagesReporter::MODE_BY_PACKAGE; - // Next gather all suggestions that are not in that list - $suggesters = array(); - $suggested = array(); - foreach ($packages as $package) { - $packageName = $package['name']; - if ((!empty($filter) && !in_array($packageName, $filter)) || empty($package['suggest'])) { - continue; - } - foreach ($package['suggest'] as $suggestion => $reason) { - if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $suggestion) && null !== $platform->findPackage($suggestion, '*')) { - continue; - } - if (!isset($installed[$suggestion])) { - $suggesters[$packageName][$suggestion] = $reason; - $suggested[$suggestion][$packageName] = $reason; - } - } - } - ksort($suggesters); - ksort($suggested); - - // Determine output mode - $mode = 0; - $io = $this->getIO(); - if ($input->getOption('by-package') || $io->isVerbose()) { - $mode |= 1; - } + // if by-suggestion is given we override the default if ($input->getOption('by-suggestion')) { - $mode |= 2; + $mode = SuggestedPackagesReporter::MODE_BY_SUGGESTION; } - - // Simple mode - if ($mode === 0) { - foreach (array_keys($suggested) as $suggestion) { - $io->write(sprintf('%s', $suggestion)); - } - - return 0; + // unless by-package is also present then we enable both + if ($input->getOption('by-package')) { + $mode |= SuggestedPackagesReporter::MODE_BY_PACKAGE; } - - // Grouped by package - if ($mode & 1) { - foreach ($suggesters as $suggester => $suggestions) { - $io->write(sprintf('%s suggests:', $suggester)); - - foreach ($suggestions as $suggestion => $reason) { - $io->write(sprintf(' - %s: %s', $suggestion, $reason ?: '*')); - } - $io->write(''); - } + // list is exclusive and overrides everything else + if ($input->getOption('list')) { + $mode = SuggestedPackagesReporter::MODE_LIST; } - // Grouped by suggestion - if ($mode & 2) { - // Improve readability in full mode - if ($mode & 1) { - $io->write(str_repeat('-', 78)); - } - foreach ($suggested as $suggestion => $suggesters) { - $io->write(sprintf('%s is suggested by:', $suggestion)); - - foreach ($suggesters as $suggester => $reason) { - $io->write(sprintf(' - %s: %s', $suggester, $reason ?: '*')); - } - $io->write(''); - } - } + $reporter->output($mode, $installedRepo, empty($filter) && !$input->getOption('all') ? $composer->getPackage() : null); return 0; } diff --git a/app/vendor/composer/composer/src/Composer/Command/UpdateCommand.php b/app/vendor/composer/composer/src/Composer/Command/UpdateCommand.php index 44f1e7dea..0fe582299 100644 --- a/app/vendor/composer/composer/src/Composer/Command/UpdateCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/UpdateCommand.php @@ -13,10 +13,16 @@ namespace Composer\Command; use Composer\Composer; +use Composer\DependencyResolver\Request; use Composer\Installer; use Composer\IO\IOInterface; +use Composer\Package\Loader\RootPackageLoader; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; +use Composer\Package\Version\VersionParser; +use Composer\Util\HttpDownloader; +use Composer\Semver\Constraint\MultiConstraint; +use Composer\Package\Link; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -38,24 +44,28 @@ protected function configure() ->setDescription('Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.') ->setDefinition(array( new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that should be updated, if not provided all packages are.'), + new InputOption('with', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Temporary version constraint to add, e.g. foo/bar:1.0.0 or foo/bar=1.0.0'), new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), - new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), + new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist (default behavior).'), + new InputOption('prefer-install', null, InputOption::VALUE_REQUIRED, 'Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).'), new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), - new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'), + new InputOption('dev', null, InputOption::VALUE_NONE, 'DEPRECATED: Enables installation of require-dev packages (enabled by default, only present for BC).'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'), - new InputOption('lock', null, InputOption::VALUE_NONE, 'Only updates the lock file hash to suppress warning about the lock file being out of date.'), - new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'), + new InputOption('lock', null, InputOption::VALUE_NONE, 'Overwrites the lock file hash to suppress warning about the lock file being out of date without updating package versions. Package metadata like mirrors and URLs are updated if they changed.'), + new InputOption('no-install', null, InputOption::VALUE_NONE, 'Skip the install step after updating the composer.lock file.'), new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), + new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'DEPRECATED: This flag does not exist anymore.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), - new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'), - new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also dependencies of allowed packages to the allow list, except those defined in root package.'), - new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of allowed packages to the allow list, including those defined in root package.'), + new InputOption('with-dependencies', 'w', InputOption::VALUE_NONE, 'Update also dependencies of packages in the argument list, except those which are root requirements.'), + new InputOption('with-all-dependencies', 'W', InputOption::VALUE_NONE, 'Update also dependencies of packages in the argument list, including those which are root requirements.'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('apcu-autoloader-prefix', null, InputOption::VALUE_REQUIRED, 'Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader'), + new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'), new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'), new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'), new InputOption('interactive', 'i', InputOption::VALUE_NONE, 'Interactive interface with autocompletion to select the packages to update.'), @@ -79,6 +89,14 @@ protected function configure() php composer.phar update vendor/package1 foo/* [...] +To run an update with more restrictive constraints you can use: + +php composer.phar update --with vendor/package:1.0.* + +To run a partial update with more restrictive constraints you can use the shorthand: + +php composer.phar update vendor/package:1.0.* + To select packages names interactively with auto-completion use -i. Read more at https://getcomposer.org/doc/03-cli.md#update-u @@ -90,42 +108,93 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $io = $this->getIO(); - if ($input->getOption('no-custom-installers')) { - $io->writeError('You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.'); - $input->setOption('no-plugins', true); - } - if ($input->getOption('dev')) { - $io->writeError('You are using the deprecated option "dev". Dev packages are installed by default now.'); + $io->writeError('You are using the deprecated option "--dev". It has no effect and will break in Composer 3.'); + } + if ($input->getOption('no-suggest')) { + $io->writeError('You are using the deprecated option "--no-suggest". It has no effect and will break in Composer 3.'); } $composer = $this->getComposer(true, $input->getOption('no-plugins')); + $composer->getEventDispatcher()->setRunScripts(!$input->getOption('no-scripts')); + + if (!HttpDownloader::isCurlEnabled()) { + $io->writeError('Composer is operating significantly slower than normal because you do not have the PHP curl extension enabled.'); + } $packages = $input->getArgument('packages'); + $reqs = $this->formatRequirements($input->getOption('with')); + + // extract --with shorthands from the allowlist + if ($packages) { + $allowlistPackagesWithRequirements = array_filter($packages, function ($pkg) { + return preg_match('{\S+[ =:]\S+}', $pkg) > 0; + }); + foreach ($this->formatRequirements($allowlistPackagesWithRequirements) as $package => $constraint) { + $reqs[$package] = $constraint; + } + + // replace the foo/bar:req by foo/bar in the allowlist + foreach ($allowlistPackagesWithRequirements as $package) { + $packageName = preg_replace('{^([^ =:]+)[ =:].*$}', '$1', $package); + $index = array_search($package, $packages); + $packages[$index] = $packageName; + } + } + + $rootPackage = $composer->getPackage(); + $rootRequires = $rootPackage->getRequires(); + $rootDevRequires = $rootPackage->getDevRequires(); + foreach ($reqs as $package => $constraint) { + if (isset($rootRequires[$package])) { + $rootRequires[$package] = $this->appendConstraintToLink($rootRequires[$package], $constraint); + } elseif (isset($rootDevRequires[$package])) { + $rootDevRequires[$package] = $this->appendConstraintToLink($rootDevRequires[$package], $constraint); + } else { + throw new \UnexpectedValueException('Only root package requirements can receive temporary constraints and '.$package.' is not one'); + } + } + $rootPackage->setRequires($rootRequires); + $rootPackage->setDevRequires($rootDevRequires); + $rootPackage->setReferences(RootPackageLoader::extractReferences($reqs, $rootPackage->getReferences())); + $rootPackage->setStabilityFlags(RootPackageLoader::extractStabilityFlags($reqs, $rootPackage->getMinimumStability(), $rootPackage->getStabilityFlags())); if ($input->getOption('interactive')) { $packages = $this->getPackagesInteractively($io, $input, $output, $composer, $packages); } if ($input->getOption('root-reqs')) { - $require = array_keys($composer->getPackage()->getRequires()); + $requires = array_keys($rootRequires); if (!$input->getOption('no-dev')) { - $requireDev = array_keys($composer->getPackage()->getDevRequires()); - $require = array_merge($require, $requireDev); + $requires = array_merge($requires, array_keys($rootDevRequires)); } if (!empty($packages)) { - $packages = array_intersect($packages, $require); + $packages = array_intersect($packages, $requires); } else { - $packages = $require; + $packages = $requires; } } - $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); + // the arguments lock/nothing/mirrors are not package names but trigger a mirror update instead + // they are further mutually exclusive with listing actual package names + $filteredPackages = array_filter($packages, function ($package) { + return !in_array($package, array('lock', 'nothing', 'mirrors'), true); + }); + $updateMirrors = $input->getOption('lock') || count($filteredPackages) != count($packages); + $packages = $filteredPackages; + + if ($updateMirrors && !empty($packages)) { + $io->writeError('You cannot simultaneously update only a selection of packages and regenerate the lock file metadata.'); + + return -1; + } $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'update', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); + $composer->getInstallationManager()->setOutputProgress(!$input->getOption('no-progress')); + $install = Installer::create($io, $composer); $config = $composer->getConfig(); @@ -133,7 +202,17 @@ protected function execute(InputInterface $input, OutputInterface $output) $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader'); $authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative'); - $apcu = $input->getOption('apcu-autoloader') || $config->get('apcu-autoloader'); + $apcuPrefix = $input->getOption('apcu-autoloader-prefix'); + $apcu = $apcuPrefix !== null || $input->getOption('apcu-autoloader') || $config->get('apcu-autoloader'); + + $updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED; + if ($input->getOption('with-all-dependencies')) { + $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS; + } elseif ($input->getOption('with-dependencies')) { + $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; + } + + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); $install ->setDryRun($input->getOption('dry-run')) @@ -142,16 +221,15 @@ protected function execute(InputInterface $input, OutputInterface $output) ->setPreferDist($preferDist) ->setDevMode(!$input->getOption('no-dev')) ->setDumpAutoloader(!$input->getOption('no-autoloader')) - ->setRunScripts(!$input->getOption('no-scripts')) - ->setSkipSuggest($input->getOption('no-suggest')) ->setOptimizeAutoloader($optimize) ->setClassMapAuthoritative($authoritative) - ->setApcuAutoloader($apcu) + ->setApcuAutoloader($apcu, $apcuPrefix) ->setUpdate(true) - ->setUpdateAllowList($input->getOption('lock') ? array('lock') : $packages) - ->setAllowListTransitiveDependencies($input->getOption('with-dependencies')) - ->setAllowListAllDependencies($input->getOption('with-all-dependencies')) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) + ->setInstall(!$input->getOption('no-install')) + ->setUpdateMirrors($updateMirrors) + ->setUpdateAllowList($packages) + ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) + ->setIgnorePlatformRequirements($ignorePlatformReqs) ->setPreferStable($input->getOption('prefer-stable')) ->setPreferLowest($input->getOption('prefer-lowest')) ; @@ -219,10 +297,27 @@ private function getPackagesInteractively(IOInterface $io, InputInterface $input if ($io->askConfirmation(sprintf( 'Would you like to continue and update the above package%s [yes]? ', 1 === count($packages) ? '' : 's' - ), true)) { + ))) { return $packages; } throw new \RuntimeException('Installation aborted.'); } + + private function appendConstraintToLink(Link $link, $constraint) + { + $parser = new VersionParser; + $oldPrettyString = $link->getConstraint()->getPrettyString(); + $newConstraint = MultiConstraint::create(array($link->getConstraint(), $parser->parseConstraints($constraint))); + $newConstraint->setPrettyString($oldPrettyString.', '.$constraint); + + return new Link( + $link->getSource(), + $link->getTarget(), + $newConstraint, + /** @phpstan-ignore-next-line */ + $link->getDescription(), + $link->getPrettyConstraint() . ', ' . $constraint + ); + } } diff --git a/app/vendor/composer/composer/src/Composer/Command/ValidateCommand.php b/app/vendor/composer/composer/src/Composer/Command/ValidateCommand.php index 6ab95ed1c..fdb424423 100644 --- a/app/vendor/composer/composer/src/Composer/Command/ValidateCommand.php +++ b/app/vendor/composer/composer/src/Composer/Command/ValidateCommand.php @@ -16,7 +16,10 @@ use Composer\Package\Loader\ValidatingArrayLoader; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; +use Composer\Repository\InstalledRepository; +use Composer\Repository\PlatformRepository; use Composer\Util\ConfigValidator; +use Composer\Util\Filesystem; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -42,6 +45,7 @@ protected function configure() new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not validate requires for overly strict/loose constraints'), new InputOption('no-check-lock', null, InputOption::VALUE_NONE, 'Do not check if lock file is up to date'), new InputOption('no-check-publish', null, InputOption::VALUE_NONE, 'Do not check for publish errors'), + new InputOption('no-check-version', null, InputOption::VALUE_NONE, 'Do not report a warning if the version field is present'), new InputOption('with-dependencies', 'A', InputOption::VALUE_NONE, 'Also validate the composer.json of all installed dependencies'), new InputOption('strict', null, InputOption::VALUE_NONE, 'Return a non-zero exit code for warnings as well as errors'), new InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file'), @@ -76,7 +80,7 @@ protected function execute(InputInterface $input, OutputInterface $output) return 3; } - if (!is_readable($file)) { + if (!Filesystem::isReadable($file)) { $io->writeError('' . $file . ' is not readable.'); return 3; @@ -86,14 +90,47 @@ protected function execute(InputInterface $input, OutputInterface $output) $checkAll = $input->getOption('no-check-all') ? 0 : ValidatingArrayLoader::CHECK_ALL; $checkPublish = !$input->getOption('no-check-publish'); $checkLock = !$input->getOption('no-check-lock'); + $checkVersion = $input->getOption('no-check-version') ? 0 : ConfigValidator::CHECK_VERSION; $isStrict = $input->getOption('strict'); - list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll); + list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll, $checkVersion); $lockErrors = array(); $composer = Factory::create($io, $file, $input->hasParameterOption('--no-plugins')); $locker = $composer->getLocker(); if ($locker->isLocked() && !$locker->isFresh()) { - $lockErrors[] = 'The lock file is not up to date with the latest changes in composer.json, it is recommended that you run `composer update` or `composer update `.'; + $lockErrors[] = '- The lock file is not up to date with the latest changes in composer.json, it is recommended that you run `composer update` or `composer update `.'; + } + + if ($locker->isLocked()) { + $missingRequirements = false; + $sets = array( + array('repo' => $locker->getLockedRepository(false), 'method' => 'getRequires', 'description' => 'Required'), + array('repo' => $locker->getLockedRepository(true), 'method' => 'getDevRequires', 'description' => 'Required (in require-dev)'), + ); + foreach ($sets as $set) { + $installedRepo = new InstalledRepository(array($set['repo'])); + + foreach (call_user_func(array($composer->getPackage(), $set['method'])) as $link) { + if (PlatformRepository::isPlatformPackage($link->getTarget())) { + continue; + } + if (!$installedRepo->findPackagesWithReplacersAndProviders($link->getTarget(), $link->getConstraint())) { + if ($results = $installedRepo->findPackagesWithReplacersAndProviders($link->getTarget())) { + $provider = reset($results); + $lockErrors[] = '- ' . $set['description'].' package "' . $link->getTarget() . '" is in the lock file as "'.$provider->getPrettyVersion().'" but that does not satisfy your constraint "'.$link->getPrettyConstraint().'".'; + } else { + $lockErrors[] = '- ' . $set['description'].' package "' . $link->getTarget() . '" is not present in the lock file.'; + } + $missingRequirements = true; + } + } + } + + if ($missingRequirements) { + $lockErrors[] = 'This usually happens when composer files are incorrectly merged or the composer.json file is manually edited.'; + $lockErrors[] = 'Read more about correctly resolving merge conflicts https://getcomposer.org/doc/articles/resolving-merge-conflicts.md'; + $lockErrors[] = 'and prefer using the "require" command over editing the composer.json file directly https://getcomposer.org/doc/03-cli.md#require'; + } } $this->outputResult($io, $file, $errors, $warnings, $checkPublish, $publishErrors, $checkLock, $lockErrors, true); @@ -107,7 +144,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $path = $composer->getInstallationManager()->getInstallPath($package); $file = $path . '/composer.json'; if (is_dir($path) && file_exists($file)) { - list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll); + list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll, $checkVersion); $this->outputResult($io, $package->getPrettyName(), $errors, $warnings, $checkPublish, $publishErrors); @@ -120,9 +157,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'validate', $input, $output); $eventCode = $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); - $exitCode = max($eventCode, $exitCode); - return $exitCode; + return max($eventCode, $exitCode); } private function outputResult($io, $name, &$errors, &$warnings, $checkPublish = false, $publishErrors = array(), $checkLock = false, $lockErrors = array(), $printSchemaUrl = false) @@ -132,36 +168,62 @@ private function outputResult($io, $name, &$errors, &$warnings, $checkPublish = if ($errors) { $io->writeError('' . $name . ' is invalid, the following errors/warnings were found:'); } elseif ($publishErrors) { - $io->writeError('' . $name . ' is valid for simple usage with composer but has'); - $io->writeError('strict errors that make it unable to be published as a package:'); + $io->writeError('' . $name . ' is valid for simple usage with Composer but has'); + $io->writeError('strict errors that make it unable to be published as a package'); $doPrintSchemaUrl = $printSchemaUrl; } elseif ($warnings) { $io->writeError('' . $name . ' is valid, but with a few warnings'); $doPrintSchemaUrl = $printSchemaUrl; + } elseif ($lockErrors) { + $io->write('' . $name . ' is valid but your composer.lock has some '.($checkLock ? 'errors' : 'warnings').''); } else { $io->write('' . $name . ' is valid'); - // if ($lockErrors) then they will be displayed below } if ($doPrintSchemaUrl) { $io->writeError('See https://getcomposer.org/doc/04-schema.md for details on the schema'); } + if ($errors) { + $errors = array_map(function ($err) { + return '- ' . $err; + }, $errors); + array_unshift($errors, '# General errors'); + } + if ($warnings) { + $warnings = array_map(function ($err) { + return '- ' . $err; + }, $warnings); + array_unshift($warnings, '# General warnings'); + } + // Avoid setting the exit code to 1 in case --strict and --no-check-publish/--no-check-lock are combined $extraWarnings = array(); // If checking publish errors, display them as errors, otherwise just show them as warnings - if ($checkPublish) { - $errors = array_merge($errors, $publishErrors); - } else { - $extraWarnings = array_merge($extraWarnings, $publishErrors); + if ($publishErrors) { + $publishErrors = array_map(function ($err) { + return '- ' . $err; + }, $publishErrors); + + if ($checkPublish) { + array_unshift($publishErrors, '# Publish errors'); + $errors = array_merge($errors, $publishErrors); + } else { + array_unshift($publishErrors, '# Publish warnings'); + $extraWarnings = array_merge($extraWarnings, $publishErrors); + } } // If checking lock errors, display them as errors, otherwise just show them as warnings - if ($checkLock) { - $errors = array_merge($errors, $lockErrors); - } else { - $extraWarnings = array_merge($extraWarnings, $lockErrors); + if ($lockErrors) { + if ($checkLock) { + array_unshift($lockErrors, '# Lock file errors'); + $errors = array_merge($errors, $lockErrors); + } else { + array_unshift($lockErrors, '# Lock file warnings'); + $extraWarnings = array_merge($extraWarnings, $lockErrors); + } } $messages = array( @@ -171,7 +233,11 @@ private function outputResult($io, $name, &$errors, &$warnings, $checkPublish = foreach ($messages as $style => $msgs) { foreach ($msgs as $msg) { - $io->writeError('<' . $style . '>' . $msg . ''); + if (strpos($msg, '#') === 0) { + $io->writeError('<' . $style . '>' . $msg . ''); + } else { + $io->writeError($msg); + } } } } diff --git a/app/vendor/composer/composer/src/Composer/Compiler.php b/app/vendor/composer/composer/src/Composer/Compiler.php index 651b2deb9..8476a529f 100644 --- a/app/vendor/composer/composer/src/Composer/Compiler.php +++ b/app/vendor/composer/composer/src/Composer/Compiler.php @@ -13,7 +13,6 @@ namespace Composer; use Composer\Json\JsonFile; -use Composer\Spdx\SpdxLicenses; use Composer\CaBundle\CaBundle; use Symfony\Component\Finder\Finder; use Symfony\Component\Process\Process; @@ -44,13 +43,25 @@ public function compile($pharFile = 'composer.phar') unlink($pharFile); } - $process = new Process('git log --pretty="%H" -n1 HEAD', __DIR__); + // TODO in v2.3 always call with an array + if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) { + $process = new Process(array('git', 'log', '--pretty="%H"', '-n1', 'HEAD'), __DIR__); + } else { + // @phpstan-ignore-next-line + $process = new Process('git log --pretty="%H" -n1 HEAD', __DIR__); + } if ($process->run() != 0) { throw new \RuntimeException('Can\'t run git log. You must ensure to run compile from composer git repository clone and that git binary is available.'); } $this->version = trim($process->getOutput()); - $process = new Process('git log -n1 --pretty=%ci HEAD', __DIR__); + // TODO in v2.3 always call with an array + if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) { + $process = new Process(array('git', 'log', '-n1', '--pretty=%ci', 'HEAD'), __DIR__); + } else { + // @phpstan-ignore-next-line + $process = new Process('git log -n1 --pretty=%ci HEAD', __DIR__); + } if ($process->run() != 0) { throw new \RuntimeException('Can\'t run git log. You must ensure to run compile from composer git repository clone and that git binary is available.'); } @@ -58,7 +69,13 @@ public function compile($pharFile = 'composer.phar') $this->versionDate = new \DateTime(trim($process->getOutput())); $this->versionDate->setTimezone(new \DateTimeZone('UTC')); - $process = new Process('git describe --tags --exact-match HEAD'); + // TODO in v2.3 always call with an array + if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) { + $process = new Process(array('git', 'describe', '--tags', '--exact-match', 'HEAD'), __DIR__); + } else { + // @phpstan-ignore-next-line + $process = new Process('git describe --tags --exact-match HEAD'); + } if ($process->run() == 0) { $this->version = trim($process->getOutput()); } else { @@ -72,7 +89,7 @@ public function compile($pharFile = 'composer.phar') } $phar = new \Phar($pharFile, 0, 'composer.phar'); - $phar->setSignatureAlgorithm(\Phar::SHA1); + $phar->setSignatureAlgorithm(\Phar::SHA512); $phar->startBuffering(); @@ -80,72 +97,83 @@ public function compile($pharFile = 'composer.phar') return strcmp(strtr($a->getRealPath(), '\\', '/'), strtr($b->getRealPath(), '\\', '/')); }; + // Add Composer sources $finder = new Finder(); $finder->files() ->ignoreVCS(true) ->name('*.php') ->notName('Compiler.php') ->notName('ClassLoader.php') + ->notName('InstalledVersions.php') ->in(__DIR__.'/..') ->sort($finderSort) ; - foreach ($finder as $file) { $this->addFile($phar, $file); } + // Add runtime utilities separately to make sure they retains the docblocks as these will get copied into projects $this->addFile($phar, new \SplFileInfo(__DIR__ . '/Autoload/ClassLoader.php'), false); + $this->addFile($phar, new \SplFileInfo(__DIR__ . '/InstalledVersions.php'), false); + // Add Composer resources $finder = new Finder(); $finder->files() - ->name('*.json') ->in(__DIR__.'/../../res') - ->in(SpdxLicenses::getResourcesDir()) ->sort($finderSort) ; - foreach ($finder as $file) { $this->addFile($phar, $file, false); } - $this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/symfony/console/Resources/bin/hiddeninput.exe'), false); - $this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/symfony/polyfill-mbstring/Resources/mb_convert_variables.php8'), false); + // Add vendor files $finder = new Finder(); $finder->files() ->ignoreVCS(true) - ->name('*.php') - ->name('LICENSE') + ->notPath('/\/(composer\.(json|lock)|[A-Z]+\.md|\.gitignore|appveyor.yml|phpunit\.xml\.dist|phpstan\.neon\.dist|phpstan-config\.neon)$/') + ->notPath('/bin\/(jsonlint|validate-json|simple-phpunit)(\.bat)?$/') + ->notPath('symfony/debug/Resources/ext/') + ->notPath('justinrainbow/json-schema/demo/') + ->notPath('justinrainbow/json-schema/dist/') + ->notPath('composer/installed.json') + ->notPath('composer/LICENSE') ->exclude('Tests') ->exclude('tests') ->exclude('docs') - ->in(__DIR__.'/../../vendor/symfony/') - ->in(__DIR__.'/../../vendor/seld/jsonlint/') - ->in(__DIR__.'/../../vendor/justinrainbow/json-schema/') - ->in(__DIR__.'/../../vendor/composer/spdx-licenses/') - ->in(__DIR__.'/../../vendor/composer/semver/') - ->in(__DIR__.'/../../vendor/composer/ca-bundle/') - ->in(__DIR__.'/../../vendor/composer/xdebug-handler/') - ->in(__DIR__.'/../../vendor/psr/') + ->in(__DIR__.'/../../vendor/') ->sort($finderSort) ; + $extraFiles = array( + realpath(__DIR__ . '/../../vendor/composer/spdx-licenses/res/spdx-exceptions.json'), + realpath(__DIR__ . '/../../vendor/composer/spdx-licenses/res/spdx-licenses.json'), + realpath(CaBundle::getBundledCaBundlePath()), + realpath(__DIR__ . '/../../vendor/symfony/console/Resources/bin/hiddeninput.exe'), + realpath(__DIR__ . '/../../vendor/symfony/polyfill-mbstring/Resources/mb_convert_variables.php8'), + ); + $unexpectedFiles = array(); + foreach ($finder as $file) { - $this->addFile($phar, $file); - } + if (in_array(realpath($file), $extraFiles, true)) { + unset($extraFiles[array_search(realpath($file), $extraFiles, true)]); + } elseif (!preg_match('{([/\\\\]LICENSE|\.php)$}', $file)) { + $unexpectedFiles[] = (string) $file; + } - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/autoload.php')); - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_namespaces.php')); - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_psr4.php')); - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_classmap.php')); - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_files.php')); - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_real.php')); - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_static.php')); - if (file_exists(__DIR__.'/../../vendor/composer/include_paths.php')) { - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/include_paths.php')); + if (preg_match('{\.php[\d.]*$}', $file)) { + $this->addFile($phar, $file); + } else { + $this->addFile($phar, $file, false); + } } - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/ClassLoader.php')); - $this->addFile($phar, new \SplFileInfo(CaBundle::getBundledCaBundlePath()), false); + if ($extraFiles) { + throw new \RuntimeException('These files were expected but not added to the phar, they might be excluded or gone from the source package:'.PHP_EOL.implode(PHP_EOL, $extraFiles)); + } + if ($unexpectedFiles) { + throw new \RuntimeException('These files were unexpectedly added to the phar, make sure they are excluded or listed in $extraFiles:'.PHP_EOL.implode(PHP_EOL, $unexpectedFiles)); + } + // Add bin/composer $this->addComposerBin($phar); // Stubs @@ -163,7 +191,7 @@ public function compile($pharFile = 'composer.phar') // re-sign the phar with reproducible timestamp / signature $util = new Timestamps($pharFile); $util->updateTimestamps($this->versionDate); - $util->save($pharFile, \Phar::SHA1); + $util->save($pharFile, \Phar::SHA512); Linter::lint($pharFile); } @@ -194,9 +222,14 @@ private function addFile($phar, $file, $strip = true) } if ($path === 'src/Composer/Composer.php') { - $content = str_replace('@package_version@', $this->version, $content); - $content = str_replace('@package_branch_alias_version@', $this->branchAliasVersion, $content); - $content = str_replace('@release_date@', $this->versionDate->format('Y-m-d H:i:s'), $content); + $content = strtr( + $content, + array( + '@package_version@' => $this->version, + '@package_branch_alias_version@' => $this->branchAliasVersion, + '@release_date@' => $this->versionDate->format('Y-m-d H:i:s'), + ) + ); $content = preg_replace('{SOURCE_VERSION = \'[^\']+\';}', 'SOURCE_VERSION = \'\';', $content); } @@ -269,6 +302,11 @@ private function getStub() } } +if (!class_exists('Phar')) { + echo 'PHP\'s phar extension is missing. Composer requires it to run. Enable the extension or recompile php without --disable-phar then try again.' . PHP_EOL; + exit(1); +} + Phar::mapPhar('composer.phar'); EOF; diff --git a/app/vendor/composer/composer/src/Composer/Composer.php b/app/vendor/composer/composer/src/Composer/Composer.php index 4544d60ba..8d06dd7a0 100644 --- a/app/vendor/composer/composer/src/Composer/Composer.php +++ b/app/vendor/composer/composer/src/Composer/Composer.php @@ -14,6 +14,7 @@ use Composer\Package\RootPackageInterface; use Composer\Package\Locker; +use Composer\Util\Loop; use Composer\Repository\RepositoryManager; use Composer\Installer\InstallationManager; use Composer\Plugin\PluginManager; @@ -50,9 +51,9 @@ class Composer * const RELEASE_DATE = '@release_date@'; * const SOURCE_VERSION = '1.8-dev+source'; */ - const VERSION = '1.10.22'; + const VERSION = '2.1.9'; const BRANCH_ALIAS_VERSION = ''; - const RELEASE_DATE = '2021-04-27 13:10:45'; + const RELEASE_DATE = '2021-10-05 09:47:38'; const SOURCE_VERSION = ''; /** @@ -64,7 +65,7 @@ class Composer * * @var string */ - const RUNTIME_API_VERSION = '1.0.0'; + const RUNTIME_API_VERSION = '2.1.0'; public static function getVersion() { @@ -82,14 +83,19 @@ public static function getVersion() } /** - * @var Package\RootPackageInterface + * @var RootPackageInterface */ private $package; /** - * @var Locker + * @var ?Locker */ - private $locker; + private $locker = null; + + /** + * @var Loop + */ + private $loop; /** * @var Repository\RepositoryManager @@ -132,7 +138,7 @@ public static function getVersion() private $archiveManager; /** - * @param Package\RootPackageInterface $package + * @param RootPackageInterface $package * @return void */ public function setPackage(RootPackageInterface $package) @@ -141,7 +147,7 @@ public function setPackage(RootPackageInterface $package) } /** - * @return Package\RootPackageInterface + * @return RootPackageInterface */ public function getPackage() { @@ -165,7 +171,7 @@ public function getConfig() } /** - * @param Package\Locker $locker + * @param Locker $locker */ public function setLocker(Locker $locker) { @@ -173,7 +179,7 @@ public function setLocker(Locker $locker) } /** - * @return Package\Locker + * @return ?Locker */ public function getLocker() { @@ -181,7 +187,23 @@ public function getLocker() } /** - * @param Repository\RepositoryManager $manager + * @param Loop $loop + */ + public function setLoop(Loop $loop) + { + $this->loop = $loop; + } + + /** + * @return Loop + */ + public function getLoop() + { + return $this->loop; + } + + /** + * @param RepositoryManager $manager */ public function setRepositoryManager(RepositoryManager $manager) { @@ -189,7 +211,7 @@ public function setRepositoryManager(RepositoryManager $manager) } /** - * @return Repository\RepositoryManager + * @return RepositoryManager */ public function getRepositoryManager() { @@ -197,7 +219,7 @@ public function getRepositoryManager() } /** - * @param Downloader\DownloadManager $manager + * @param DownloadManager $manager */ public function setDownloadManager(DownloadManager $manager) { @@ -205,7 +227,7 @@ public function setDownloadManager(DownloadManager $manager) } /** - * @return Downloader\DownloadManager + * @return DownloadManager */ public function getDownloadManager() { @@ -229,7 +251,7 @@ public function getArchiveManager() } /** - * @param Installer\InstallationManager $manager + * @param InstallationManager $manager */ public function setInstallationManager(InstallationManager $manager) { @@ -237,7 +259,7 @@ public function setInstallationManager(InstallationManager $manager) } /** - * @return Installer\InstallationManager + * @return InstallationManager */ public function getInstallationManager() { @@ -245,7 +267,7 @@ public function getInstallationManager() } /** - * @param Plugin\PluginManager $manager + * @param PluginManager $manager */ public function setPluginManager(PluginManager $manager) { @@ -253,7 +275,7 @@ public function setPluginManager(PluginManager $manager) } /** - * @return Plugin\PluginManager + * @return PluginManager */ public function getPluginManager() { @@ -277,7 +299,7 @@ public function getEventDispatcher() } /** - * @param Autoload\AutoloadGenerator $autoloadGenerator + * @param AutoloadGenerator $autoloadGenerator */ public function setAutoloadGenerator(AutoloadGenerator $autoloadGenerator) { @@ -285,7 +307,7 @@ public function setAutoloadGenerator(AutoloadGenerator $autoloadGenerator) } /** - * @return Autoload\AutoloadGenerator + * @return AutoloadGenerator */ public function getAutoloadGenerator() { diff --git a/app/vendor/composer/composer/src/Composer/Config.php b/app/vendor/composer/composer/src/Composer/Config.php index b305fe371..4cb7ab8ae 100644 --- a/app/vendor/composer/composer/src/Composer/Config.php +++ b/app/vendor/composer/composer/src/Composer/Config.php @@ -28,9 +28,10 @@ class Config public static $defaultConfig = array( 'process-timeout' => 300, 'use-include-path' => false, - 'preferred-install' => 'auto', + 'preferred-install' => 'dist', 'notify-on-install' => true, 'github-protocols' => array('https', 'ssh', 'git'), + 'gitlab-protocol' => null, 'vendor-dir' => 'vendor', 'bin-dir' => '{$vendor-dir}/bin', 'cache-dir' => '{$home}/cache', @@ -41,6 +42,7 @@ class Config 'cache-ttl' => 15552000, // 6 months 'cache-files-ttl' => null, // fallback to cache-ttl 'cache-files-maxsize' => '300MiB', + 'cache-read-only' => false, 'bin-compat' => 'auto', 'discard-changes' => false, 'autoloader-suffix' => null, @@ -53,6 +55,7 @@ class Config 'bitbucket-expose-hostname' => true, 'disable-tls' => false, 'secure-http' => true, + 'secure-svn-domains' => array(), 'cafile' => null, 'capath' => null, 'github-expose-hostname' => true, @@ -64,6 +67,7 @@ class Config 'htaccess-protect' => true, 'use-github-api' => true, 'lock' => true, + 'platform-check' => 'php-only', // valid keys without defaults (auth config stuff): // bitbucket-oauth // github-oauth @@ -76,8 +80,7 @@ class Config public static $defaultRepositories = array( 'packagist.org' => array( 'type' => 'composer', - 'url' => 'https?://repo.packagist.org', - 'allow_ssl_downgrade' => true, + 'url' => 'https://repo.packagist.org', ), ); @@ -136,6 +139,8 @@ public function merge($config) foreach ($config['config'] as $key => $val) { if (in_array($key, array('bitbucket-oauth', 'github-oauth', 'gitlab-oauth', 'gitlab-token', 'http-basic', 'bearer')) && isset($this->config[$key])) { $this->config[$key] = array_merge($this->config[$key], $val); + } elseif (in_array($key, array('gitlab-domains', 'github-domains')) && isset($this->config[$key])) { + $this->config[$key] = array_unique(array_merge($this->config[$key], $val)); } elseif ('preferred-install' === $key && isset($this->config[$key])) { if (is_array($val) || is_array($this->config[$key])) { if (is_string($val)) { @@ -176,6 +181,11 @@ public function merge($config) continue; } + // auto-deactivate the default packagist.org repo if it gets redefined + if (isset($repository['type'], $repository['url']) && $repository['type'] === 'composer' && preg_match('{^https?://(?:[a-z0-9-.]+\.)?packagist.org(/|$)}', $repository['url'])) { + $this->disableRepoByName('packagist.org'); + } + // store repo if (is_int($name)) { $this->repositories[] = $repository; @@ -210,6 +220,7 @@ public function getRepositories() public function get($key, $flags = 0) { switch ($key) { + // strings/paths with env var and {$refs} support case 'vendor-dir': case 'bin-dir': case 'process-timeout': @@ -233,20 +244,40 @@ public function get($key, $flags = 0) return (($flags & self::RELATIVE_PATHS) == self::RELATIVE_PATHS) ? $val : $this->realpath($val); + // booleans with env var support + case 'cache-read-only': case 'htaccess-protect': - $value = $this->getComposerEnv('COMPOSER_HTACCESS_PROTECT'); - if (false === $value) { - $value = $this->config[$key]; + // convert foo-bar to COMPOSER_FOO_BAR and check if it exists since it overrides the local config + $env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_')); + + $val = $this->getComposerEnv($env); + if (false === $val) { + $val = $this->config[$key]; + } + + return $val !== 'false' && (bool) $val; + + // booleans without env var support + case 'disable-tls': + case 'secure-http': + case 'use-github-api': + case 'lock': + // special case for secure-http + if ($key === 'secure-http' && $this->get('disable-tls') === true) { + return false; } - return $value !== 'false' && (bool) $value; + return $this->config[$key] !== 'false' && (bool) $this->config[$key]; + + // ints without env var support case 'cache-ttl': return (int) $this->config[$key]; + // numbers with kb/mb/gb support, without env var support case 'cache-files-maxsize': if (!preg_match('/^\s*([0-9.]+)\s*(?:([kmg])(?:i?b)?)?\s*$/i', $this->config[$key], $matches)) { throw new \RuntimeException( - "Could not parse the value of 'cache-files-maxsize': {$this->config[$key]}" + "Could not parse the value of '$key': {$this->config[$key]}" ); } $size = $matches[1]; @@ -268,6 +299,7 @@ public function get($key, $flags = 0) return $size; + // special cases below case 'cache-files-ttl': if (isset($this->config[$key])) { return (int) $this->config[$key]; @@ -283,9 +315,9 @@ public function get($key, $flags = 0) case 'bin-compat': $value = $this->getComposerEnv('COMPOSER_BIN_COMPAT') ?: $this->config[$key]; - if (!in_array($value, array('auto', 'full'))) { + if (!in_array($value, array('auto', 'full', 'symlink'))) { throw new \RuntimeException( - "Invalid value for 'bin-compat': {$value}. Expected auto, full" + "Invalid value for 'bin-compat': {$value}. Expected auto, full or symlink" ); } @@ -325,18 +357,6 @@ public function get($key, $flags = 0) return $protos; - case 'disable-tls': - return $this->config[$key] !== 'false' && (bool) $this->config[$key]; - case 'secure-http': - if ($this->get('disable-tls') === true) { - return false; - } - - return $this->config[$key] !== 'false' && (bool) $this->config[$key]; - case 'use-github-api': - return $this->config[$key] !== 'false' && (bool) $this->config[$key]; - case 'lock': - return $this->config[$key] !== 'false' && (bool) $this->config[$key]; default: if (!isset($this->config[$key])) { return null; @@ -456,10 +476,20 @@ public function prohibitUrlByConfig($url, IOInterface $io = null) // Extract scheme and throw exception on known insecure protocols $scheme = parse_url($url, PHP_URL_SCHEME); + $hostname = parse_url($url, PHP_URL_HOST); if (in_array($scheme, array('http', 'git', 'ftp', 'svn'))) { if ($this->get('secure-http')) { + if ($scheme === 'svn') { + if (in_array($hostname, $this->get('secure-svn-domains'), true)) { + return; + } + + throw new TransportException("Your configuration does not allow connections to $url. See https://getcomposer.org/doc/06-config.md#secure-svn-domains for details."); + } + throw new TransportException("Your configuration does not allow connections to $url. See https://getcomposer.org/doc/06-config.md#secure-http for details."); - } elseif ($io) { + } + if ($io) { $host = parse_url($url, PHP_URL_HOST); if (!isset($this->warnedHosts[$host])) { $io->writeError("Warning: Accessing $host over $scheme which is an insecure protocol."); diff --git a/app/vendor/composer/composer/src/Composer/Config/ConfigSourceInterface.php b/app/vendor/composer/composer/src/Composer/Config/ConfigSourceInterface.php index a00e989bb..e1ef38de0 100644 --- a/app/vendor/composer/composer/src/Composer/Config/ConfigSourceInterface.php +++ b/app/vendor/composer/composer/src/Composer/Config/ConfigSourceInterface.php @@ -23,10 +23,10 @@ interface ConfigSourceInterface /** * Add a repository * - * @param string $name Name - * @param array $config Configuration + * @param string $name Name + * @param array|false $config Configuration */ - public function addRepository($name, $config); + public function addRepository($name, $config, $append = true); /** * Remove a repository @@ -38,7 +38,7 @@ public function removeRepository($name); /** * Add a config setting * - * @param string $name Name + * @param string $name Name * @param string|array $value Value */ public function addConfigSetting($name, $value); diff --git a/app/vendor/composer/composer/src/Composer/Config/JsonConfigSource.php b/app/vendor/composer/composer/src/Composer/Config/JsonConfigSource.php index 6084d66e8..4be155782 100644 --- a/app/vendor/composer/composer/src/Composer/Config/JsonConfigSource.php +++ b/app/vendor/composer/composer/src/Composer/Config/JsonConfigSource.php @@ -14,6 +14,8 @@ use Composer\Json\JsonFile; use Composer\Json\JsonManipulator; +use Composer\Json\JsonValidationException; +use Composer\Util\Filesystem; use Composer\Util\Silencer; /** @@ -57,9 +59,9 @@ public function getName() /** * {@inheritdoc} */ - public function addRepository($name, $config) + public function addRepository($name, $config, $append = true) { - $this->manipulateJson('addRepository', $name, $config, function (&$config, $repo, $repoConfig) { + $this->manipulateJson('addRepository', $name, $config, $append, function (&$config, $repo, $repoConfig) use ($append) { // if converting from an array format to hashmap format, and there is a {"packagist.org":false} repo, we have // to convert it to "packagist.org": false key on the hashmap otherwise it fails schema validation if (isset($config['repositories'])) { @@ -75,7 +77,11 @@ public function addRepository($name, $config) } } - $config['repositories'][$repo] = $repoConfig; + if ($append) { + $config['repositories'][$repo] = $repoConfig; + } else { + $config['repositories'] = array($repo => $repoConfig) + $config['repositories']; + } }); } @@ -135,7 +141,7 @@ public function removeConfigSetting($name) public function addProperty($name, $value) { $this->manipulateJson('addProperty', $name, $value, function (&$config, $key, $val) { - if (substr($key, 0, 6) === 'extra.' || substr($key, 0, 8) === 'scripts.') { + if (strpos($key, 'extra.') === 0 || strpos($key, 'scripts.') === 0) { $bits = explode('.', $key); $last = array_pop($bits); $arr = &$config[reset($bits)]; @@ -157,9 +163,8 @@ public function addProperty($name, $value) */ public function removeProperty($name) { - $authConfig = $this->authConfig; $this->manipulateJson('removeProperty', $name, function (&$config, $key) { - if (substr($key, 0, 6) === 'extra.' || substr($key, 0, 8) === 'scripts.') { + if (strpos($key, 'extra.') === 0 || strpos($key, 'scripts.') === 0) { $bits = explode('.', $key); $last = array_pop($bits); $arr = &$config[reset($bits)]; @@ -193,7 +198,8 @@ public function removeLink($type, $name) { $this->manipulateJson('removeSubNode', $type, $name, function (&$config, $type, $name) { unset($config[$type][$name]); - + }); + $this->manipulateJson('removeMainKeyIfEmpty', $type, function (&$config, $type) { if (0 === count($config[$type])) { unset($config[$type]); } @@ -212,7 +218,7 @@ protected function manipulateJson($method, $args, $fallback) throw new \RuntimeException(sprintf('The file "%s" is not writable.', $this->file->getPath())); } - if (!is_readable($this->file->getPath())) { + if (!Filesystem::isReadable($this->file->getPath())) { throw new \RuntimeException(sprintf('The file "%s" is not readable.', $this->file->getPath())); } @@ -247,14 +253,35 @@ protected function manipulateJson($method, $args, $fallback) $this->arrayUnshiftRef($args, $config); call_user_func_array($fallback, $args); // avoid ending up with arrays for keys that should be objects - foreach (array('require', 'require-dev', 'conflict', 'provide', 'replace', 'suggest', 'config', 'autoload', 'autoload-dev') as $linkType) { - if (isset($config[$linkType]) && $config[$linkType] === array()) { - $config[$linkType] = new \stdClass; + foreach (array('require', 'require-dev', 'conflict', 'provide', 'replace', 'suggest', 'config', 'autoload', 'autoload-dev', 'scripts', 'scripts-descriptions', 'support') as $prop) { + if (isset($config[$prop]) && $config[$prop] === array()) { + $config[$prop] = new \stdClass; + } + } + foreach (array('psr-0', 'psr-4') as $prop) { + if (isset($config['autoload'][$prop]) && $config['autoload'][$prop] === array()) { + $config['autoload'][$prop] = new \stdClass; + } + if (isset($config['autoload-dev'][$prop]) && $config['autoload-dev'][$prop] === array()) { + $config['autoload-dev'][$prop] = new \stdClass; + } + } + foreach (array('platform', 'http-basic', 'bearer', 'gitlab-token', 'gitlab-oauth', 'github-oauth', 'preferred-install') as $prop) { + if (isset($config['config'][$prop]) && $config['config'][$prop] === array()) { + $config['config'][$prop] = new \stdClass; } } $this->file->write($config); } + try { + $this->file->validateSchema(JsonFile::LAX_SCHEMA); + } catch (JsonValidationException $e) { + // restore contents to the original state + file_put_contents($this->file->getPath(), $contents); + throw new \RuntimeException('Failed to update composer.json with a valid format, reverting to the original content. Please report an issue to us with details (command you run and a copy of your composer.json).', 0, $e); + } + if ($newFile) { Silencer::call('chmod', $this->file->getPath(), 0600); } @@ -265,7 +292,7 @@ protected function manipulateJson($method, $args, $fallback) * * @param array $array * @param mixed $value - * @return array + * @return int */ private function arrayUnshiftRef(&$array, &$value) { diff --git a/app/vendor/composer/composer/src/Composer/Console/Application.php b/app/vendor/composer/composer/src/Composer/Console/Application.php index bc1cf0993..bdc9b2a32 100644 --- a/app/vendor/composer/composer/src/Composer/Console/Application.php +++ b/app/vendor/composer/composer/src/Composer/Console/Application.php @@ -13,6 +13,7 @@ namespace Composer\Console; use Composer\IO\NullIO; +use Composer\Util\Filesystem; use Composer\Util\Platform; use Composer\Util\Silencer; use Symfony\Component\Console\Application as BaseApplication; @@ -22,6 +23,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Seld\JsonLint\ParsingException; use Composer\Command; use Composer\Composer; use Composer\Factory; @@ -29,8 +31,11 @@ use Composer\IO\ConsoleIO; use Composer\Json\JsonValidationException; use Composer\Util\ErrorHandler; +use Composer\Util\HttpDownloader; use Composer\EventDispatcher\ScriptExecutionException; use Composer\Exception\NoSslException; +use Composer\XdebugHandler\XdebugHandler; +use Symfony\Component\Process\Exception\ProcessTimedOutException; /** * The console application that handles the commands @@ -42,7 +47,7 @@ class Application extends BaseApplication { /** - * @var Composer + * @var ?Composer */ protected $composer; @@ -51,6 +56,7 @@ class Application extends BaseApplication */ protected $io; + /** @var string */ private static $logo = ' ______ / ____/___ ____ ___ ____ ____ ________ _____ / / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/ @@ -59,21 +65,23 @@ class Application extends BaseApplication /_/ '; + /** @var bool */ private $hasPluginCommands = false; + /** @var bool */ private $disablePluginsByDefault = false; /** * @var string Store the initial working directory at startup time */ - private $initialWorkingDirectory = ''; + private $initialWorkingDirectory; public function __construct() { static $shutdownRegistered = false; if (function_exists('ini_set') && extension_loaded('xdebug')) { - ini_set('xdebug.show_exception_trace', false); - ini_set('xdebug.scream', false); + ini_set('xdebug.show_exception_trace', '0'); + ini_set('xdebug.scream', '0'); } if (function_exists('date_default_timezone_set') && function_exists('date_default_timezone_get')) { @@ -81,6 +89,13 @@ public function __construct() } if (!$shutdownRegistered) { + if (function_exists('pcntl_async_signals') && function_exists('pcntl_signal')) { + pcntl_async_signals(true); + pcntl_signal(SIGINT, function ($sig) { + exit(130); + }); + } + $shutdownRegistered = true; register_shutdown_function(function () { @@ -120,19 +135,20 @@ public function doRun(InputInterface $input, OutputInterface $output) { $this->disablePluginsByDefault = $input->hasParameterOption('--no-plugins'); - if (getenv('COMPOSER_NO_INTERACTION')) { + if (getenv('COMPOSER_NO_INTERACTION') || !Platform::isTty(defined('STDIN') ? STDIN : fopen('php://stdin', 'r'))) { $input->setInteractive(false); } $io = $this->io = new ConsoleIO($input, $output, new HelperSet(array( new QuestionHelper(), ))); + + // Register error handler again to pass it the IO instance ErrorHandler::register($io); if ($input->hasParameterOption('--no-cache')) { $io->writeError('Disabling cache usage', true, IOInterface::DEBUG); - $_SERVER['COMPOSER_CACHE_DIR'] = Platform::isWindows() ? 'nul' : '/dev/null'; - putenv('COMPOSER_CACHE_DIR='.$_SERVER['COMPOSER_CACHE_DIR']); + Platform::putEnv('COMPOSER_CACHE_DIR', Platform::isWindows() ? 'nul' : '/dev/null'); } // switch working dir @@ -149,8 +165,8 @@ public function doRun(InputInterface $input, OutputInterface $output) try { $commandName = $this->find($name)->getName(); } catch (CommandNotFoundException $e) { - // we'll check command validity again later after plugins are loaded - $commandName = false; + // we'll check command validity again later after plugins are loaded + $commandName = false; } catch (\InvalidArgumentException $e) { } } @@ -163,7 +179,7 @@ public function doRun(InputInterface $input, OutputInterface $output) // abort when we reach the home dir or top of the filesystem while (dirname($dir) !== $dir && $dir !== $home) { if (file_exists($dir.'/'.Factory::getComposerFile())) { - if ($io->askConfirmation('No composer.json in current directory, do you want to use the one at '.$dir.'? [Y,n]? ', true)) { + if ($io->askConfirmation('No composer.json in current directory, do you want to use the one at '.$dir.'? [Y,n]? ')) { $oldWorkingDir = getcwd(); chdir($dir); } @@ -173,7 +189,17 @@ public function doRun(InputInterface $input, OutputInterface $output) } } - if (!$this->disablePluginsByDefault && !$this->hasPluginCommands && 'global' !== $commandName) { + // avoid loading plugins/initializing the Composer instance earlier than necessary if no plugin command is needed + // if showing the version, we never need plugin commands + $mayNeedPluginCommand = false === $input->hasParameterOption(array('--version', '-V')) + && ( + // not a composer command, so try loading plugin ones + false === $commandName + // list command requires plugin commands to show them + || in_array($commandName, array('', 'list'), true) + ); + + if ($mayNeedPluginCommand && !$this->disablePluginsByDefault && !$this->hasPluginCommands && 'global' !== $commandName) { try { foreach ($this->getPluginCommands() as $command) { if ($this->has($command->getName())) { @@ -184,6 +210,20 @@ public function doRun(InputInterface $input, OutputInterface $output) } } catch (NoSslException $e) { // suppress these as they are not relevant at this point + } catch (ParsingException $e) { + $details = $e->getDetails(); + + $file = realpath(Factory::getComposerFile()); + + $line = null; + if ($details && isset($details['line'])) { + $line = $details['line']; + } + + $ghe = new GithubActionError($this->io); + $ghe->emit($e->getMessage(), $file, $line); + + throw $e; } $this->hasPluginCommands = true; @@ -213,12 +253,12 @@ function_exists('php_uname') ? php_uname('s') . ' / ' . php_uname('r') : 'Unknow $io->writeError('Composer only officially supports PHP 5.3.2 and above, you will most likely encounter problems with your PHP '.PHP_VERSION.', upgrading is strongly recommended.'); } - if (extension_loaded('xdebug') && !getenv('COMPOSER_DISABLE_XDEBUG_WARN')) { - $io->writeError('You are running composer with Xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug'); + if (XdebugHandler::isXdebugActive() && !getenv('COMPOSER_DISABLE_XDEBUG_WARN')) { + $io->writeError('Composer is operating slower than normal because you have Xdebug enabled. See https://getcomposer.org/xdebug'); } if (defined('COMPOSER_DEV_WARNING_TIME') && $commandName !== 'self-update' && $commandName !== 'selfupdate' && time() > COMPOSER_DEV_WARNING_TIME) { - $io->writeError(sprintf('Warning: This development build of composer is over 60 days old. It is recommended to update it by running "%s self-update" to get the latest version.', $_SERVER['PHP_SELF'])); + $io->writeError(sprintf('Warning: This development build of Composer is over 60 days old. It is recommended to update it by running "%s self-update" to get the latest version.', $_SERVER['PHP_SELF'])); } if ( @@ -230,6 +270,12 @@ function_exists('php_uname') ? php_uname('s') . ' / ' . php_uname('r') : 'Unknow if (function_exists('posix_getuid') && posix_getuid() === 0) { if ($commandName !== 'self-update' && $commandName !== 'selfupdate') { $io->writeError('Do not run Composer as root/super user! See https://getcomposer.org/root for details'); + + if ($io->isInteractive()) { + if (!$io->askConfirmation('Continue as root/super user [yes]? ')) { + return 1; + } + } } if ($uid = (int) getenv('SUDO_UID')) { // Silently clobber any sudo credentials on the invoking user to avoid privilege escalations later on @@ -251,7 +297,7 @@ function_exists('php_uname') ? php_uname('s') . ' / ' . php_uname('r') : 'Unknow // add non-standard scripts as own commands $file = Factory::getComposerFile(); - if (is_file($file) && is_readable($file) && is_array($composer = json_decode(file_get_contents($file), true))) { + if (is_file($file) && Filesystem::isReadable($file) && is_array($composer = json_decode(file_get_contents($file), true))) { if (isset($composer['scripts']) && is_array($composer['scripts'])) { foreach ($composer['scripts'] as $script => $dummy) { if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) { @@ -280,8 +326,9 @@ function_exists('php_uname') ? php_uname('s') . ' / ' . php_uname('r') : 'Unknow $result = parent::doRun($input, $output); + // chdir back to $oldWorkingDir if set if (isset($oldWorkingDir)) { - chdir($oldWorkingDir); + Silencer::call('chdir', $oldWorkingDir); } if (isset($startTime)) { @@ -292,10 +339,15 @@ function_exists('php_uname') ? php_uname('s') . ' / ' . php_uname('r') : 'Unknow return $result; } catch (ScriptExecutionException $e) { - return $e->getCode(); + return (int) $e->getCode(); } catch (\Exception $e) { + $ghe = new GithubActionError($this->io); + $ghe->emit($e->getMessage()); + $this->hintCommonErrors($e); + restore_error_handler(); + throw $e; } } @@ -349,13 +401,25 @@ private function hintCommonErrors($exception) $io->writeError('The following exception is caused by a lack of memory or swap, or not having swap configured', true, IOInterface::QUIET); $io->writeError('Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details', true, IOInterface::QUIET); } + + if ($exception instanceof ProcessTimedOutException) { + $io->writeError('The following exception is caused by a process timeout', true, IOInterface::QUIET); + $io->writeError('Check https://getcomposer.org/doc/06-config.md#process-timeout for details', true, IOInterface::QUIET); + } + + if ($hints = HttpDownloader::getExceptionHints($exception)) { + foreach ($hints as $hint) { + $io->writeError($hint, true, IOInterface::QUIET); + } + } } /** * @param bool $required * @param bool|null $disablePlugins * @throws JsonValidationException - * @return \Composer\Composer + * @throws \InvalidArgumentException + * @return ?\Composer\Composer If $required is true then the return value is guaranteed */ public function getComposer($required = true, $disablePlugins = null) { @@ -369,12 +433,16 @@ public function getComposer($required = true, $disablePlugins = null) } catch (\InvalidArgumentException $e) { if ($required) { $this->io->writeError($e->getMessage()); - exit(1); + // TODO composer 2.3 simplify to $this->areExceptionsCaught() + if (!method_exists($this, 'areExceptionsCaught') || $this->areExceptionsCaught()) { + exit(1); + } + throw $e; } } catch (JsonValidationException $e) { - $errors = ' - ' . implode(PHP_EOL . ' - ', $e->getErrors()); - $message = $e->getMessage() . ':' . PHP_EOL . $errors; - throw new JsonValidationException($message); + if ($required) { + throw $e; + } } } @@ -387,7 +455,7 @@ public function getComposer($required = true, $disablePlugins = null) public function resetComposer() { $this->composer = null; - if ($this->getIO() && method_exists($this->getIO(), 'resetAuthentications')) { + if (method_exists($this->getIO(), 'resetAuthentications')) { $this->getIO()->resetAuthentications(); } } @@ -438,9 +506,10 @@ protected function getDefaultCommands() new Command\OutdatedCommand(), new Command\CheckPlatformReqsCommand(), new Command\FundCommand(), + new Command\ReinstallCommand(), )); - if ('phar:' === substr(__FILE__, 0, 5)) { + if (strpos(__FILE__, 'phar:') === 0) { $commands[] = new Command\SelfUpdateCommand(); } @@ -485,7 +554,7 @@ private function getPluginCommands() $composer = $this->getComposer(false, false); if (null === $composer) { - $composer = Factory::createGlobal($this->io, false); + $composer = Factory::createGlobal($this->io); } if (null !== $composer) { @@ -508,7 +577,7 @@ private function getPluginCommands() } /** - * Get the working directoy at startup time + * Get the working directory at startup time * * @return string */ diff --git a/app/vendor/composer/composer/src/Composer/Console/GithubActionError.php b/app/vendor/composer/composer/src/Composer/Console/GithubActionError.php new file mode 100644 index 000000000..c862dd9be --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Console/GithubActionError.php @@ -0,0 +1,50 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Console; + +use Composer\IO\IOInterface; + +final class GithubActionError +{ + /** + * @var IOInterface + */ + protected $io; + + public function __construct(IOInterface $io) + { + $this->io = $io; + } + + /** + * @param string $message + * @param null|string $file + * @param null|int $line + */ + public function emit($message, $file = null, $line = null) + { + if (getenv('GITHUB_ACTIONS') && !getenv('COMPOSER_TESTS_ARE_RUNNING')) { + // newlines need to be encoded + // see https://github.com/actions/starter-workflows/issues/68#issuecomment-581479448 + $message = str_replace("\n", '%0A', $message); + + if ($file && $line) { + $this->io->write("::error file=". $file .",line=". $line ."::". $message); + } elseif ($file) { + $this->io->write("::error file=". $file ."::". $message); + } else { + $this->io->write("::error ::". $message); + } + } + } +} diff --git a/app/vendor/composer/composer/src/Composer/Console/HtmlOutputFormatter.php b/app/vendor/composer/composer/src/Composer/Console/HtmlOutputFormatter.php index 994fcbcd5..acef97325 100644 --- a/app/vendor/composer/composer/src/Composer/Console/HtmlOutputFormatter.php +++ b/app/vendor/composer/composer/src/Composer/Console/HtmlOutputFormatter.php @@ -19,6 +19,7 @@ */ class HtmlOutputFormatter extends OutputFormatter { + /** @var array */ private static $availableForegroundColors = array( 30 => 'black', 31 => 'red', @@ -29,6 +30,7 @@ class HtmlOutputFormatter extends OutputFormatter 36 => 'cyan', 37 => 'white', ); + /** @var array */ private static $availableBackgroundColors = array( 40 => 'black', 41 => 'red', @@ -39,6 +41,7 @@ class HtmlOutputFormatter extends OutputFormatter 46 => 'cyan', 47 => 'white', ); + /** @var array */ private static $availableOptions = array( 1 => 'bold', 4 => 'underscore', diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Decisions.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Decisions.php index e2773501f..799a0a1fa 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Decisions.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Decisions.php @@ -16,17 +16,21 @@ * Stores decisions on installing, removing or keeping packages * * @author Nils Adermann + * @implements \Iterator */ class Decisions implements \Iterator, \Countable { const DECISION_LITERAL = 0; const DECISION_REASON = 1; + /** @var Pool */ protected $pool; + /** @var array */ protected $decisionMap; + /** @var array */ protected $decisionQueue = array(); - public function __construct($pool) + public function __construct(Pool $pool) { $this->pool = $pool; $this->decisionMap = array(); @@ -108,17 +112,17 @@ public function atOffset($queueOffset) public function validOffset($queueOffset) { - return $queueOffset >= 0 && $queueOffset < count($this->decisionQueue); + return $queueOffset >= 0 && $queueOffset < \count($this->decisionQueue); } public function lastReason() { - return $this->decisionQueue[count($this->decisionQueue) - 1][self::DECISION_REASON]; + return $this->decisionQueue[\count($this->decisionQueue) - 1][self::DECISION_REASON]; } public function lastLiteral() { - return $this->decisionQueue[count($this->decisionQueue) - 1][self::DECISION_LITERAL]; + return $this->decisionQueue[\count($this->decisionQueue) - 1][self::DECISION_LITERAL]; } public function reset() @@ -130,7 +134,7 @@ public function reset() public function resetToOffset($offset) { - while (count($this->decisionQueue) > $offset + 1) { + while (\count($this->decisionQueue) > $offset + 1) { $decision = array_pop($this->decisionQueue); $this->decisionMap[abs($decision[self::DECISION_LITERAL])] = 0; } @@ -142,31 +146,37 @@ public function revertLast() array_pop($this->decisionQueue); } + #[\ReturnTypeWillChange] public function count() { - return count($this->decisionQueue); + return \count($this->decisionQueue); } + #[\ReturnTypeWillChange] public function rewind() { end($this->decisionQueue); } + #[\ReturnTypeWillChange] public function current() { return current($this->decisionQueue); } + #[\ReturnTypeWillChange] public function key() { return key($this->decisionQueue); } + #[\ReturnTypeWillChange] public function next() { - return prev($this->decisionQueue); + prev($this->decisionQueue); } + #[\ReturnTypeWillChange] public function valid() { return false !== current($this->decisionQueue); @@ -174,7 +184,7 @@ public function valid() public function isEmpty() { - return count($this->decisionQueue) === 0; + return \count($this->decisionQueue) === 0; } protected function addDecision($literal, $level) @@ -197,15 +207,21 @@ protected function addDecision($literal, $level) } } - public function __toString() + public function toString(Pool $pool = null) { $decisionMap = $this->decisionMap; ksort($decisionMap); $str = '['; foreach ($decisionMap as $packageId => $level) { - $str .= $packageId.':'.$level.','; + $str .= (($pool) ? $pool->literalToPackage($packageId) : $packageId).':'.$level.','; } $str .= ']'; + return $str; } + + public function __toString() + { + return $this->toString(); + } } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/DefaultPolicy.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/DefaultPolicy.php index d7d386801..832a009e5 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/DefaultPolicy.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/DefaultPolicy.php @@ -12,9 +12,9 @@ namespace Composer\DependencyResolver; -use Composer\Package\PackageInterface; use Composer\Package\AliasPackage; use Composer\Package\BasePackage; +use Composer\Package\PackageInterface; use Composer\Semver\Constraint\Constraint; /** @@ -23,7 +23,9 @@ */ class DefaultPolicy implements PolicyInterface { + /** @var bool */ private $preferStable; + /** @var bool */ private $preferLowest; public function __construct($preferStable = false, $preferLowest = false) @@ -44,54 +46,33 @@ public function versionCompare(PackageInterface $a, PackageInterface $b, $operat return $constraint->matchSpecific($version, true); } - public function findUpdatePackages(Pool $pool, array $installedMap, PackageInterface $package, $mustMatchName = false) - { - $packages = array(); - - foreach ($pool->whatProvides($package->getName(), null, $mustMatchName) as $candidate) { - if ($candidate !== $package) { - $packages[] = $candidate; - } - } - - return $packages; - } - - public function getPriority(Pool $pool, PackageInterface $package) - { - return $pool->getPriority($package->getRepository()); - } - - public function selectPreferredPackages(Pool $pool, array $installedMap, array $literals, $requiredPackage = null) + public function selectPreferredPackages(Pool $pool, array $literals, $requiredPackage = null) { - $packages = $this->groupLiteralsByNamePreferInstalled($pool, $installedMap, $literals); + $packages = $this->groupLiteralsByName($pool, $literals); + $policy = $this; - foreach ($packages as &$literals) { - $policy = $this; - usort($literals, function ($a, $b) use ($policy, $pool, $installedMap, $requiredPackage) { - return $policy->compareByPriorityPreferInstalled($pool, $installedMap, $pool->literalToPackage($a), $pool->literalToPackage($b), $requiredPackage, true); + foreach ($packages as &$nameLiterals) { + usort($nameLiterals, function ($a, $b) use ($policy, $pool, $requiredPackage) { + return $policy->compareByPriority($pool, $pool->literalToPackage($a), $pool->literalToPackage($b), $requiredPackage, true); }); } - foreach ($packages as &$literals) { - $literals = $this->pruneToHighestPriorityOrInstalled($pool, $installedMap, $literals); - - $literals = $this->pruneToBestVersion($pool, $literals); - - $literals = $this->pruneRemoteAliases($pool, $literals); + foreach ($packages as &$sortedLiterals) { + $sortedLiterals = $this->pruneToBestVersion($pool, $sortedLiterals); + $sortedLiterals = $this->pruneRemoteAliases($pool, $sortedLiterals); } - $selected = call_user_func_array('array_merge', array_values($packages)); + $selected = \call_user_func_array('array_merge', array_values($packages)); // now sort the result across all packages to respect replaces across packages - usort($selected, function ($a, $b) use ($policy, $pool, $installedMap, $requiredPackage) { - return $policy->compareByPriorityPreferInstalled($pool, $installedMap, $pool->literalToPackage($a), $pool->literalToPackage($b), $requiredPackage); + usort($selected, function ($a, $b) use ($policy, $pool, $requiredPackage) { + return $policy->compareByPriority($pool, $pool->literalToPackage($a), $pool->literalToPackage($b), $requiredPackage); }); return $selected; } - protected function groupLiteralsByNamePreferInstalled(Pool $pool, array $installedMap, $literals) + protected function groupLiteralsByName(Pool $pool, $literals) { $packages = array(); foreach ($literals as $literal) { @@ -100,12 +81,7 @@ protected function groupLiteralsByNamePreferInstalled(Pool $pool, array $install if (!isset($packages[$packageName])) { $packages[$packageName] = array(); } - - if (isset($installedMap[abs($literal)])) { - array_unshift($packages[$packageName], $literal); - } else { - $packages[$packageName][] = $literal; - } + $packages[$packageName][] = $literal; } return $packages; @@ -114,61 +90,49 @@ protected function groupLiteralsByNamePreferInstalled(Pool $pool, array $install /** * @protected */ - public function compareByPriorityPreferInstalled(Pool $pool, array $installedMap, PackageInterface $a, PackageInterface $b, $requiredPackage = null, $ignoreReplace = false) + public function compareByPriority(Pool $pool, BasePackage $a, BasePackage $b, $requiredPackage = null, $ignoreReplace = false) { - if ($a->getRepository() === $b->getRepository()) { - // prefer aliases to the original package - if ($a->getName() === $b->getName()) { - $aAliased = $a instanceof AliasPackage; - $bAliased = $b instanceof AliasPackage; - if ($aAliased && !$bAliased) { - return -1; // use a - } - if (!$aAliased && $bAliased) { - return 1; // use b - } + // prefer aliases to the original package + if ($a->getName() === $b->getName()) { + $aAliased = $a instanceof AliasPackage; + $bAliased = $b instanceof AliasPackage; + if ($aAliased && !$bAliased) { + return -1; // use a + } + if (!$aAliased && $bAliased) { + return 1; // use b } + } - if (!$ignoreReplace) { - // return original, not replaced - if ($this->replaces($a, $b)) { - return 1; // use b - } - if ($this->replaces($b, $a)) { - return -1; // use a - } + if (!$ignoreReplace) { + // return original, not replaced + if ($this->replaces($a, $b)) { + return 1; // use b + } + if ($this->replaces($b, $a)) { + return -1; // use a + } - // for replacers not replacing each other, put a higher prio on replacing - // packages with the same vendor as the required package - if ($requiredPackage && false !== ($pos = strpos($requiredPackage, '/'))) { - $requiredVendor = substr($requiredPackage, 0, $pos); + // for replacers not replacing each other, put a higher prio on replacing + // packages with the same vendor as the required package + if ($requiredPackage && false !== ($pos = strpos($requiredPackage, '/'))) { + $requiredVendor = substr($requiredPackage, 0, $pos); - $aIsSameVendor = substr($a->getName(), 0, $pos) === $requiredVendor; - $bIsSameVendor = substr($b->getName(), 0, $pos) === $requiredVendor; + $aIsSameVendor = strpos($a->getName(), $requiredVendor) === 0; + $bIsSameVendor = strpos($b->getName(), $requiredVendor) === 0; - if ($bIsSameVendor !== $aIsSameVendor) { - return $aIsSameVendor ? -1 : 1; - } + if ($bIsSameVendor !== $aIsSameVendor) { + return $aIsSameVendor ? -1 : 1; } } - - // priority equal, sort by package id to make reproducible - if ($a->id === $b->id) { - return 0; - } - - return ($a->id < $b->id) ? -1 : 1; - } - - if (isset($installedMap[$a->id])) { - return -1; } - if (isset($installedMap[$b->id])) { - return 1; + // priority equal, sort by package id to make reproducible + if ($a->id === $b->id) { + return 0; } - return ($this->getPriority($pool, $a) > $this->getPriority($pool, $b)) ? -1 : 1; + return ($a->id < $b->id) ? -1 : 1; } /** @@ -177,11 +141,11 @@ public function compareByPriorityPreferInstalled(Pool $pool, array $installedMap * Replace constraints are ignored. This method should only be used for * prioritisation, not for actual constraint verification. * - * @param PackageInterface $source - * @param PackageInterface $target + * @param BasePackage $source + * @param BasePackage $target * @return bool */ - protected function replaces(PackageInterface $source, PackageInterface $target) + protected function replaces(BasePackage $source, BasePackage $target) { foreach ($source->getReplaces() as $link) { if ($link->getTarget() === $target->getName() @@ -218,37 +182,6 @@ protected function pruneToBestVersion(Pool $pool, $literals) return $bestLiterals; } - /** - * Assumes that installed packages come first and then all highest priority packages - */ - protected function pruneToHighestPriorityOrInstalled(Pool $pool, array $installedMap, array $literals) - { - $selected = array(); - - $priority = null; - - foreach ($literals as $literal) { - $package = $pool->literalToPackage($literal); - - if (isset($installedMap[$package->id])) { - $selected[] = $literal; - continue; - } - - if (null === $priority) { - $priority = $this->getPriority($pool, $package); - } - - if ($this->getPriority($pool, $package) != $priority) { - break; - } - - $selected[] = $literal; - } - - return $selected; - } - /** * Assumes that locally aliased (in root package requires) packages take priority over branch-alias ones * diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/GenericRule.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/GenericRule.php index df8a2a003..61d95275f 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/GenericRule.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/GenericRule.php @@ -12,25 +12,24 @@ namespace Composer\DependencyResolver; -use Composer\Package\PackageInterface; +use Composer\Package\BasePackage; use Composer\Package\Link; +use Composer\Semver\Constraint\ConstraintInterface; /** * @author Nils Adermann */ class GenericRule extends Rule { + /** @var int[] */ protected $literals; /** - * @param array $literals - * @param int $reason A RULE_* constant describing the reason for generating this rule - * @param Link|PackageInterface $reasonData - * @param array $job The job this rule was created from + * @param int[] $literals */ - public function __construct(array $literals, $reason, $reasonData, $job = null) + public function __construct(array $literals, $reason, $reasonData) { - parent::__construct($reason, $reasonData, $job); + parent::__construct($reason, $reasonData); // sort all packages ascending by id sort($literals); @@ -65,7 +64,7 @@ public function equals(Rule $rule) public function isAssertion() { - return 1 === count($this->literals); + return 1 === \count($this->literals); } /** diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/LocalRepoTransaction.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/LocalRepoTransaction.php new file mode 100644 index 000000000..daefc12b9 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/LocalRepoTransaction.php @@ -0,0 +1,30 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\DependencyResolver; + +use Composer\Repository\RepositoryInterface; + +/** + * @author Nils Adermann + * @internal + */ +class LocalRepoTransaction extends Transaction +{ + public function __construct(RepositoryInterface $lockedRepository, $localRepository) + { + parent::__construct( + $localRepository->getPackages(), + $lockedRepository->getPackages() + ); + } +} diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/LockTransaction.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/LockTransaction.php new file mode 100644 index 000000000..3a3d94505 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/LockTransaction.php @@ -0,0 +1,148 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\DependencyResolver; + +use Composer\Package\AliasPackage; +use Composer\Package\BasePackage; +use Composer\Package\Package; + +/** + * @author Nils Adermann + * @internal + */ +class LockTransaction extends Transaction +{ + /** + * packages in current lock file, platform repo or otherwise present + * + * Indexed by spl_object_hash + * + * @var array + */ + protected $presentMap; + + /** + * Packages which cannot be mapped, platform repo, root package, other fixed repos + * + * Indexed by package id + * + * @var array + */ + protected $unlockableMap; + + /** + * @var array{dev: BasePackage[], non-dev: BasePackage[], all: BasePackage[]} + */ + protected $resultPackages; + + /** + * @param array $presentMap + * @param array $unlockableMap + */ + public function __construct(Pool $pool, array $presentMap, array $unlockableMap, Decisions $decisions) + { + $this->presentMap = $presentMap; + $this->unlockableMap = $unlockableMap; + + $this->setResultPackages($pool, $decisions); + parent::__construct($this->presentMap, $this->resultPackages['all']); + } + + // TODO make this a bit prettier instead of the two text indexes? + public function setResultPackages(Pool $pool, Decisions $decisions) + { + $this->resultPackages = array('all' => array(), 'non-dev' => array(), 'dev' => array()); + foreach ($decisions as $i => $decision) { + $literal = $decision[Decisions::DECISION_LITERAL]; + + if ($literal > 0) { + $package = $pool->literalToPackage($literal); + + $this->resultPackages['all'][] = $package; + if (!isset($this->unlockableMap[$package->id])) { + $this->resultPackages['non-dev'][] = $package; + } + } + } + } + + public function setNonDevPackages(LockTransaction $extractionResult) + { + $packages = $extractionResult->getNewLockPackages(false); + + $this->resultPackages['dev'] = $this->resultPackages['non-dev']; + $this->resultPackages['non-dev'] = array(); + + foreach ($packages as $package) { + foreach ($this->resultPackages['dev'] as $i => $resultPackage) { + // TODO this comparison is probably insufficient, aliases, what about modified versions? I guess they aren't possible? + if ($package->getName() == $resultPackage->getName()) { + $this->resultPackages['non-dev'][] = $resultPackage; + unset($this->resultPackages['dev'][$i]); + } + } + } + } + + // TODO additionalFixedRepository needs to be looked at here as well? + public function getNewLockPackages($devMode, $updateMirrors = false) + { + $packages = array(); + foreach ($this->resultPackages[$devMode ? 'dev' : 'non-dev'] as $package) { + if (!$package instanceof AliasPackage) { + // if we're just updating mirrors we need to reset references to the same as currently "present" packages' references to keep the lock file as-is + // we do not reset references if the currently present package didn't have any, or if the type of VCS has changed + if ($updateMirrors && !isset($this->presentMap[spl_object_hash($package)])) { + foreach ($this->presentMap as $presentPackage) { + if ($package->getName() == $presentPackage->getName() && $package->getVersion() == $presentPackage->getVersion()) { + if ($presentPackage->getSourceReference() && $presentPackage->getSourceType() === $package->getSourceType()) { + $package->setSourceDistReferences($presentPackage->getSourceReference()); + } + if ($presentPackage->getReleaseDate() && $package instanceof Package) { + $package->setReleaseDate($presentPackage->getReleaseDate()); + } + } + } + } + $packages[] = $package; + } + } + + return $packages; + } + + /** + * Checks which of the given aliases from composer.json are actually in use for the lock file + */ + public function getAliases($aliases) + { + $usedAliases = array(); + + foreach ($this->resultPackages['all'] as $package) { + if ($package instanceof AliasPackage) { + foreach ($aliases as $index => $alias) { + if ($alias['package'] === $package->getName()) { + $usedAliases[] = $alias; + unset($aliases[$index]); + } + } + } + } + + usort($usedAliases, function ($a, $b) { + return strcmp($a['package'], $b['package']); + }); + + return $usedAliases; + } +} diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/MultiConflictRule.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/MultiConflictRule.php new file mode 100644 index 000000000..16a7bd9e2 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/MultiConflictRule.php @@ -0,0 +1,105 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\DependencyResolver; + +use Composer\Package\BasePackage; +use Composer\Package\Link; + +/** + * @author Nils Adermann + * + * MultiConflictRule([A, B, C]) acts as Rule([-A, -B]), Rule([-A, -C]), Rule([-B, -C]) + */ +class MultiConflictRule extends Rule +{ + /** @var int[] */ + protected $literals; + + /** + * @param int[] $literals + */ + public function __construct(array $literals, $reason, $reasonData) + { + parent::__construct($reason, $reasonData); + + if (\count($literals) < 3) { + throw new \RuntimeException("multi conflict rule requires at least 3 literals"); + } + + // sort all packages ascending by id + sort($literals); + + $this->literals = $literals; + } + + public function getLiterals() + { + return $this->literals; + } + + public function getHash() + { + $data = unpack('ihash', md5('c:'.implode(',', $this->literals), true)); + + return $data['hash']; + } + + /** + * Checks if this rule is equal to another one + * + * Ignores whether either of the rules is disabled. + * + * @param Rule $rule The rule to check against + * @return bool Whether the rules are equal + */ + public function equals(Rule $rule) + { + if ($rule instanceof MultiConflictRule) { + return $this->literals === $rule->getLiterals(); + } + + return false; + } + + public function isAssertion() + { + return false; + } + + public function disable() + { + throw new \RuntimeException("Disabling multi conflict rules is not possible. Please contact composer at https://github.com/composer/composer to let us debug what lead to this situation."); + } + + /** + * Formats a rule as a string of the format (Literal1|Literal2|...) + * + * @return string + */ + public function __toString() + { + // TODO multi conflict? + $result = $this->isDisabled() ? 'disabled(multi(' : '(multi('; + + foreach ($this->literals as $i => $literal) { + if ($i != 0) { + $result .= '|'; + } + $result .= $literal; + } + + $result .= '))'; + + return $result; + } +} diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/InstallOperation.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/InstallOperation.php index 08c659c49..6eefbd5bb 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/InstallOperation.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/InstallOperation.php @@ -19,20 +19,17 @@ * * @author Konstantin Kudryashov */ -class InstallOperation extends SolverOperation +class InstallOperation extends SolverOperation implements OperationInterface { - protected $package; + const TYPE = 'install'; /** - * Initializes operation. - * - * @param PackageInterface $package package instance - * @param string $reason operation reason + * @var PackageInterface */ - public function __construct(PackageInterface $package, $reason = null) - { - parent::__construct($reason); + protected $package; + public function __construct(PackageInterface $package) + { $this->package = $package; } @@ -47,20 +44,15 @@ public function getPackage() } /** - * Returns job type. - * - * @return string + * {@inheritDoc} */ - public function getJobType() + public function show($lock) { - return 'install'; + return self::format($this->package, $lock); } - /** - * {@inheritDoc} - */ - public function __toString() + public static function format(PackageInterface $package, $lock = false) { - return 'Installing '.$this->package->getPrettyName().' ('.$this->formatVersion($this->package).')'; + return ($lock ? 'Locking ' : 'Installing ').''.$package->getPrettyName().' ('.$package->getFullPrettyVersion().')'; } } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/MarkAliasInstalledOperation.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/MarkAliasInstalledOperation.php index 920e54e67..a1aa1219f 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/MarkAliasInstalledOperation.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/MarkAliasInstalledOperation.php @@ -13,55 +13,41 @@ namespace Composer\DependencyResolver\Operation; use Composer\Package\AliasPackage; -use Composer\Package\PackageInterface; /** * Solver install operation. * * @author Nils Adermann */ -class MarkAliasInstalledOperation extends SolverOperation +class MarkAliasInstalledOperation extends SolverOperation implements OperationInterface { - protected $package; + const TYPE = 'markAliasInstalled'; /** - * Initializes operation. - * - * @param AliasPackage $package package instance - * @param string $reason operation reason + * @var AliasPackage */ - public function __construct(AliasPackage $package, $reason = null) - { - parent::__construct($reason); + protected $package; + public function __construct(AliasPackage $package) + { $this->package = $package; } /** * Returns package instance. * - * @return PackageInterface + * @return AliasPackage */ public function getPackage() { return $this->package; } - /** - * Returns job type. - * - * @return string - */ - public function getJobType() - { - return 'markAliasInstalled'; - } - /** * {@inheritDoc} */ - public function __toString() + public function show($lock) { - return 'Marking '.$this->package->getPrettyName().' ('.$this->formatVersion($this->package).') as installed, alias of '.$this->package->getAliasOf()->getPrettyName().' ('.$this->formatVersion($this->package->getAliasOf()).')'; + return 'Marking '.$this->package->getPrettyName().' ('.$this->package->getFullPrettyVersion().') as installed, alias of '.$this->package->getAliasOf()->getPrettyName().' ('.$this->package->getAliasOf()->getFullPrettyVersion().')'; } } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/MarkAliasUninstalledOperation.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/MarkAliasUninstalledOperation.php index 77f3aef8c..d0c193478 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/MarkAliasUninstalledOperation.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/MarkAliasUninstalledOperation.php @@ -13,55 +13,41 @@ namespace Composer\DependencyResolver\Operation; use Composer\Package\AliasPackage; -use Composer\Package\PackageInterface; /** * Solver install operation. * * @author Nils Adermann */ -class MarkAliasUninstalledOperation extends SolverOperation +class MarkAliasUninstalledOperation extends SolverOperation implements OperationInterface { - protected $package; + const TYPE = 'markAliasUninstalled'; /** - * Initializes operation. - * - * @param AliasPackage $package package instance - * @param string $reason operation reason + * @var AliasPackage */ - public function __construct(AliasPackage $package, $reason = null) - { - parent::__construct($reason); + protected $package; + public function __construct(AliasPackage $package) + { $this->package = $package; } /** * Returns package instance. * - * @return PackageInterface + * @return AliasPackage */ public function getPackage() { return $this->package; } - /** - * Returns job type. - * - * @return string - */ - public function getJobType() - { - return 'markAliasUninstalled'; - } - /** * {@inheritDoc} */ - public function __toString() + public function show($lock) { - return 'Marking '.$this->package->getPrettyName().' ('.$this->formatVersion($this->package).') as uninstalled, alias of '.$this->package->getAliasOf()->getPrettyName().' ('.$this->formatVersion($this->package->getAliasOf()).')'; + return 'Marking '.$this->package->getPrettyName().' ('.$this->package->getFullPrettyVersion().') as uninstalled, alias of '.$this->package->getAliasOf()->getPrettyName().' ('.$this->package->getAliasOf()->getFullPrettyVersion().')'; } } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/OperationInterface.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/OperationInterface.php index 330cbceb1..b0cc29b6d 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/OperationInterface.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/OperationInterface.php @@ -20,18 +20,19 @@ interface OperationInterface { /** - * Returns job type. + * Returns operation type. * * @return string */ - public function getJobType(); + public function getOperationType(); /** - * Returns operation reason. + * Serializes the operation in a human readable format * + * @param bool $lock Whether this is an operation on the lock file * @return string */ - public function getReason(); + public function show($lock); /** * Serializes the operation in a human readable format diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/SolverOperation.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/SolverOperation.php index e1a68585e..7ab87fe8a 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/SolverOperation.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/SolverOperation.php @@ -12,39 +12,30 @@ namespace Composer\DependencyResolver\Operation; -use Composer\Package\PackageInterface; - /** - * Abstract solver operation class. + * Abstract operation class. * - * @author Konstantin Kudryashov + * @author Aleksandr Bezpiatov */ abstract class SolverOperation implements OperationInterface { - protected $reason; + const TYPE = null; /** - * Initializes operation. + * Returns operation type. * - * @param string $reason operation reason + * @return string */ - public function __construct($reason = null) + public function getOperationType() { - $this->reason = $reason; + return static::TYPE; } /** - * Returns operation reason. - * - * @return string + * {@inheritDoc} */ - public function getReason() - { - return $this->reason; - } - - protected function formatVersion(PackageInterface $package) + public function __toString() { - return $package->getFullPrettyVersion(); + return $this->show(false); } } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/UninstallOperation.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/UninstallOperation.php index b4a73811e..585fdb1e5 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/UninstallOperation.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/UninstallOperation.php @@ -19,20 +19,17 @@ * * @author Konstantin Kudryashov */ -class UninstallOperation extends SolverOperation +class UninstallOperation extends SolverOperation implements OperationInterface { - protected $package; + const TYPE = 'uninstall'; /** - * Initializes operation. - * - * @param PackageInterface $package package instance - * @param string $reason operation reason + * @var PackageInterface */ - public function __construct(PackageInterface $package, $reason = null) - { - parent::__construct($reason); + protected $package; + public function __construct(PackageInterface $package) + { $this->package = $package; } @@ -47,20 +44,15 @@ public function getPackage() } /** - * Returns job type. - * - * @return string + * {@inheritDoc} */ - public function getJobType() + public function show($lock) { - return 'uninstall'; + return self::format($this->package, $lock); } - /** - * {@inheritDoc} - */ - public function __toString() + public static function format(PackageInterface $package, $lock = false) { - return 'Uninstalling '.$this->package->getPrettyName().' ('.$this->formatVersion($this->package).')'; + return 'Removing '.$package->getPrettyName().' ('.$package->getFullPrettyVersion().')'; } } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/UpdateOperation.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/UpdateOperation.php index 8cfc6b700..543c8e783 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/UpdateOperation.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Operation/UpdateOperation.php @@ -20,22 +20,26 @@ * * @author Konstantin Kudryashov */ -class UpdateOperation extends SolverOperation +class UpdateOperation extends SolverOperation implements OperationInterface { + const TYPE = 'update'; + + /** + * @var PackageInterface + */ protected $initialPackage; + + /** + * @var PackageInterface + */ protected $targetPackage; /** - * Initializes update operation. - * * @param PackageInterface $initial initial package * @param PackageInterface $target target package (updated) - * @param string $reason update reason */ - public function __construct(PackageInterface $initial, PackageInterface $target, $reason = null) + public function __construct(PackageInterface $initial, PackageInterface $target) { - parent::__construct($reason); - $this->initialPackage = $initial; $this->targetPackage = $target; } @@ -61,23 +65,28 @@ public function getTargetPackage() } /** - * Returns job type. - * - * @return string + * {@inheritDoc} */ - public function getJobType() + public function show($lock) { - return 'update'; + return self::format($this->initialPackage, $this->targetPackage, $lock); } - /** - * {@inheritDoc} - */ - public function __toString() + public static function format(PackageInterface $initialPackage, PackageInterface $targetPackage, $lock = false) { - $actionName = VersionParser::isUpgrade($this->initialPackage->getVersion(), $this->targetPackage->getVersion()) ? 'Updating' : 'Downgrading'; + $fromVersion = $initialPackage->getFullPrettyVersion(); + $toVersion = $targetPackage->getFullPrettyVersion(); + + if ($fromVersion === $toVersion && $initialPackage->getSourceReference() !== $targetPackage->getSourceReference()) { + $fromVersion = $initialPackage->getFullPrettyVersion(true, PackageInterface::DISPLAY_SOURCE_REF); + $toVersion = $targetPackage->getFullPrettyVersion(true, PackageInterface::DISPLAY_SOURCE_REF); + } elseif ($fromVersion === $toVersion && $initialPackage->getDistReference() !== $targetPackage->getDistReference()) { + $fromVersion = $initialPackage->getFullPrettyVersion(true, PackageInterface::DISPLAY_DIST_REF); + $toVersion = $targetPackage->getFullPrettyVersion(true, PackageInterface::DISPLAY_DIST_REF); + } + + $actionName = VersionParser::isUpgrade($initialPackage->getVersion(), $targetPackage->getVersion()) ? 'Upgrading' : 'Downgrading'; - return $actionName.' '.$this->initialPackage->getPrettyName().' ('.$this->formatVersion($this->initialPackage).') to '. - $this->targetPackage->getPrettyName(). ' ('.$this->formatVersion($this->targetPackage).')'; + return $actionName.' '.$initialPackage->getPrettyName().' ('.$fromVersion.' => '.$toVersion.')'; } } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/PolicyInterface.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/PolicyInterface.php index 3464bd594..b10a43e9f 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/PolicyInterface.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/PolicyInterface.php @@ -21,7 +21,5 @@ interface PolicyInterface { public function versionCompare(PackageInterface $a, PackageInterface $b, $operator); - public function findUpdatePackages(Pool $pool, array $installedMap, PackageInterface $package); - - public function selectPreferredPackages(Pool $pool, array $installedMap, array $literals, $requiredPackage = null); + public function selectPreferredPackages(Pool $pool, array $literals, $requiredPackage = null); } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Pool.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Pool.php index 8021275b6..733b0edf5 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Pool.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Pool.php @@ -12,157 +12,66 @@ namespace Composer\DependencyResolver; -use Composer\Package\BasePackage; -use Composer\Package\AliasPackage; use Composer\Package\Version\VersionParser; +use Composer\Semver\CompilingMatcher; use Composer\Semver\Constraint\ConstraintInterface; use Composer\Semver\Constraint\Constraint; -use Composer\Semver\Constraint\EmptyConstraint; -use Composer\Repository\RepositoryInterface; -use Composer\Repository\CompositeRepository; -use Composer\Repository\ComposerRepository; -use Composer\Repository\InstalledRepositoryInterface; -use Composer\Repository\PlatformRepository; -use Composer\Package\PackageInterface; +use Composer\Package\BasePackage; /** - * A package pool contains repositories that provide packages. + * A package pool contains all packages for dependency resolution * * @author Nils Adermann * @author Jordi Boggiano */ class Pool implements \Countable { - const MATCH_NAME = -1; - const MATCH_NONE = 0; - const MATCH = 1; - const MATCH_PROVIDE = 2; - const MATCH_REPLACE = 3; - const MATCH_FILTERED = 4; - - protected $repositories = array(); - protected $providerRepos = array(); + /** @var BasePackage[] */ protected $packages = array(); + /** @var array */ protected $packageByName = array(); - protected $packageByExactName = array(); - protected $acceptableStabilities; - protected $stabilityFlags; + /** @var VersionParser */ protected $versionParser; + /** @var array> */ protected $providerCache = array(); - protected $filterRequires; - protected $whitelist = null; // TODO 2.0 rename to allowList - protected $id = 1; + /** @var BasePackage[] */ + protected $unacceptableFixedOrLockedPackages; - public function __construct($minimumStability = 'stable', array $stabilityFlags = array(), array $filterRequires = array()) + public function __construct(array $packages = array(), array $unacceptableFixedOrLockedPackages = array()) { $this->versionParser = new VersionParser; - $this->acceptableStabilities = array(); - foreach (BasePackage::$stabilities as $stability => $value) { - if ($value <= BasePackage::$stabilities[$minimumStability]) { - $this->acceptableStabilities[$stability] = $value; - } - } - $this->stabilityFlags = $stabilityFlags; - $this->filterRequires = $filterRequires; - foreach ($filterRequires as $name => $constraint) { - if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) { - unset($this->filterRequires[$name]); - } - } + $this->setPackages($packages); + $this->unacceptableFixedOrLockedPackages = $unacceptableFixedOrLockedPackages; } - public function setAllowList($allowList) + private function setPackages(array $packages) { - // call original method for BC - $this->setWhitelist($allowList); - } + $id = 1; - /** - * @deprecated use setAllowList instead - */ - public function setWhitelist($whitelist) - { - $this->whitelist = $whitelist; - $this->providerCache = array(); - } + foreach ($packages as $package) { + $this->packages[] = $package; - /** - * Adds a repository and its packages to this package pool - * - * @param RepositoryInterface $repo A package repository - * @param array $rootAliases - */ - public function addRepository(RepositoryInterface $repo, $rootAliases = array()) - { - if ($repo instanceof CompositeRepository) { - $repos = $repo->getRepositories(); - } else { - $repos = array($repo); - } + $package->id = $id++; - foreach ($repos as $repo) { - $this->repositories[] = $repo; - - $exempt = $repo instanceof PlatformRepository || $repo instanceof InstalledRepositoryInterface; - - if ($repo instanceof ComposerRepository && $repo->hasProviders()) { - $this->providerRepos[] = $repo; - $repo->setRootAliases($rootAliases); - $repo->resetPackageIds(); - } else { - foreach ($repo->getPackages() as $package) { - $names = $package->getNames(); - $stability = $package->getStability(); - if ($exempt || $this->isPackageAcceptable($names, $stability)) { - $package->setId($this->id++); - $this->packages[] = $package; - $this->packageByExactName[$package->getName()][$package->id] = $package; - - foreach ($names as $provided) { - $this->packageByName[$provided][] = $package; - } - - // handle root package aliases - $name = $package->getName(); - if (isset($rootAliases[$name][$package->getVersion()])) { - $alias = $rootAliases[$name][$package->getVersion()]; - if ($package instanceof AliasPackage) { - $package = $package->getAliasOf(); - } - $aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']); - $aliasPackage->setRootPackageAlias(true); - $aliasPackage->setId($this->id++); - - $package->getRepository()->addPackage($aliasPackage); - $this->packages[] = $aliasPackage; - $this->packageByExactName[$aliasPackage->getName()][$aliasPackage->id] = $aliasPackage; - - foreach ($aliasPackage->getNames() as $name) { - $this->packageByName[$name][] = $aliasPackage; - } - } - } - } + foreach ($package->getNames() as $provided) { + $this->packageByName[$provided][] = $package; } } } - public function getPriority(RepositoryInterface $repo) + /** + * @return BasePackage[] + */ + public function getPackages() { - $priority = array_search($repo, $this->repositories, true); - - if (false === $priority) { - throw new \RuntimeException("Could not determine repository priority. The repository was not registered in the pool."); - } - - return -$priority; + return $this->packages; } /** * Retrieves the package object for a given package id. * - * @param int $id - * @return PackageInterface + * @param int $id + * @return BasePackage */ public function packageById($id) { @@ -172,117 +81,48 @@ public function packageById($id) /** * Returns how many packages have been loaded into the pool */ + #[\ReturnTypeWillChange] public function count() { - return count($this->packages); + return \count($this->packages); } /** * Searches all packages providing the given package name and match the constraint * - * @param string $name The package name to be searched for - * @param ConstraintInterface $constraint A constraint that all returned - * packages must match or null to return all - * @param bool $mustMatchName Whether the name of returned packages - * must match the given name - * @param bool $bypassFilters If enabled, filterRequires and stability matching is ignored - * @return PackageInterface[] A set of packages + * @param string $name The package name to be searched for + * @param ConstraintInterface $constraint A constraint that all returned + * packages must match or null to return all + * @return BasePackage[] A set of packages */ - public function whatProvides($name, ConstraintInterface $constraint = null, $mustMatchName = false, $bypassFilters = false) + public function whatProvides($name, ConstraintInterface $constraint = null) { - if ($bypassFilters) { - return $this->computeWhatProvides($name, $constraint, $mustMatchName, true); - } - - $key = ((int) $mustMatchName).$constraint; + $key = (string) $constraint; if (isset($this->providerCache[$name][$key])) { return $this->providerCache[$name][$key]; } - return $this->providerCache[$name][$key] = $this->computeWhatProvides($name, $constraint, $mustMatchName, $bypassFilters); + return $this->providerCache[$name][$key] = $this->computeWhatProvides($name, $constraint); } /** * @see whatProvides */ - private function computeWhatProvides($name, $constraint, $mustMatchName = false, $bypassFilters = false) + private function computeWhatProvides($name, $constraint) { - $candidates = array(); - - foreach ($this->providerRepos as $repo) { - foreach ($repo->whatProvides($this, $name, $bypassFilters) as $candidate) { - $candidates[] = $candidate; - if ($candidate->id < 1) { - $candidate->setId($this->id++); - $this->packages[$this->id - 2] = $candidate; - } - } - } - - if ($mustMatchName) { - $candidates = array_filter($candidates, function ($candidate) use ($name) { - return $candidate->getName() == $name; - }); - if (isset($this->packageByExactName[$name])) { - $candidates = array_merge($candidates, $this->packageByExactName[$name]); - } - } elseif (isset($this->packageByName[$name])) { - $candidates = array_merge($candidates, $this->packageByName[$name]); + if (!isset($this->packageByName[$name])) { + return array(); } - $matches = $provideMatches = array(); - $nameMatch = false; + $matches = array(); - foreach ($candidates as $candidate) { - $aliasOfCandidate = null; - - // alias packages are not white listed, make sure that the package - // being aliased is white listed - if ($candidate instanceof AliasPackage) { - $aliasOfCandidate = $candidate->getAliasOf(); - } - - if ($this->whitelist !== null && !$bypassFilters && ( - (!($candidate instanceof AliasPackage) && !isset($this->whitelist[$candidate->id])) || - ($candidate instanceof AliasPackage && !isset($this->whitelist[$aliasOfCandidate->id])) - )) { - continue; + foreach ($this->packageByName[$name] as $candidate) { + if ($this->match($candidate, $name, $constraint)) { + $matches[] = $candidate; } - switch ($this->match($candidate, $name, $constraint, $bypassFilters)) { - case self::MATCH_NONE: - break; - - case self::MATCH_NAME: - $nameMatch = true; - break; - - case self::MATCH: - $nameMatch = true; - $matches[] = $candidate; - break; - - case self::MATCH_PROVIDE: - $provideMatches[] = $candidate; - break; - - case self::MATCH_REPLACE: - $matches[] = $candidate; - break; - - case self::MATCH_FILTERED: - break; - - default: - throw new \UnexpectedValueException('Unexpected match type'); - } - } - - // if a package with the required name exists, we ignore providers - if ($nameMatch) { - return $matches; } - return array_merge($matches, $provideMatches); + return $matches; } public function literalToPackage($literal) @@ -305,53 +145,22 @@ public function literalToPrettyString($literal, $installedMap) return $prefix.' '.$package->getPrettyString(); } - public function isPackageAcceptable($name, $stability) - { - foreach ((array) $name as $n) { - // allow if package matches the global stability requirement and has no exception - if (!isset($this->stabilityFlags[$n]) && isset($this->acceptableStabilities[$stability])) { - return true; - } - - // allow if package matches the package-specific stability flag - if (isset($this->stabilityFlags[$n]) && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$n]) { - return true; - } - } - - return false; - } - /** * Checks if the package matches the given constraint directly or through * provided or replaced packages * - * @param PackageInterface $candidate - * @param string $name Name of the package to be matched - * @param ConstraintInterface $constraint The constraint to verify - * @return int One of the MATCH* constants of this class or 0 if there is no match + * @param BasePackage $candidate + * @param string $name Name of the package to be matched + * @param ConstraintInterface $constraint The constraint to verify + * @return bool */ - public function match($candidate, $name, ConstraintInterface $constraint = null, $bypassFilters) + public function match(BasePackage $candidate, $name, ConstraintInterface $constraint = null) { $candidateName = $candidate->getName(); $candidateVersion = $candidate->getVersion(); - $isDev = $candidate->getStability() === 'dev'; - $isAlias = $candidate instanceof AliasPackage; - - if (!$bypassFilters && !$isDev && !$isAlias && isset($this->filterRequires[$name])) { - $requireFilter = $this->filterRequires[$name]; - } else { - $requireFilter = new EmptyConstraint; - } if ($candidateName === $name) { - $pkgConstraint = new Constraint('==', $candidateVersion); - - if ($constraint === null || $constraint->matches($pkgConstraint)) { - return $requireFilter->matches($pkgConstraint) ? self::MATCH : self::MATCH_FILTERED; - } - - return self::MATCH_NAME; + return $constraint === null || CompilingMatcher::match($constraint, Constraint::OP_EQ, $candidateVersion); } $provides = $candidate->getProvides(); @@ -361,27 +170,43 @@ public function match($candidate, $name, ConstraintInterface $constraint = null, if (isset($replaces[0]) || isset($provides[0])) { foreach ($provides as $link) { if ($link->getTarget() === $name && ($constraint === null || $constraint->matches($link->getConstraint()))) { - return $requireFilter->matches($link->getConstraint()) ? self::MATCH_PROVIDE : self::MATCH_FILTERED; + return true; } } foreach ($replaces as $link) { if ($link->getTarget() === $name && ($constraint === null || $constraint->matches($link->getConstraint()))) { - return $requireFilter->matches($link->getConstraint()) ? self::MATCH_REPLACE : self::MATCH_FILTERED; + return true; } } - return self::MATCH_NONE; + return false; } if (isset($provides[$name]) && ($constraint === null || $constraint->matches($provides[$name]->getConstraint()))) { - return $requireFilter->matches($provides[$name]->getConstraint()) ? self::MATCH_PROVIDE : self::MATCH_FILTERED; + return true; } if (isset($replaces[$name]) && ($constraint === null || $constraint->matches($replaces[$name]->getConstraint()))) { - return $requireFilter->matches($replaces[$name]->getConstraint()) ? self::MATCH_REPLACE : self::MATCH_FILTERED; + return true; + } + + return false; + } + + public function isUnacceptableFixedOrLockedPackage(BasePackage $package) + { + return \in_array($package, $this->unacceptableFixedOrLockedPackages, true); + } + + public function __toString() + { + $str = "Pool:\n"; + + foreach ($this->packages as $package) { + $str .= '- '.str_pad((string) $package->id, 6, ' ', STR_PAD_LEFT).': '.$package->getName()."\n"; } - return self::MATCH_NONE; + return $str; } } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/PoolBuilder.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/PoolBuilder.php new file mode 100644 index 000000000..97159a3c8 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/PoolBuilder.php @@ -0,0 +1,548 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\DependencyResolver; + +use Composer\EventDispatcher\EventDispatcher; +use Composer\IO\IOInterface; +use Composer\Package\AliasPackage; +use Composer\Package\BasePackage; +use Composer\Package\CompleteAliasPackage; +use Composer\Package\CompletePackage; +use Composer\Package\CompletePackageInterface; +use Composer\Package\PackageInterface; +use Composer\Package\Version\StabilityFilter; +use Composer\Plugin\PluginEvents; +use Composer\Plugin\PrePoolCreateEvent; +use Composer\Repository\PlatformRepository; +use Composer\Repository\RootPackageRepository; +use Composer\Semver\CompilingMatcher; +use Composer\Semver\Constraint\Constraint; +use Composer\Semver\Constraint\ConstraintInterface; +use Composer\Semver\Constraint\MatchAllConstraint; +use Composer\Semver\Constraint\MultiConstraint; +use Composer\Semver\Intervals; + +/** + * @author Nils Adermann + */ +class PoolBuilder +{ + /** + * @var int[] + * @phpstan-var array + */ + private $acceptableStabilities; + /** + * @var int[] + * @phpstan-var array + */ + private $stabilityFlags; + /** + * @var array[] + * @phpstan-var array> + */ + private $rootAliases; + /** + * @var string[] + * @phpstan-var array + */ + private $rootReferences; + /** + * @var ?EventDispatcher + */ + private $eventDispatcher; + /** + * @var IOInterface + */ + private $io; + /** + * @var array[] + * @phpstan-var array + */ + private $aliasMap = array(); + /** + * @var ConstraintInterface[] + * @phpstan-var array + */ + private $packagesToLoad = array(); + /** + * @var ConstraintInterface[] + * @phpstan-var array + */ + private $loadedPackages = array(); + /** + * @var array[] + * @phpstan-var array>> + */ + private $loadedPerRepo = array(); + /** + * @var PackageInterface[] + */ + private $packages = array(); + /** + * @var PackageInterface[] + * @phpstan-var list + */ + private $unacceptableFixedOrLockedPackages = array(); + /** @var string[] */ + private $updateAllowList = array(); + /** @var array */ + private $skippedLoad = array(); + + /** + * Keeps a list of dependencies which are root requirements, and as such + * have already their maximum required range loaded and can not be + * extended by markPackageNameForLoading + * + * Packages get cleared from this list if they get unlocked as in that case + * we need to actually load them + * + * @var array + */ + private $maxExtendedReqs = array(); + /** + * @var array + * @phpstan-var array + */ + private $updateAllowWarned = array(); + + /** @var int */ + private $indexCounter = 0; + + /** + * @param int[] $acceptableStabilities array of stability => BasePackage::STABILITY_* value + * @phpstan-param array $acceptableStabilities + * @param int[] $stabilityFlags an array of package name => BasePackage::STABILITY_* value + * @phpstan-param array $stabilityFlags + * @param array[] $rootAliases + * @phpstan-param array> $rootAliases + * @param string[] $rootReferences an array of package name => source reference + * @phpstan-param array $rootReferences + */ + public function __construct(array $acceptableStabilities, array $stabilityFlags, array $rootAliases, array $rootReferences, IOInterface $io, EventDispatcher $eventDispatcher = null) + { + $this->acceptableStabilities = $acceptableStabilities; + $this->stabilityFlags = $stabilityFlags; + $this->rootAliases = $rootAliases; + $this->rootReferences = $rootReferences; + $this->eventDispatcher = $eventDispatcher; + $this->io = $io; + } + + public function buildPool(array $repositories, Request $request) + { + if ($request->getUpdateAllowList()) { + $this->updateAllowList = $request->getUpdateAllowList(); + $this->warnAboutNonMatchingUpdateAllowList($request); + + foreach ($request->getLockedRepository()->getPackages() as $lockedPackage) { + if (!$this->isUpdateAllowed($lockedPackage)) { + $request->lockPackage($lockedPackage); + $lockedName = $lockedPackage->getName(); + // remember which packages we skipped loading remote content for in this partial update + $this->skippedLoad[$lockedName] = $lockedName; + foreach ($lockedPackage->getReplaces() as $link) { + $this->skippedLoad[$link->getTarget()] = $lockedName; + } + } + } + } + + foreach ($request->getFixedOrLockedPackages() as $package) { + // using MatchAllConstraint here because fixed packages do not need to retrigger + // loading any packages + $this->loadedPackages[$package->getName()] = new MatchAllConstraint(); + + // replace means conflict, so if a fixed package replaces a name, no need to load that one, packages would conflict anyways + foreach ($package->getReplaces() as $link) { + $this->loadedPackages[$link->getTarget()] = new MatchAllConstraint(); + } + + // TODO in how far can we do the above for conflicts? It's more tricky cause conflicts can be limited to + // specific versions while replace is a conflict with all versions of the name + + if ( + $package->getRepository() instanceof RootPackageRepository + || $package->getRepository() instanceof PlatformRepository + || StabilityFilter::isPackageAcceptable($this->acceptableStabilities, $this->stabilityFlags, $package->getNames(), $package->getStability()) + ) { + $this->loadPackage($request, $package, false); + } else { + $this->unacceptableFixedOrLockedPackages[] = $package; + } + } + + foreach ($request->getRequires() as $packageName => $constraint) { + // fixed and locked packages have already been added, so if a root require needs one of them, no need to do anything + if (isset($this->loadedPackages[$packageName])) { + continue; + } + + $this->packagesToLoad[$packageName] = $constraint; + $this->maxExtendedReqs[$packageName] = true; + } + + // clean up packagesToLoad for anything we manually marked loaded above + foreach ($this->packagesToLoad as $name => $constraint) { + if (isset($this->loadedPackages[$name])) { + unset($this->packagesToLoad[$name]); + } + } + + while (!empty($this->packagesToLoad)) { + $this->loadPackagesMarkedForLoading($request, $repositories); + } + + foreach ($this->packages as $i => $package) { + // we check all alias related packages at once, so no need to check individual aliases + // isset also checks non-null value + if (!$package instanceof AliasPackage) { + $constraint = new Constraint('==', $package->getVersion()); + $aliasedPackages = array($i => $package); + if (isset($this->aliasMap[spl_object_hash($package)])) { + $aliasedPackages += $this->aliasMap[spl_object_hash($package)]; + } + + $found = false; + foreach ($aliasedPackages as $packageOrAlias) { + if (CompilingMatcher::match($constraint, Constraint::OP_EQ, $packageOrAlias->getVersion())) { + $found = true; + } + } + if (!$found) { + foreach ($aliasedPackages as $index => $packageOrAlias) { + unset($this->packages[$index]); + } + } + } + } + + if ($this->eventDispatcher) { + $prePoolCreateEvent = new PrePoolCreateEvent( + PluginEvents::PRE_POOL_CREATE, + $repositories, + $request, + $this->acceptableStabilities, + $this->stabilityFlags, + $this->rootAliases, + $this->rootReferences, + $this->packages, + $this->unacceptableFixedOrLockedPackages + ); + $this->eventDispatcher->dispatch($prePoolCreateEvent->getName(), $prePoolCreateEvent); + $this->packages = $prePoolCreateEvent->getPackages(); + $this->unacceptableFixedOrLockedPackages = $prePoolCreateEvent->getUnacceptableFixedPackages(); + } + + $pool = new Pool($this->packages, $this->unacceptableFixedOrLockedPackages); + + $this->aliasMap = array(); + $this->packagesToLoad = array(); + $this->loadedPackages = array(); + $this->loadedPerRepo = array(); + $this->packages = array(); + $this->unacceptableFixedOrLockedPackages = array(); + $this->maxExtendedReqs = array(); + $this->skippedLoad = array(); + $this->indexCounter = 0; + + Intervals::clear(); + + return $pool; + } + + private function markPackageNameForLoading(Request $request, $name, ConstraintInterface $constraint) + { + // Skip platform requires at this stage + if (PlatformRepository::isPlatformPackage($name)) { + return; + } + + // Root require (which was not unlocked) already loaded the maximum range so no + // need to check anything here + if (isset($this->maxExtendedReqs[$name])) { + return; + } + + // Root requires can not be overruled by dependencies so there is no point in + // extending the loaded constraint for those. + // This is triggered when loading a root require which was locked but got unlocked, then + // we make sure that we load at most the intervals covered by the root constraint. + $rootRequires = $request->getRequires(); + if (isset($rootRequires[$name]) && !Intervals::isSubsetOf($constraint, $rootRequires[$name])) { + $constraint = $rootRequires[$name]; + } + + // Not yet loaded or already marked for a reload, set the constraint to be loaded + if (!isset($this->loadedPackages[$name])) { + // Maybe it was already marked before but not loaded yet. In that case + // we have to extend the constraint (we don't check if they are identical because + // MultiConstraint::create() will optimize anyway) + if (isset($this->packagesToLoad[$name])) { + // Already marked for loading and this does not expand the constraint to be loaded, nothing to do + if (Intervals::isSubsetOf($constraint, $this->packagesToLoad[$name])) { + return; + } + + // extend the constraint to be loaded + $constraint = Intervals::compactConstraint(MultiConstraint::create(array($this->packagesToLoad[$name], $constraint), false)); + } + + $this->packagesToLoad[$name] = $constraint; + + return; + } + + // No need to load this package with this constraint because it is + // a subset of the constraint with which we have already loaded packages + if (Intervals::isSubsetOf($constraint, $this->loadedPackages[$name])) { + return; + } + + // We have already loaded that package but not in the constraint that's + // required. We extend the constraint and mark that package as not being loaded + // yet so we get the required package versions + $this->packagesToLoad[$name] = Intervals::compactConstraint(MultiConstraint::create(array($this->loadedPackages[$name], $constraint), false)); + unset($this->loadedPackages[$name]); + } + + private function loadPackagesMarkedForLoading(Request $request, $repositories) + { + foreach ($this->packagesToLoad as $name => $constraint) { + $this->loadedPackages[$name] = $constraint; + } + + $packageBatch = $this->packagesToLoad; + $this->packagesToLoad = array(); + + foreach ($repositories as $repoIndex => $repository) { + if (empty($packageBatch)) { + break; + } + + // these repos have their packages fixed or locked if they need to be loaded so we + // never need to load anything else from them + if ($repository instanceof PlatformRepository || $repository === $request->getLockedRepository()) { + continue; + } + $result = $repository->loadPackages($packageBatch, $this->acceptableStabilities, $this->stabilityFlags, isset($this->loadedPerRepo[$repoIndex]) ? $this->loadedPerRepo[$repoIndex] : array()); + + foreach ($result['namesFound'] as $name) { + // avoid loading the same package again from other repositories once it has been found + unset($packageBatch[$name]); + } + foreach ($result['packages'] as $package) { + $this->loadedPerRepo[$repoIndex][$package->getName()][$package->getVersion()] = $package; + $this->loadPackage($request, $package); + } + } + } + + private function loadPackage(Request $request, BasePackage $package, $propagateUpdate = true) + { + $index = $this->indexCounter++; + $this->packages[$index] = $package; + + if ($package instanceof AliasPackage) { + $this->aliasMap[spl_object_hash($package->getAliasOf())][$index] = $package; + } + + $name = $package->getName(); + + // we're simply setting the root references on all versions for a name here and rely on the solver to pick the + // right version. It'd be more work to figure out which versions and which aliases of those versions this may + // apply to + if (isset($this->rootReferences[$name])) { + // do not modify the references on already locked or fixed packages + if (!$request->isLockedPackage($package) && !$request->isFixedPackage($package)) { + $package->setSourceDistReferences($this->rootReferences[$name]); + } + } + + // if propogateUpdate is false we are loading a fixed or locked package, root aliases do not apply as they are + // manually loaded as separate packages in this case + if ($propagateUpdate && isset($this->rootAliases[$name][$package->getVersion()])) { + $alias = $this->rootAliases[$name][$package->getVersion()]; + if ($package instanceof AliasPackage) { + $basePackage = $package->getAliasOf(); + } else { + $basePackage = $package; + } + if ($basePackage instanceof CompletePackage) { + $aliasPackage = new CompleteAliasPackage($basePackage, $alias['alias_normalized'], $alias['alias']); + } else { + $aliasPackage = new AliasPackage($basePackage, $alias['alias_normalized'], $alias['alias']); + } + $aliasPackage->setRootPackageAlias(true); + + $newIndex = $this->indexCounter++; + $this->packages[$newIndex] = $aliasPackage; + $this->aliasMap[spl_object_hash($aliasPackage->getAliasOf())][$newIndex] = $aliasPackage; + } + + foreach ($package->getRequires() as $link) { + $require = $link->getTarget(); + $linkConstraint = $link->getConstraint(); + + // if the required package is loaded as a locked package only and hasn't had its deps analyzed + if (isset($this->skippedLoad[$require])) { + // if we're doing a full update or this is a partial update with transitive deps and we're currently + // looking at a package which needs to be updated we need to unlock the package we now know is a + // dependency of another package which we are trying to update, and then attempt to load it again + if ($propagateUpdate && $request->getUpdateAllowTransitiveDependencies()) { + if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $this->skippedLoad[$require])) { + $this->unlockPackage($request, $require); + $this->markPackageNameForLoading($request, $require, $linkConstraint); + } elseif (!isset($this->updateAllowWarned[$this->skippedLoad[$require]])) { + $this->updateAllowWarned[$this->skippedLoad[$require]] = true; + $this->io->writeError('Dependency "'.$this->skippedLoad[$require].'" is also a root requirement. Package has not been listed as an update argument, so keeping locked at old version. Use --with-all-dependencies (-W) to include root dependencies.'); + } + } + } else { + $this->markPackageNameForLoading($request, $require, $linkConstraint); + } + } + + // if we're doing a partial update with deps we also need to unlock packages which are being replaced in case + // they are currently locked and thus prevent this updateable package from being installable/updateable + if ($propagateUpdate && $request->getUpdateAllowTransitiveDependencies()) { + foreach ($package->getReplaces() as $link) { + $replace = $link->getTarget(); + if (isset($this->loadedPackages[$replace], $this->skippedLoad[$replace])) { + if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $this->skippedLoad[$replace])) { + $this->unlockPackage($request, $replace); + $this->markPackageNameForLoading($request, $replace, $link->getConstraint()); + } elseif (!$request->getUpdateAllowTransitiveRootDependencies() && $this->isRootRequire($request, $replace) && !isset($this->updateAllowWarned[$replace])) { + $this->updateAllowWarned[$replace] = true; + $this->io->writeError('Dependency "'.$replace.'" is also a root requirement. Package has not been listed as an update argument, so keeping locked at old version. Use --with-all-dependencies (-W) to include root dependencies.'); + } + } + } + } + } + + /** + * Checks if a particular name is required directly in the request + * + * @return bool + */ + private function isRootRequire(Request $request, $name) + { + $rootRequires = $request->getRequires(); + + return isset($rootRequires[$name]); + } + + /** + * Checks whether the update allow list allows this package in the lock file to be updated + * @return bool + */ + private function isUpdateAllowed(PackageInterface $package) + { + // Path repo packages are never loaded from lock, to force them to always remain in sync + // unless symlinking is disabled in which case we probably should rather treat them like + // regular packages + if ($package->getDistType() === 'path') { + $transportOptions = $package->getTransportOptions(); + if (!isset($transportOptions['symlink']) || $transportOptions['symlink'] !== false) { + return true; + } + } + + foreach ($this->updateAllowList as $pattern => $void) { + $patternRegexp = BasePackage::packageNameToRegexp($pattern); + if (preg_match($patternRegexp, $package->getName())) { + return true; + } + } + + return false; + } + + private function warnAboutNonMatchingUpdateAllowList(Request $request) + { + foreach ($this->updateAllowList as $pattern => $void) { + $patternRegexp = BasePackage::packageNameToRegexp($pattern); + // update pattern matches a locked package? => all good + foreach ($request->getLockedRepository()->getPackages() as $package) { + if (preg_match($patternRegexp, $package->getName())) { + continue 2; + } + } + // update pattern matches a root require? => all good, probably a new package + foreach ($request->getRequires() as $packageName => $constraint) { + if (preg_match($patternRegexp, $packageName)) { + continue 2; + } + } + if (strpos($pattern, '*') !== false) { + $this->io->writeError('Pattern "' . $pattern . '" listed for update does not match any locked packages.'); + } else { + $this->io->writeError('Package "' . $pattern . '" listed for update is not locked.'); + } + } + } + + /** + * Reverts the decision to use a locked package if a partial update with transitive dependencies + * found that this package actually needs to be updated + */ + private function unlockPackage(Request $request, $name) + { + if ( + // if we unfixed a replaced package name, we also need to unfix the replacer itself + $this->skippedLoad[$name] !== $name + // as long as it was not unfixed yet + && isset($this->skippedLoad[$this->skippedLoad[$name]]) + ) { + $this->unlockPackage($request, $this->skippedLoad[$name]); + } + + unset($this->skippedLoad[$name], $this->loadedPackages[$name], $this->maxExtendedReqs[$name]); + + // remove locked package by this name which was already initialized + foreach ($request->getLockedPackages() as $lockedPackage) { + if (!($lockedPackage instanceof AliasPackage) && $lockedPackage->getName() === $name) { + if (false !== $index = array_search($lockedPackage, $this->packages, true)) { + $request->unlockPackage($lockedPackage); + $this->removeLoadedPackage($request, $lockedPackage, $index); + + // make sure that any requirements for this package by other locked or fixed packages are now + // also loaded, as they were previously ignored because the locked (now unlocked) package already + // satisfied their requirements + foreach ($request->getFixedOrLockedPackages() as $fixedOrLockedPackage) { + if ($fixedOrLockedPackage !== $lockedPackage && isset($this->skippedLoad[$fixedOrLockedPackage->getName()])) { + foreach ($fixedOrLockedPackage->getRequires() as $requireLink) { + if ($requireLink->getTarget() === $lockedPackage->getName()) { + $this->markPackageNameForLoading($request, $lockedPackage->getName(), $requireLink->getConstraint()); + } + } + } + } + } + } + } + } + + private function removeLoadedPackage(Request $request, PackageInterface $package, $index) + { + unset($this->packages[$index]); + if (isset($this->aliasMap[spl_object_hash($package)])) { + foreach ($this->aliasMap[spl_object_hash($package)] as $aliasIndex => $aliasPackage) { + $request->unlockPackage($aliasPackage); + unset($this->packages[$aliasIndex]); + } + unset($this->aliasMap[spl_object_hash($package)]); + } + } +} diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Problem.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Problem.php index df289a4c1..35ce3b045 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Problem.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Problem.php @@ -13,6 +13,13 @@ namespace Composer\DependencyResolver; use Composer\Package\CompletePackageInterface; +use Composer\Package\AliasPackage; +use Composer\Package\RootPackageInterface; +use Composer\Repository\RepositorySet; +use Composer\Repository\LockArrayRepository; +use Composer\Semver\Constraint\Constraint; +use Composer\Semver\Constraint\ConstraintInterface; +use Composer\Package\Version\VersionParser; /** * Represents a problem detected while solving dependencies @@ -23,25 +30,19 @@ class Problem { /** * A map containing the id of each rule part of this problem as a key - * @var array + * @var array */ protected $reasonSeen; /** - * A set of reasons for the problem, each is a rule or a job and a rule - * @var array + * A set of reasons for the problem, each is a rule or a root require and a rule + * @var array> */ protected $reasons = array(); + /** @var int */ protected $section = 0; - protected $pool; - - public function __construct(Pool $pool) - { - $this->pool = $pool; - } - /** * Add a rule as a reason * @@ -49,10 +50,7 @@ public function __construct(Pool $pool) */ public function addRule(Rule $rule) { - $this->addReason(spl_object_hash($rule), array( - 'rule' => $rule, - 'job' => $rule->getJob(), - )); + $this->addReason(spl_object_hash($rule), $rule); } /** @@ -68,189 +66,405 @@ public function getReasons() /** * A human readable textual representation of the problem's reasons * - * @param array $installedMap A map of all installed packages + * @param array $installedMap A map of all present packages * @return string */ - public function getPrettyString(array $installedMap = array()) + public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, array $installedMap = array(), array $learnedPool = array()) { + // TODO doesn't this entirely defeat the purpose of the problem sections? what's the point of sections? $reasons = call_user_func_array('array_merge', array_reverse($this->reasons)); if (count($reasons) === 1) { reset($reasons); - $reason = current($reasons); + $rule = current($reasons); - $job = $reason['job']; + if (!in_array($rule->getReason(), array(Rule::RULE_ROOT_REQUIRE, Rule::RULE_FIXED), true)) { + throw new \LogicException("Single reason problems must contain a request rule."); + } - $packageName = $job['packageName']; - $constraint = $job['constraint']; + $reasonData = $rule->getReasonData(); + $packageName = $reasonData['packageName']; + $constraint = $reasonData['constraint']; if (isset($constraint)) { - $packages = $this->pool->whatProvides($packageName, $constraint); + $packages = $pool->whatProvides($packageName, $constraint); } else { $packages = array(); } - if ($job && $job['cmd'] === 'install' && empty($packages)) { - - // handle php/hhvm - if ($packageName === 'php' || $packageName === 'php-64bit' || $packageName === 'hhvm') { - $version = phpversion(); - $available = $this->pool->whatProvides($packageName); + if (empty($packages)) { + return "\n ".implode(self::getMissingPackageReason($repositorySet, $request, $pool, $isVerbose, $packageName, $constraint)); + } + } - if (count($available)) { - $firstAvailable = reset($available); - $version = $firstAvailable->getPrettyVersion(); - $extra = $firstAvailable->getExtra(); - if ($firstAvailable instanceof CompletePackageInterface && isset($extra['config.platform']) && $extra['config.platform'] === true) { - $version .= '; ' . $firstAvailable->getDescription(); - } - } + return self::formatDeduplicatedRules($reasons, ' ', $repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool); + } - $msg = "\n - This package requires ".$packageName.$this->constraintToText($constraint).' but '; + /** + * @internal + */ + public static function formatDeduplicatedRules($rules, $indent, RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, array $installedMap = array(), array $learnedPool = array()) + { + $messages = array(); + $templates = array(); + $parser = new VersionParser; + $deduplicatableRuleTypes = array(Rule::RULE_PACKAGE_REQUIRES, Rule::RULE_PACKAGE_CONFLICT); + foreach ($rules as $rule) { + $message = $rule->getPrettyString($repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool); + if (in_array($rule->getReason(), $deduplicatableRuleTypes, true) && preg_match('{^(?P\S+) (?P\S+) (?Prequires|conflicts)}', $message, $m)) { + $template = preg_replace('{^\S+ \S+ }', '%s%s ', $message); + $messages[] = $template; + $templates[$template][$m[1]][$parser->normalize($m[2])] = $m[2]; + } elseif ($message !== '') { + $messages[] = $message; + } + } - if (defined('HHVM_VERSION') || (count($available) && $packageName === 'hhvm')) { - return $msg . 'your HHVM version does not satisfy that requirement.'; + $result = array(); + foreach (array_unique($messages) as $message) { + if (isset($templates[$message])) { + foreach ($templates[$message] as $package => $versions) { + uksort($versions, 'version_compare'); + if (!$isVerbose) { + $versions = self::condenseVersionList($versions, 1); } - - if ($packageName === 'hhvm') { - return $msg . 'you are running this with PHP and not HHVM.'; + if (count($versions) > 1) { + // remove the s from requires/conflicts to correct grammar + $message = preg_replace('{^(%s%s (?:require|conflict))s}', '$1', $message); + $result[] = sprintf($message, $package, '['.implode(', ', $versions).']'); + } else { + $result[] = sprintf($message, $package, ' '.reset($versions)); } + } + } else { + $result[] = $message; + } + } + + return "\n$indent- ".implode("\n$indent- ", $result); + } - return $msg . 'your PHP version ('. $version .') does not satisfy that requirement.'; + public function isCausedByLock(RepositorySet $repositorySet, Request $request, Pool $pool) + { + foreach ($this->reasons as $sectionRules) { + foreach ($sectionRules as $rule) { + if ($rule->isCausedByLock($repositorySet, $request, $pool)) { + return true; } + } + } + } - // handle php extensions - if (0 === stripos($packageName, 'ext-')) { - if (false !== strpos($packageName, ' ')) { - return "\n - The requested PHP extension ".$packageName.' should be required as '.str_replace(' ', '-', $packageName).'.'; - } + /** + * Store a reason descriptor but ignore duplicates + * + * @param string $id A canonical identifier for the reason + * @param Rule $reason The reason descriptor + */ + protected function addReason($id, Rule $reason) + { + // TODO: if a rule is part of a problem description in two sections, isn't this going to remove a message + // that is important to understand the issue? + + if (!isset($this->reasonSeen[$id])) { + $this->reasonSeen[$id] = true; + $this->reasons[$this->section][] = $reason; + } + } - $ext = substr($packageName, 4); - $error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system'; + public function nextSection() + { + $this->section++; + } - return "\n - The requested PHP extension ".$packageName.$this->constraintToText($constraint).' '.$error.'. Install or enable PHP\'s '.$ext.' extension.'; - } + /** + * @internal + */ + public static function getMissingPackageReason(RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, $packageName, $constraint = null) + { + // handle php/hhvm + if ($packageName === 'php' || $packageName === 'php-64bit' || $packageName === 'hhvm') { + $version = self::getPlatformPackageVersion($pool, $packageName, phpversion()); - // handle linked libs - if (0 === stripos($packageName, 'lib-')) { - if (strtolower($packageName) === 'lib-icu') { - $error = extension_loaded('intl') ? 'has the wrong version installed, try upgrading the intl extension.' : 'is missing from your system, make sure the intl extension is loaded.'; + $msg = "- Root composer.json requires ".$packageName.self::constraintToText($constraint).' but '; - return "\n - The requested linked library ".$packageName.$this->constraintToText($constraint).' '.$error; - } + if (defined('HHVM_VERSION') || ($packageName === 'hhvm' && count($pool->whatProvides($packageName)) > 0)) { + return array($msg, 'your HHVM version does not satisfy that requirement.'); + } - return "\n - The requested linked library ".$packageName.$this->constraintToText($constraint).' has the wrong version installed or is missing from your system, make sure to load the extension providing it.'; - } + if ($packageName === 'hhvm') { + return array($msg, 'HHVM was not detected on this machine, make sure it is in your PATH.'); + } - if (!preg_match('{^[A-Za-z0-9_./-]+$}', $packageName)) { - $illegalChars = preg_replace('{[A-Za-z0-9_./-]+}', '', $packageName); + return array($msg, 'your '.$packageName.' version ('. $version .') does not satisfy that requirement.'); + } - return "\n - The requested package ".$packageName.' could not be found, it looks like its name is invalid, "'.$illegalChars.'" is not allowed in package names.'; + // handle php extensions + if (0 === stripos($packageName, 'ext-')) { + if (false !== strpos($packageName, ' ')) { + return array('- ', "PHP extension ".$packageName.' should be required as '.str_replace(' ', '-', $packageName).'.'); + } + + $ext = substr($packageName, 4); + $version = self::getPlatformPackageVersion($pool, $packageName, phpversion($ext) ?: '0'); + + $error = extension_loaded($ext) ? 'it has the wrong version ('.$version.') installed' : 'it is missing from your system'; + + return array("- Root composer.json requires PHP extension ".$packageName.self::constraintToText($constraint).' but ', $error.'. Install or enable PHP\'s '.$ext.' extension.'); + } + + // handle linked libs + if (0 === stripos($packageName, 'lib-')) { + if (strtolower($packageName) === 'lib-icu') { + $error = extension_loaded('intl') ? 'it has the wrong version installed, try upgrading the intl extension.' : 'it is missing from your system, make sure the intl extension is loaded.'; + + return array("- Root composer.json requires linked library ".$packageName.self::constraintToText($constraint).' but ', $error); + } + + return array("- Root composer.json requires linked library ".$packageName.self::constraintToText($constraint).' but ', 'it has the wrong version installed or is missing from your system, make sure to load the extension providing it.'); + } + + $lockedPackage = null; + foreach ($request->getLockedPackages() as $package) { + if ($package->getName() === $packageName) { + $lockedPackage = $package; + if ($pool->isUnacceptableFixedOrLockedPackage($package)) { + return array("- ", $package->getPrettyName().' is fixed to '.$package->getPrettyVersion().' (lock file version) by a partial update but that version is rejected by your minimum-stability. Make sure you list it as an argument for the update command.'); } + break; + } + } - if ($providers = $this->pool->whatProvides($packageName, $constraint, true, true)) { - return "\n - The requested package ".$packageName.$this->constraintToText($constraint).' is satisfiable by '.$this->getPackageList($providers).' but these conflict with your requirements or minimum-stability.'; + // first check if the actual requested package is found in normal conditions + // if so it must mean it is rejected by another constraint than the one given here + if ($packages = $repositorySet->findPackages($packageName, $constraint)) { + $rootReqs = $repositorySet->getRootRequires(); + if (isset($rootReqs[$packageName])) { + $filtered = array_filter($packages, function ($p) use ($rootReqs, $packageName) { + return $rootReqs[$packageName]->matches(new Constraint('==', $p->getVersion())); + }); + if (0 === count($filtered)) { + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these conflict' : 'it conflicts').' with your root composer.json require ('.$rootReqs[$packageName]->getPrettyString().').'); } + } - if ($providers = $this->pool->whatProvides($packageName, null, true, true)) { - return "\n - The requested package ".$packageName.$this->constraintToText($constraint).' exists as '.$this->getPackageList($providers).' but these are rejected by your constraint.'; + if ($lockedPackage) { + $fixedConstraint = new Constraint('==', $lockedPackage->getVersion()); + $filtered = array_filter($packages, function ($p) use ($fixedConstraint) { + return $fixedConstraint->matches(new Constraint('==', $p->getVersion())); + }); + if (0 === count($filtered)) { + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but the package is fixed to '.$lockedPackage->getPrettyVersion().' (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.'); } + } - return "\n - The requested package ".$packageName.' could not be found in any version, there may be a typo in the package name.'; + $nonLockedPackages = array_filter($packages, function ($p) { + return !$p->getRepository() instanceof LockArrayRepository; + }); + + if (!$nonLockedPackages) { + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' in the lock file but not in remote repositories, make sure you avoid updating this package to keep the one from the lock file.'); } + + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but these were not loaded, likely because '.(self::hasMultipleNames($packages) ? 'they conflict' : 'it conflicts').' with another require.'); } - $messages = array(); + // check if the package is found when bypassing stability checks + if ($packages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES)) { + // we must first verify if a valid package would be found in a lower priority repository + if ($allReposPackages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) { + return self::computeCheckForLowerPrioRepo($isVerbose, $packageName, $constraint, $packages, $allReposPackages, 'minimum-stability'); + } - foreach ($reasons as $reason) { - $rule = $reason['rule']; - $job = $reason['job']; + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your minimum-stability.'); + } + + // check if the package is found when bypassing the constraint and stability checks + if ($packages = $repositorySet->findPackages($packageName, null, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES)) { + // we must first verify if a valid package would be found in a lower priority repository + if ($allReposPackages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) { + return self::computeCheckForLowerPrioRepo($isVerbose, $packageName, $constraint, $packages, $allReposPackages, 'constraint'); + } - if ($job) { - $messages[] = $this->jobToText($job); - } elseif ($rule) { - if ($rule instanceof Rule) { - $messages[] = $rule->getPrettyString($this->pool, $installedMap); + $suffix = ''; + if ($constraint instanceof Constraint && $constraint->getVersion() === 'dev-master') { + foreach ($packages as $candidate) { + if (in_array($candidate->getVersion(), array('dev-default', 'dev-main'), true)) { + $suffix = ' Perhaps dev-master was renamed to '.$candidate->getPrettyVersion().'?'; + break; + } } } + + // check if the root package is a name match and hint the dependencies on root troubleshooting article + $allReposPackages = $packages; + $topPackage = reset($allReposPackages); + if ($topPackage instanceof RootPackageInterface) { + $suffix = ' See https://getcomposer.org/dep-on-root for details and assistance.'; + } + + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match the constraint.' . $suffix); + } + + if (!preg_match('{^[A-Za-z0-9_./-]+$}', $packageName)) { + $illegalChars = preg_replace('{[A-Za-z0-9_./-]+}', '', $packageName); + + return array("- Root composer.json requires $packageName, it ", 'could not be found, it looks like its name is invalid, "'.$illegalChars.'" is not allowed in package names.'); + } + + if ($providers = $repositorySet->getProviders($packageName)) { + $maxProviders = 20; + $providersStr = implode(array_map(function ($p) { + $description = $p['description'] ? ' '.substr($p['description'], 0, 100) : ''; + + return " - ${p['name']}".$description."\n"; + }, count($providers) > $maxProviders + 1 ? array_slice($providers, 0, $maxProviders) : $providers)); + if (count($providers) > $maxProviders + 1) { + $providersStr .= ' ... and '.(count($providers) - $maxProviders).' more.'."\n"; + } + + return array("- Root composer.json requires $packageName".self::constraintToText($constraint).", it ", "could not be found in any version, but the following packages provide it:\n".$providersStr." Consider requiring one of these to satisfy the $packageName requirement."); } - return "\n - ".implode("\n - ", $messages); + return array("- Root composer.json requires $packageName, it ", "could not be found in any version, there may be a typo in the package name."); } /** - * Store a reason descriptor but ignore duplicates - * - * @param string $id A canonical identifier for the reason - * @param string $reason The reason descriptor + * @internal */ - protected function addReason($id, $reason) + public static function getPackageList(array $packages, $isVerbose) { - if (!isset($this->reasonSeen[$id])) { - $this->reasonSeen[$id] = true; - $this->reasons[$this->section][] = $reason; + $prepared = array(); + $hasDefaultBranch = array(); + foreach ($packages as $package) { + $prepared[$package->getName()]['name'] = $package->getPrettyName(); + $prepared[$package->getName()]['versions'][$package->getVersion()] = $package->getPrettyVersion().($package instanceof AliasPackage ? ' (alias of '.$package->getAliasOf()->getPrettyVersion().')' : ''); + if ($package->isDefaultBranch()) { + $hasDefaultBranch[$package->getName()] = true; + } } + foreach ($prepared as $name => $package) { + // remove the implicit default branch alias to avoid cruft in the display + if (isset($package['versions'][VersionParser::DEFAULT_BRANCH_ALIAS], $hasDefaultBranch[$name])) { + unset($package['versions'][VersionParser::DEFAULT_BRANCH_ALIAS]); + } + + uksort($package['versions'], 'version_compare'); + + if (!$isVerbose) { + $package['versions'] = self::condenseVersionList($package['versions'], 4); + } + $prepared[$name] = $package['name'].'['.implode(', ', $package['versions']).']'; + } + + return implode(', ', $prepared); } - public function nextSection() + private static function getPlatformPackageVersion(Pool $pool, $packageName, $version) { - $this->section++; + $available = $pool->whatProvides($packageName); + + if (count($available)) { + $firstAvailable = reset($available); + $version = $firstAvailable->getPrettyVersion(); + $extra = $firstAvailable->getExtra(); + if ($firstAvailable instanceof CompletePackageInterface && isset($extra['config.platform']) && $extra['config.platform'] === true) { + $version .= '; ' . str_replace('Package ', '', $firstAvailable->getDescription()); + } + } + + return $version; } /** - * Turns a job into a human readable description - * - * @param array $job - * @return string + * @param string[] $versions an array of pretty versions, with normalized versions as keys + * @return list a list of pretty versions and '...' where versions were removed */ - protected function jobToText($job) + private static function condenseVersionList(array $versions, $max, $maxDev = 16) { - $packageName = $job['packageName']; - $constraint = $job['constraint']; - switch ($job['cmd']) { - case 'install': - $packages = $this->pool->whatProvides($packageName, $constraint); - if (!$packages) { - return 'No package found to satisfy install request for '.$packageName.$this->constraintToText($constraint); - } - - return 'Installation request for '.$packageName.$this->constraintToText($constraint).' -> satisfiable by '.$this->getPackageList($packages).'.'; - case 'update': - return 'Update request for '.$packageName.$this->constraintToText($constraint).'.'; - case 'remove': - return 'Removal request for '.$packageName.$this->constraintToText($constraint).''; + if (count($versions) <= $max) { + return $versions; } - if (isset($constraint)) { - $packages = $this->pool->whatProvides($packageName, $constraint); - } else { - $packages = array(); + $filtered = array(); + $byMajor = array(); + foreach ($versions as $version => $pretty) { + if (0 === stripos($version, 'dev-')) { + $byMajor['dev'][] = $pretty; + } else { + $byMajor[preg_replace('{^(\d+)\..*}', '$1', $version)][] = $pretty; + } + } + foreach ($byMajor as $majorVersion => $versionsForMajor) { + $maxVersions = $majorVersion === 'dev' ? $maxDev : $max; + if (count($versionsForMajor) > $maxVersions) { + // output only 1st and last versions + $filtered[] = $versionsForMajor[0]; + $filtered[] = '...'; + $filtered[] = $versionsForMajor[count($versionsForMajor) - 1]; + } else { + $filtered = array_merge($filtered, $versionsForMajor); + } } - return 'Job(cmd='.$job['cmd'].', target='.$packageName.', packages=['.$this->getPackageList($packages).'])'; + return $filtered; } - protected function getPackageList($packages) + private static function hasMultipleNames(array $packages) { - $prepared = array(); + $name = null; foreach ($packages as $package) { - $prepared[$package->getName()]['name'] = $package->getPrettyName(); - $prepared[$package->getName()]['versions'][$package->getVersion()] = $package->getPrettyVersion(); + if ($name === null || $name === $package->getName()) { + $name = $package->getName(); + } else { + return true; + } } - foreach ($prepared as $name => $package) { - $prepared[$name] = $package['name'].'['.implode(', ', $package['versions']).']'; + + return false; + } + + private static function computeCheckForLowerPrioRepo($isVerbose, $packageName, $constraint, array $higherRepoPackages, array $allReposPackages, $reason) + { + $nextRepoPackages = array(); + $nextRepo = null; + + foreach ($allReposPackages as $package) { + if ($nextRepo === null || $nextRepo === $package->getRepository()) { + $nextRepoPackages[] = $package; + $nextRepo = $package->getRepository(); + } else { + break; + } } - return implode(', ', $prepared); + if ($higherRepoPackages) { + $topPackage = reset($higherRepoPackages); + if ($topPackage instanceof RootPackageInterface) { + return array( + "- Root composer.json requires $packageName".self::constraintToText($constraint).', it is ', + 'satisfiable by '.self::getPackageList($nextRepoPackages, $isVerbose).' from '.$nextRepo->getRepoName().' but '.$topPackage->getPrettyName().' is the root package and cannot be modified. See https://getcomposer.org/dep-on-root for details and assistance.', + ); + } + } + + if ($nextRepo instanceof LockArrayRepository) { + $singular = count($higherRepoPackages) === 1; + + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', it is ', + 'found '.self::getPackageList($nextRepoPackages, $isVerbose).' in the lock file and '.self::getPackageList($higherRepoPackages, $isVerbose).' from '.reset($higherRepoPackages)->getRepository()->getRepoName().' but ' . ($singular ? 'it does' : 'these do') . ' not match your '.$reason.' and ' . ($singular ? 'is' : 'are') . ' therefore not installable. Make sure you either fix the '.$reason.' or avoid updating this package to keep the one from the lock file.', ); + } + + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', it is ', 'satisfiable by '.self::getPackageList($nextRepoPackages, $isVerbose).' from '.$nextRepo->getRepoName().' but '.self::getPackageList($higherRepoPackages, $isVerbose).' from '.reset($higherRepoPackages)->getRepository()->getRepoName().' has higher repository priority. The packages with higher priority do not match your '.$reason.' and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.'); } /** - * Turns a constraint into text usable in a sentence describing a job + * Turns a constraint into text usable in a sentence describing a request * - * @param \Composer\Semver\Constraint\ConstraintInterface $constraint + * @param ?ConstraintInterface $constraint * @return string */ - protected function constraintToText($constraint) + protected static function constraintToText($constraint) { return $constraint ? ' '.$constraint->getPrettyString() : ''; } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Request.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Request.php index 85dc9c4d0..3514bd4c8 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Request.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Request.php @@ -12,67 +12,194 @@ namespace Composer\DependencyResolver; +use Composer\Package\Package; +use Composer\Package\PackageInterface; +use Composer\Repository\LockArrayRepository; use Composer\Semver\Constraint\ConstraintInterface; +use Composer\Semver\Constraint\MatchAllConstraint; /** * @author Nils Adermann */ class Request { - protected $jobs; + /** + * Identifies a partial update for listed packages only, all dependencies will remain at locked versions + */ + const UPDATE_ONLY_LISTED = 0; + + /** + * Identifies a partial update for listed packages and recursively all their dependencies, however dependencies + * also directly required by the root composer.json and their dependencies will remain at the locked version. + */ + const UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE = 1; + + /** + * Identifies a partial update for listed packages and recursively all their dependencies, even dependencies + * also directly required by the root composer.json will be updated. + */ + const UPDATE_LISTED_WITH_TRANSITIVE_DEPS = 2; - public function __construct() + /** @var ?LockArrayRepository */ + protected $lockedRepository; + /** @var array */ + protected $requires = array(); + /** @var array */ + protected $fixedPackages = array(); + /** @var array */ + protected $lockedPackages = array(); + /** @var array */ + protected $fixedLockedPackages = array(); + /** @var string[] */ + protected $updateAllowList = array(); + /** @var false|self::UPDATE_* */ + protected $updateAllowTransitiveDependencies = false; + + public function __construct(LockArrayRepository $lockedRepository = null) { - $this->jobs = array(); + $this->lockedRepository = $lockedRepository; } - public function install($packageName, ConstraintInterface $constraint = null) + public function requireName($packageName, ConstraintInterface $constraint = null) { - $this->addJob($packageName, 'install', $constraint); + $packageName = strtolower($packageName); + + if ($constraint === null) { + $constraint = new MatchAllConstraint(); + } + if (isset($this->requires[$packageName])) { + throw new \LogicException('Overwriting requires seems like a bug ('.$packageName.' '.$this->requires[$packageName]->getPrettyString().' => '.$constraint->getPrettyString().', check why it is happening, might be a root alias'); + } + $this->requires[$packageName] = $constraint; } - public function update($packageName, ConstraintInterface $constraint = null) + /** + * Mark a package as currently present and having to remain installed + * + * This is used for platform packages which cannot be modified by Composer. A rule enforcing their installation is + * generated for dependency resolution. Partial updates with dependencies cannot in any way modify these packages. + */ + public function fixPackage(PackageInterface $package) { - $this->addJob($packageName, 'update', $constraint); + $this->fixedPackages[spl_object_hash($package)] = $package; } - public function remove($packageName, ConstraintInterface $constraint = null) + /** + * Mark a package as locked to a specific version but removable + * + * This is used for lock file packages which need to be treated similar to fixed packages by the pool builder in + * that by default they should really only have the currently present version loaded and no remote alternatives. + * + * However unlike fixed packages there will not be a special rule enforcing their installation for the solver, so + * if nothing requires these packages they will be removed. Additionally in a partial update these packages can be + * unlocked, meaning other versions can be installed if explicitly requested as part of the update. + */ + public function lockPackage(PackageInterface $package) { - $this->addJob($packageName, 'remove', $constraint); + $this->lockedPackages[spl_object_hash($package)] = $package; } /** - * Mark an existing package as being installed and having to remain installed - * - * These jobs will not be tempered with by the solver + * Marks a locked package fixed. So it's treated irremovable like a platform package. * - * @param string $packageName - * @param ConstraintInterface|null $constraint + * This is necessary for the composer install step which verifies the lock file integrity and should not allow + * removal of any packages. At the same time lock packages there cannot simply be marked fixed, as error reporting + * would then report them as platform packages, so this still marks them as locked packages at the same time. */ - public function fix($packageName, ConstraintInterface $constraint = null) + public function fixLockedPackage(PackageInterface $package) { - $this->addJob($packageName, 'install', $constraint, true); + $this->fixedPackages[spl_object_hash($package)] = $package; + $this->fixedLockedPackages[spl_object_hash($package)] = $package; } - protected function addJob($packageName, $cmd, ConstraintInterface $constraint = null, $fixed = false) + public function unlockPackage(PackageInterface $package) { - $packageName = strtolower($packageName); + unset($this->lockedPackages[spl_object_hash($package)]); + } + + public function setUpdateAllowList($updateAllowList, $updateAllowTransitiveDependencies) + { + $this->updateAllowList = $updateAllowList; + $this->updateAllowTransitiveDependencies = $updateAllowTransitiveDependencies; + } - $this->jobs[] = array( - 'cmd' => $cmd, - 'packageName' => $packageName, - 'constraint' => $constraint, - 'fixed' => $fixed, - ); + public function getUpdateAllowList() + { + return $this->updateAllowList; } - public function updateAll() + public function getUpdateAllowTransitiveDependencies() { - $this->jobs[] = array('cmd' => 'update-all'); + return $this->updateAllowTransitiveDependencies !== self::UPDATE_ONLY_LISTED; + } + + public function getUpdateAllowTransitiveRootDependencies() + { + return $this->updateAllowTransitiveDependencies === self::UPDATE_LISTED_WITH_TRANSITIVE_DEPS; + } + + public function getRequires() + { + return $this->requires; + } + + public function getFixedPackages() + { + return $this->fixedPackages; + } + + public function isFixedPackage(PackageInterface $package) + { + return isset($this->fixedPackages[spl_object_hash($package)]); + } + + public function getLockedPackages() + { + return $this->lockedPackages; + } + + public function isLockedPackage(PackageInterface $package) + { + return isset($this->lockedPackages[spl_object_hash($package)]) || isset($this->fixedLockedPackages[spl_object_hash($package)]); + } + + public function getFixedOrLockedPackages() + { + return array_merge($this->fixedPackages, $this->lockedPackages); + } + + // TODO look into removing the packageIds option, the only place true is used is for the installed map in the solver problems + // some locked packages may not be in the pool so they have a package->id of -1 + public function getPresentMap($packageIds = false) + { + $presentMap = array(); + + if ($this->lockedRepository) { + foreach ($this->lockedRepository->getPackages() as $package) { + $presentMap[$packageIds ? $package->getId() : spl_object_hash($package)] = $package; + } + } + + foreach ($this->fixedPackages as $package) { + $presentMap[$packageIds ? $package->getId() : spl_object_hash($package)] = $package; + } + + return $presentMap; + } + + public function getFixedPackagesMap() + { + $fixedPackagesMap = array(); + + foreach ($this->fixedPackages as $package) { + $fixedPackagesMap[$package->getId()] = $package; + } + + return $fixedPackagesMap; } - public function getJobs() + public function getLockedRepository() { - return $this->jobs; + return $this->lockedRepository; } } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Rule.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Rule.php index 82c9c499c..edc486496 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Rule.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Rule.php @@ -12,51 +12,57 @@ namespace Composer\DependencyResolver; -use Composer\Package\CompletePackage; use Composer\Package\Link; -use Composer\Package\PackageInterface; +use Composer\Package\BasePackage; +use Composer\Package\AliasPackage; +use Composer\Repository\RepositorySet; +use Composer\Repository\PlatformRepository; +use Composer\Package\Version\VersionParser; +use Composer\Semver\Constraint\Constraint; +use Composer\Semver\Constraint\ConstraintInterface; /** * @author Nils Adermann * @author Ruben Gonzalez + * @phpstan-type ReasonData Link|BasePackage|string|int|array{packageName: string, constraint: ConstraintInterface}|array{package: BasePackage} */ abstract class Rule { - // reason constants - const RULE_INTERNAL_ALLOW_UPDATE = 1; - const RULE_JOB_INSTALL = 2; - const RULE_JOB_REMOVE = 3; - const RULE_PACKAGE_CONFLICT = 6; - const RULE_PACKAGE_REQUIRES = 7; - const RULE_PACKAGE_OBSOLETES = 8; - const RULE_INSTALLED_PACKAGE_OBSOLETES = 9; - const RULE_PACKAGE_SAME_NAME = 10; - const RULE_PACKAGE_IMPLICIT_OBSOLETES = 11; - const RULE_LEARNED = 12; - const RULE_PACKAGE_ALIAS = 13; + // reason constants and // their reason data contents + const RULE_ROOT_REQUIRE = 2; // array{packageName: string, constraint: ConstraintInterface} + const RULE_FIXED = 3; // array{package: BasePackage} + const RULE_PACKAGE_CONFLICT = 6; // Link + const RULE_PACKAGE_REQUIRES = 7; // Link + const RULE_PACKAGE_SAME_NAME = 10; // string (package name) + const RULE_LEARNED = 12; // int (rule id) + const RULE_PACKAGE_ALIAS = 13; // BasePackage + const RULE_PACKAGE_INVERSE_ALIAS = 14; // BasePackage // bitfield defs const BITFIELD_TYPE = 0; const BITFIELD_REASON = 8; const BITFIELD_DISABLED = 16; + /** @var int */ protected $bitfield; - protected $job; + /** @var Request */ + protected $request; + /** + * @var Link|BasePackage|ConstraintInterface|string + * @phpstan-var ReasonData + */ protected $reasonData; /** - * @param int $reason A RULE_* constant describing the reason for generating this rule - * @param Link|PackageInterface $reasonData - * @param array $job The job this rule was created from + * @param self::RULE_* $reason A RULE_* constant describing the reason for generating this rule + * @param Link|BasePackage|ConstraintInterface|string $reasonData + * + * @phpstan-param ReasonData $reasonData */ - public function __construct($reason, $reasonData, $job = null) + public function __construct($reason, $reasonData) { $this->reasonData = $reasonData; - if ($job) { - $this->job = $job; - } - $this->bitfield = (0 << self::BITFIELD_DISABLED) | ($reason << self::BITFIELD_REASON) | (255 << self::BITFIELD_TYPE); @@ -66,10 +72,7 @@ abstract public function getLiterals(); abstract public function getHash(); - public function getJob() - { - return $this->job; - } + abstract public function __toString(); abstract public function equals(Rule $rule); @@ -83,15 +86,26 @@ public function getReasonData() return $this->reasonData; } + /** + * @return ?string + */ public function getRequiredPackage() { - if ($this->getReason() === self::RULE_JOB_INSTALL) { - return $this->reasonData; + $reason = $this->getReason(); + + if ($reason === self::RULE_ROOT_REQUIRE) { + return $this->reasonData['packageName']; } - if ($this->getReason() === self::RULE_PACKAGE_REQUIRES) { + if ($reason === self::RULE_FIXED) { + return $this->reasonData['package']->getName(); + } + + if ($reason === self::RULE_PACKAGE_REQUIRES) { return $this->reasonData->getTarget(); } + + return null; } public function setType($type) @@ -126,119 +140,257 @@ public function isEnabled() abstract public function isAssertion(); - public function getPrettyString(Pool $pool, array $installedMap = array()) + public function isCausedByLock(RepositorySet $repositorySet, Request $request, Pool $pool) { - $literals = $this->getLiterals(); + if ($this->getReason() === self::RULE_PACKAGE_REQUIRES) { + if (PlatformRepository::isPlatformPackage($this->reasonData->getTarget())) { + return false; + } + if ($request->getLockedRepository()) { + foreach ($request->getLockedRepository()->getPackages() as $package) { + if ($package->getName() === $this->reasonData->getTarget()) { + if ($pool->isUnacceptableFixedOrLockedPackage($package)) { + return true; + } + if (!$this->reasonData->getConstraint()->matches(new Constraint('=', $package->getVersion()))) { + return true; + } + // required package was locked but has been unlocked and still matches + if (!$request->isLockedPackage($package)) { + return true; + } + break; + } + } + } + } - $ruleText = ''; - foreach ($literals as $i => $literal) { - if ($i != 0) { - $ruleText .= '|'; + if ($this->getReason() === self::RULE_ROOT_REQUIRE) { + if (PlatformRepository::isPlatformPackage($this->reasonData['packageName'])) { + return false; + } + if ($request->getLockedRepository()) { + foreach ($request->getLockedRepository()->getPackages() as $package) { + if ($package->getName() === $this->reasonData['packageName']) { + if ($pool->isUnacceptableFixedOrLockedPackage($package)) { + return true; + } + if (!$this->reasonData['constraint']->matches(new Constraint('=', $package->getVersion()))) { + return true; + } + break; + } + } } - $ruleText .= $pool->literalToPrettyString($literal, $installedMap); } + return false; + } + + public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, array $installedMap = array(), array $learnedPool = array()) + { + $literals = $this->getLiterals(); + switch ($this->getReason()) { - case self::RULE_INTERNAL_ALLOW_UPDATE: - return $ruleText; + case self::RULE_ROOT_REQUIRE: + $packageName = $this->reasonData['packageName']; + $constraint = $this->reasonData['constraint']; + + $packages = $pool->whatProvides($packageName, $constraint); + if (!$packages) { + return 'No package found to satisfy root composer.json require '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : ''); + } + + $packagesNonAlias = array_values(array_filter($packages, function ($p) { + return !($p instanceof AliasPackage); + })); + if (count($packagesNonAlias) === 1) { + $package = $packagesNonAlias[0]; + if ($request->isLockedPackage($package)) { + return $package->getPrettyName().' is locked to version '.$package->getPrettyVersion()." and an update of this package was not requested."; + } + } - case self::RULE_JOB_INSTALL: - return "Install command rule ($ruleText)"; + return 'Root composer.json requires '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '').' -> satisfiable by '.$this->formatPackagesUnique($pool, $packages, $isVerbose).'.'; - case self::RULE_JOB_REMOVE: - return "Remove command rule ($ruleText)"; + case self::RULE_FIXED: + $package = $this->deduplicateDefaultBranchAlias($this->reasonData['package']); + + if ($request->isLockedPackage($package)) { + return $package->getPrettyName().' is locked to version '.$package->getPrettyVersion().' and an update of this package was not requested.'; + } + + return $package->getPrettyName().' is present at version '.$package->getPrettyVersion() . ' and cannot be modified by Composer'; case self::RULE_PACKAGE_CONFLICT: - $package1 = $pool->literalToPackage($literals[0]); - $package2 = $pool->literalToPackage($literals[1]); + $package1 = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($literals[0])); + $package2 = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($literals[1])); + + $conflictTarget = $package1->getPrettyString(); + if ($reasonData = $this->getReasonData()) { + // swap literals if they are not in the right order with package2 being the conflicter + if ($reasonData->getSource() === $package1->getName()) { + list($package2, $package1) = array($package1, $package2); + } + + // if the conflict is not directly against the package but something it provides/replaces, + // we try to find that link to display a better message + if ($reasonData->getTarget() !== $package1->getName()) { + $provideType = null; + $provided = null; + foreach ($package1->getProvides() as $provide) { + if ($provide->getTarget() === $reasonData->getTarget()) { + $provideType = 'provides'; + $provided = $provide->getPrettyConstraint(); + break; + } + } + foreach ($package1->getReplaces() as $replace) { + if ($replace->getTarget() === $reasonData->getTarget()) { + $provideType = 'replaces'; + $provided = $replace->getPrettyConstraint(); + break; + } + } + if (null !== $provideType) { + $conflictTarget = $reasonData->getTarget().' '.$reasonData->getPrettyConstraint().' ('.$package1->getPrettyString().' '.$provideType.' '.$reasonData->getTarget().' '.$provided.')'; + } + } + } - return $package1->getPrettyString().' conflicts with '.$this->formatPackagesUnique($pool, array($package2)).'.'; + return $package2->getPrettyString().' conflicts with '.$conflictTarget.'.'; case self::RULE_PACKAGE_REQUIRES: $sourceLiteral = array_shift($literals); - $sourcePackage = $pool->literalToPackage($sourceLiteral); + $sourcePackage = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($sourceLiteral)); + /** @var Link */ + $reasonData = $this->reasonData; $requires = array(); foreach ($literals as $literal) { $requires[] = $pool->literalToPackage($literal); } - $text = $this->reasonData->getPrettyString($sourcePackage); + $text = $reasonData->getPrettyString($sourcePackage); if ($requires) { - $text .= ' -> satisfiable by ' . $this->formatPackagesUnique($pool, $requires) . '.'; + $text .= ' -> satisfiable by ' . $this->formatPackagesUnique($pool, $requires, $isVerbose) . '.'; } else { - $targetName = $this->reasonData->getTarget(); + $targetName = $reasonData->getTarget(); - if ($targetName === 'php' || $targetName === 'php-64bit' || $targetName === 'hhvm') { - // handle php/hhvm - if (defined('HHVM_VERSION')) { - return $text . ' -> your HHVM version does not satisfy that requirement.'; - } + $reason = Problem::getMissingPackageReason($repositorySet, $request, $pool, $isVerbose, $targetName, $this->reasonData->getConstraint()); - $packages = $pool->whatProvides($targetName); - $package = count($packages) ? current($packages) : phpversion(); + return $text . ' -> ' . $reason[1]; + } - if ($targetName === 'hhvm') { - if ($package instanceof CompletePackage) { - return $text . ' -> your HHVM version ('.$package->getPrettyVersion().') does not satisfy that requirement.'; - } else { - return $text . ' -> you are running this with PHP and not HHVM.'; - } - } + return $text; + case self::RULE_PACKAGE_SAME_NAME: + $packageNames = array(); + foreach ($literals as $literal) { + $package = $pool->literalToPackage($literal); + $packageNames[$package->getName()] = true; + } + $replacedName = $this->reasonData; - if (!($package instanceof CompletePackage)) { - return $text . ' -> your PHP version ('.phpversion().') does not satisfy that requirement.'; - } + if (count($packageNames) > 1) { + $reason = null; - $extra = $package->getExtra(); + if (!isset($packageNames[$replacedName])) { + $reason = 'They '.(count($literals) == 2 ? 'both' : 'all').' replace '.$replacedName.' and thus cannot coexist.'; + } else { + $replacerNames = $packageNames; + unset($replacerNames[$replacedName]); + $replacerNames = array_keys($replacerNames); - if (!empty($extra['config.platform'])) { - $text .= ' -> your PHP version ('.phpversion().') overridden by "config.platform.php" version ('.$package->getPrettyVersion().') does not satisfy that requirement.'; + if (count($replacerNames) == 1) { + $reason = $replacerNames[0] . ' replaces '; } else { - $text .= ' -> your PHP version ('.$package->getPrettyVersion().') does not satisfy that requirement.'; + $reason = '['.implode(', ', $replacerNames).'] replace '; } - - return $text; + $reason .= $replacedName.' and thus cannot coexist with it.'; } - if (0 === strpos($targetName, 'ext-')) { - // handle php extensions - $ext = substr($targetName, 4); - $error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system'; + $installedPackages = array(); + $removablePackages = array(); + foreach ($literals as $literal) { + if (isset($installedMap[abs($literal)])) { + $installedPackages[] = $pool->literalToPackage($literal); + } else { + $removablePackages[] = $pool->literalToPackage($literal); + } + } - return $text . ' -> the requested PHP extension '.$ext.' '.$error.'.'; + if ($installedPackages && $removablePackages) { + return $this->formatPackagesUnique($pool, $removablePackages, $isVerbose).' cannot be installed as that would require removing '.$this->formatPackagesUnique($pool, $installedPackages, $isVerbose).'. '.$reason; } - if (0 === strpos($targetName, 'lib-')) { - // handle linked libs - $lib = substr($targetName, 4); + return 'Only one of these can be installed: '.$this->formatPackagesUnique($pool, $literals, $isVerbose).'. '.$reason; + } - return $text . ' -> the requested linked library '.$lib.' has the wrong version installed or is missing from your system, make sure to have the extension providing it.'; - } + return 'You can only install one version of a package, so only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals, $isVerbose) . '.'; + case self::RULE_LEARNED: + /** @TODO currently still generates way too much output to be helpful, and in some cases can even lead to endless recursion */ + // if (isset($learnedPool[$this->reasonData])) { + // echo $this->reasonData."\n"; + // $learnedString = ', learned rules:' . Problem::formatDeduplicatedRules($learnedPool[$this->reasonData], ' ', $repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool); + // } else { + // $learnedString = ' (reasoning unavailable)'; + // } + $learnedString = ' (conflict analysis result)'; + + if (count($literals) === 1) { + $ruleText = $pool->literalToPrettyString($literals[0], $installedMap); + } else { + $groups = array(); + foreach ($literals as $literal) { + $package = $pool->literalToPackage($literal); + if (isset($installedMap[$package->id])) { + $group = $literal > 0 ? 'keep' : 'remove'; + } else { + $group = $literal > 0 ? 'install' : 'don\'t install'; + } - if ($providers = $pool->whatProvides($targetName, $this->reasonData->getConstraint(), true, true)) { - return $text . ' -> satisfiable by ' . $this->formatPackagesUnique($pool, $providers) .' but these conflict with your requirements or minimum-stability.'; + $groups[$group][] = $this->deduplicateDefaultBranchAlias($package); + } + $ruleTexts = array(); + foreach ($groups as $group => $packages) { + $ruleTexts[] = $group . (count($packages) > 1 ? ' one of' : '').' ' . $this->formatPackagesUnique($pool, $packages, $isVerbose); } - return $text . ' -> no matching package found.'; + $ruleText = implode(' | ', $ruleTexts); } - return $text; - - case self::RULE_PACKAGE_OBSOLETES: - return $ruleText; - case self::RULE_INSTALLED_PACKAGE_OBSOLETES: - return $ruleText; - case self::RULE_PACKAGE_SAME_NAME: - return 'Can only install one of: ' . $this->formatPackagesUnique($pool, $literals) . '.'; - case self::RULE_PACKAGE_IMPLICIT_OBSOLETES: - return $ruleText; - case self::RULE_LEARNED: - return 'Conclusion: '.$ruleText; + return 'Conclusion: '.$ruleText.$learnedString; case self::RULE_PACKAGE_ALIAS: - return $ruleText; + $aliasPackage = $pool->literalToPackage($literals[0]); + + // avoid returning content like "9999999-dev is an alias of dev-master" as it is useless + if ($aliasPackage->getVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) { + return ''; + } + $package = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($literals[1])); + + return $aliasPackage->getPrettyString() .' is an alias of '.$package->getPrettyString().' and thus requires it to be installed too.'; + case self::RULE_PACKAGE_INVERSE_ALIAS: + // inverse alias rules work the other way around than above + $aliasPackage = $pool->literalToPackage($literals[1]); + + // avoid returning content like "9999999-dev is an alias of dev-master" as it is useless + if ($aliasPackage->getVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) { + return ''; + } + $package = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($literals[0])); + + return $aliasPackage->getPrettyString() .' is an alias of '.$package->getPrettyString().' and must be installed with it.'; default: + $ruleText = ''; + foreach ($literals as $i => $literal) { + if ($i != 0) { + $ruleText .= '|'; + } + $ruleText .= $pool->literalToPrettyString($literal, $installedMap); + } + return '('.$ruleText.')'; } } @@ -249,20 +401,23 @@ public function getPrettyString(Pool $pool, array $installedMap = array()) * * @return string */ - protected function formatPackagesUnique($pool, array $packages) + protected function formatPackagesUnique($pool, array $packages, $isVerbose) { - $prepared = array(); - foreach ($packages as $package) { - if (!is_object($package)) { - $package = $pool->literalToPackage($package); + foreach ($packages as $index => $package) { + if (!\is_object($package)) { + $packages[$index] = $pool->literalToPackage($package); } - $prepared[$package->getName()]['name'] = $package->getPrettyName(); - $prepared[$package->getName()]['versions'][$package->getVersion()] = $package->getPrettyVersion(); } - foreach ($prepared as $name => $package) { - $prepared[$name] = $package['name'].'['.implode(', ', $package['versions']).']'; + + return Problem::getPackageList($packages, $isVerbose); + } + + private function deduplicateDefaultBranchAlias(BasePackage $package) + { + if ($package instanceof AliasPackage && $package->getPrettyVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) { + $package = $package->getAliasOf(); } - return implode(', ', $prepared); + return $package; } } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Rule2Literals.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Rule2Literals.php index 6bf47db34..6a4c599ce 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Rule2Literals.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Rule2Literals.php @@ -12,7 +12,7 @@ namespace Composer\DependencyResolver; -use Composer\Package\PackageInterface; +use Composer\Package\BasePackage; use Composer\Package\Link; /** @@ -20,19 +20,18 @@ */ class Rule2Literals extends Rule { + /** @var int */ protected $literal1; + /** @var int */ protected $literal2; /** - * @param int $literal1 - * @param int $literal2 - * @param int $reason A RULE_* constant describing the reason for generating this rule - * @param Link|PackageInterface $reasonData - * @param array $job The job this rule was created from + * @param int $literal1 + * @param int $literal2 */ - public function __construct($literal1, $literal2, $reason, $reasonData, $job = null) + public function __construct($literal1, $literal2, $reason, $reasonData) { - parent::__construct($reason, $reasonData, $job); + parent::__construct($reason, $reasonData); if ($literal1 < $literal2) { $this->literal1 = $literal1; @@ -43,11 +42,13 @@ public function __construct($literal1, $literal2, $reason, $reasonData, $job = n } } + /** @return int[] */ public function getLiterals() { return array($this->literal1, $this->literal2); } + /** @return string */ public function getHash() { return $this->literal1.','.$this->literal2; @@ -77,7 +78,7 @@ public function equals(Rule $rule) } $literals = $rule->getLiterals(); - if (2 != count($literals)) { + if (2 != \count($literals)) { return false; } @@ -92,6 +93,7 @@ public function equals(Rule $rule) return true; } + /** @return false */ public function isAssertion() { return false; diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleSet.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleSet.php index bf4de0d7c..ba9aeaee2 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleSet.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleSet.php @@ -12,44 +12,48 @@ namespace Composer\DependencyResolver; +use Composer\Repository\RepositorySet; + /** * @author Nils Adermann + * @implements \IteratorAggregate */ class RuleSet implements \IteratorAggregate, \Countable { // highest priority => lowest number const TYPE_PACKAGE = 0; - const TYPE_JOB = 1; + const TYPE_REQUEST = 1; const TYPE_LEARNED = 4; /** * READ-ONLY: Lookup table for rule id to rule object * - * @var Rule[] + * @var array */ - public $ruleById; + public $ruleById = array(); + /** @var array<255|0|1|4, string> */ protected static $types = array( 255 => 'UNKNOWN', self::TYPE_PACKAGE => 'PACKAGE', - self::TYPE_JOB => 'JOB', + self::TYPE_REQUEST => 'REQUEST', self::TYPE_LEARNED => 'LEARNED', ); + /** @var array */ protected $rules; - protected $nextRuleId; - protected $rulesByHash; + /** @var int */ + protected $nextRuleId = 0; + + /** @var array */ + protected $rulesByHash = array(); public function __construct() { - $this->nextRuleId = 0; - foreach ($this->getTypes() as $type) { $this->rules[$type] = array(); } - - $this->rulesByHash = array(); } public function add(Rule $rule, $type) @@ -63,7 +67,7 @@ public function add(Rule $rule, $type) // Do not add if rule already exists if (isset($this->rulesByHash[$hash])) { $potentialDuplicates = $this->rulesByHash[$hash]; - if (is_array($potentialDuplicates)) { + if (\is_array($potentialDuplicates)) { foreach ($potentialDuplicates as $potentialDuplicate) { if ($rule->equals($potentialDuplicate)) { return; @@ -88,7 +92,7 @@ public function add(Rule $rule, $type) if (!isset($this->rulesByHash[$hash])) { $this->rulesByHash[$hash] = $rule; - } elseif (is_array($this->rulesByHash[$hash])) { + } elseif (\is_array($this->rulesByHash[$hash])) { $this->rulesByHash[$hash][] = $rule; } else { $originalRule = $this->rulesByHash[$hash]; @@ -96,6 +100,7 @@ public function add(Rule $rule, $type) } } + #[\ReturnTypeWillChange] public function count() { return $this->nextRuleId; @@ -106,23 +111,34 @@ public function ruleById($id) return $this->ruleById[$id]; } + /** @return array */ public function getRules() { return $this->rules; } + /** + * @return RuleSetIterator + */ + #[\ReturnTypeWillChange] public function getIterator() { return new RuleSetIterator($this->getRules()); } + /** + * @param self::TYPE_*|array $types + * @return RuleSetIterator + */ public function getIteratorFor($types) { - if (!is_array($types)) { + if (!\is_array($types)) { $types = array($types); } $allRules = $this->getRules(); + + /** @var array $rules */ $rules = array(); foreach ($types as $type) { @@ -134,7 +150,7 @@ public function getIteratorFor($types) public function getIteratorWithout($types) { - if (!is_array($types)) { + if (!\is_array($types)) { $types = array($types); } @@ -147,6 +163,7 @@ public function getIteratorWithout($types) return new RuleSetIterator($rules); } + /** @return array{0: 0, 1: 1, 2: 4} */ public function getTypes() { $types = self::$types; @@ -155,13 +172,13 @@ public function getTypes() return array_keys($types); } - public function getPrettyString(Pool $pool = null) + public function getPrettyString(RepositorySet $repositorySet = null, Request $request = null, Pool $pool = null, $isVerbose = false) { $string = "\n"; foreach ($this->rules as $type => $rules) { $string .= str_pad(self::$types[$type], 8, ' ') . ": "; foreach ($rules as $rule) { - $string .= ($pool ? $rule->getPrettyString($pool) : $rule)."\n"; + $string .= ($repositorySet && $request && $pool ? $rule->getPrettyString($repositorySet, $request, $pool, $isVerbose) : $rule)."\n"; } $string .= "\n\n"; } @@ -171,6 +188,6 @@ public function getPrettyString(Pool $pool = null) public function __toString() { - return $this->getPrettyString(null); + return $this->getPrettyString(); } } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleSetGenerator.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleSetGenerator.php index 8638440bd..245f1fdda 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -12,30 +12,33 @@ namespace Composer\DependencyResolver; -use Composer\Package\PackageInterface; +use Composer\Package\BasePackage; use Composer\Package\AliasPackage; +use Composer\Package\PackageInterface; use Composer\Repository\PlatformRepository; /** * @author Nils Adermann + * @phpstan-import-type ReasonData from Rule */ class RuleSetGenerator { + /** @var PolicyInterface */ protected $policy; + /** @var Pool */ protected $pool; + /** @var RuleSet */ protected $rules; - protected $jobs; - protected $installedMap; - protected $allowListedMap; - protected $addedMap; - protected $conflictAddedMap; - protected $addedPackages; - protected $addedPackagesByNames; + /** @var array */ + protected $addedMap = array(); + /** @var array */ + protected $addedPackagesByNames = array(); public function __construct(PolicyInterface $policy, Pool $pool) { $this->policy = $policy; $this->pool = $pool; + $this->rules = new RuleSet; } /** @@ -44,15 +47,17 @@ public function __construct(PolicyInterface $policy, Pool $pool) * This rule is of the form (-A|B|C), where B and C are the providers of * one requirement of the package A. * - * @param PackageInterface $package The package with a requirement - * @param array $providers The providers of the requirement - * @param int $reason A RULE_* constant describing the - * reason for generating this rule - * @param mixed $reasonData Any data, e.g. the requirement name, - * that goes with the reason - * @return Rule|null The generated rule or null if tautological + * @param BasePackage $package The package with a requirement + * @param array $providers The providers of the requirement + * @param Rule::RULE_* $reason A RULE_* constant describing the + * reason for generating this rule + * @param mixed $reasonData Any data, e.g. the requirement name, + * that goes with the reason + * @return Rule|null The generated rule or null if tautological + * + * @phpstan-param ReasonData $reasonData */ - protected function createRequireRule(PackageInterface $package, array $providers, $reason, $reasonData = null) + protected function createRequireRule(BasePackage $package, array $providers, $reason, $reasonData = null) { $literals = array(-$package->id); @@ -73,36 +78,22 @@ protected function createRequireRule(PackageInterface $package, array $providers * The rule is (A|B|C) with A, B and C different packages. If the given * set of packages is empty an impossible rule is generated. * - * @param array $packages The set of packages to choose from - * @param int $reason A RULE_* constant describing the reason for - * generating this rule - * @param array $job The job this rule was created from - * @return Rule The generated rule + * @param BasePackage[] $packages The set of packages to choose from + * @param Rule::RULE_* $reason A RULE_* constant describing the reason for + * generating this rule + * @param array $reasonData Additional data like the root require or fix request info + * @return Rule The generated rule + * + * @phpstan-param ReasonData $reasonData */ - protected function createInstallOneOfRule(array $packages, $reason, $job) + protected function createInstallOneOfRule(array $packages, $reason, $reasonData) { $literals = array(); foreach ($packages as $package) { $literals[] = $package->id; } - return new GenericRule($literals, $reason, $job['packageName'], $job); - } - - /** - * Creates a rule to remove a package - * - * The rule for a package A is (-A). - * - * @param PackageInterface $package The package to be removed - * @param int $reason A RULE_* constant describing the - * reason for generating this rule - * @param array $job The job this rule was created from - * @return Rule The generated rule - */ - protected function createRemoveRule(PackageInterface $package, $reason, $job) - { - return new GenericRule(array(-$package->id), $reason, $job['packageName'], $job); + return new GenericRule($literals, $reason, $reasonData); } /** @@ -111,15 +102,17 @@ protected function createRemoveRule(PackageInterface $package, $reason, $job) * The rule for conflicting packages A and B is (-A|-B). A is called the issuer * and B the provider. * - * @param PackageInterface $issuer The package declaring the conflict - * @param PackageInterface $provider The package causing the conflict - * @param int $reason A RULE_* constant describing the - * reason for generating this rule - * @param mixed $reasonData Any data, e.g. the package name, that - * goes with the reason - * @return Rule|null The generated rule + * @param BasePackage $issuer The package declaring the conflict + * @param BasePackage $provider The package causing the conflict + * @param Rule::RULE_* $reason A RULE_* constant describing the + * reason for generating this rule + * @param mixed $reasonData Any data, e.g. the package name, that + * goes with the reason + * @return Rule|null The generated rule + * + * @phpstan-param ReasonData $reasonData */ - protected function createRule2Literals(PackageInterface $issuer, PackageInterface $provider, $reason, $reasonData = null) + protected function createRule2Literals(BasePackage $issuer, BasePackage $provider, $reason, $reasonData = null) { // ignore self conflict if ($issuer === $provider) { @@ -129,6 +122,20 @@ protected function createRule2Literals(PackageInterface $issuer, PackageInterfac return new Rule2Literals(-$issuer->id, -$provider->id, $reason, $reasonData); } + protected function createMultiConflictRule(array $packages, $reason, $reasonData = null) + { + $literals = array(); + foreach ($packages as $package) { + $literals[] = -$package->id; + } + + if (\count($literals) == 2) { + return new Rule2Literals($literals[0], $literals[1], $reason, $reasonData); + } + + return new MultiConflictRule($literals, $reason, $reasonData); + } + /** * Adds a rule unless it duplicates an existing one of any type * @@ -147,71 +154,40 @@ private function addRule($type, Rule $newRule = null) $this->rules->add($newRule, $type); } - protected function allowListFromPackage(PackageInterface $package) - { - // call original method for BC - $this->whitelistFromPackage($package); - } - - /** - * @deprecated use whitelistFromPackage instead - */ - protected function whitelistFromPackage(PackageInterface $package) + protected function addRulesForPackage(BasePackage $package, $ignorePlatformReqs) { + /** @var \SplQueue */ $workQueue = new \SplQueue; $workQueue->enqueue($package); while (!$workQueue->isEmpty()) { $package = $workQueue->dequeue(); - if (isset($this->allowListedMap[$package->id])) { + if (isset($this->addedMap[$package->id])) { continue; } - $this->allowListedMap[$package->id] = true; + $this->addedMap[$package->id] = $package; - foreach ($package->getRequires() as $link) { - $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint(), true); - - foreach ($possibleRequires as $require) { - $workQueue->enqueue($require); + if (!$package instanceof AliasPackage) { + foreach ($package->getNames(false) as $name) { + $this->addedPackagesByNames[$name][] = $package; } - } + } else { + $workQueue->enqueue($package->getAliasOf()); + $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, array($package->getAliasOf()), Rule::RULE_PACKAGE_ALIAS, $package)); - $obsoleteProviders = $this->pool->whatProvides($package->getName(), null, true); + // aliases must be installed with their main package, so create a rule the other way around as well + $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package->getAliasOf(), array($package), Rule::RULE_PACKAGE_INVERSE_ALIAS, $package->getAliasOf())); - foreach ($obsoleteProviders as $provider) { - if ($provider === $package) { + // if alias package has no self.version requires, its requirements do not + // need to be added as the aliased package processing will take care of it + if (!$package->hasSelfVersionRequires()) { continue; } - - if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) { - $workQueue->enqueue($provider); - } - } - } - } - - protected function addRulesForPackage(PackageInterface $package, $ignorePlatformReqs) - { - $workQueue = new \SplQueue; - $workQueue->enqueue($package); - - while (!$workQueue->isEmpty()) { - /** @var PackageInterface $package */ - $package = $workQueue->dequeue(); - if (isset($this->addedMap[$package->id])) { - continue; - } - - $this->addedMap[$package->id] = true; - - $this->addedPackages[] = $package; - foreach ($package->getNames() as $name) { - $this->addedPackagesByNames[$name][] = $package; } foreach ($package->getRequires() as $link) { - if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($link->getTarget(), $ignorePlatformReqs, true))) && PlatformRepository::isPlatformPackage($link->getTarget())) { continue; } @@ -223,172 +199,118 @@ protected function addRulesForPackage(PackageInterface $package, $ignorePlatform $workQueue->enqueue($require); } } - - $packageName = $package->getName(); - $obsoleteProviders = $this->pool->whatProvides($packageName, null); - - foreach ($obsoleteProviders as $provider) { - if ($provider === $package) { - continue; - } - - if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) { - $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, $package)); - } elseif (!$this->obsoleteImpossibleForAlias($package, $provider)) { - $reason = ($packageName == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES; - $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $provider, $reason, $package)); - } - } } } protected function addConflictRules($ignorePlatformReqs = false) { - /** @var PackageInterface $package */ - foreach ($this->addedPackages as $package) { + /** @var BasePackage $package */ + foreach ($this->addedMap as $package) { foreach ($package->getConflicts() as $link) { + // even if conlict ends up being with an alias, there would be at least one actual package by this name if (!isset($this->addedPackagesByNames[$link->getTarget()])) { continue; } - if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($link->getTarget(), $ignorePlatformReqs, true))) && PlatformRepository::isPlatformPackage($link->getTarget())) { continue; } - /** @var PackageInterface $possibleConflict */ - foreach ($this->addedPackagesByNames[$link->getTarget()] as $possibleConflict) { - $conflictMatch = $this->pool->match($possibleConflict, $link->getTarget(), $link->getConstraint(), true); + $conflicts = $this->pool->whatProvides($link->getTarget(), $link->getConstraint()); - if ($conflictMatch === Pool::MATCH || $conflictMatch === Pool::MATCH_REPLACE) { - $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $possibleConflict, Rule::RULE_PACKAGE_CONFLICT, $link)); + foreach ($conflicts as $conflict) { + // define the conflict rule for regular packages, for alias packages it's only needed if the name + // matches the conflict exactly, otherwise the name match is by provide/replace which means the + // package which this is an alias of will conflict anyway, so no need to create additional rules + if (!$conflict instanceof AliasPackage || $conflict->getName() === $link->getTarget()) { + $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $conflict, Rule::RULE_PACKAGE_CONFLICT, $link)); } - } } + } - // check obsoletes and implicit obsoletes of a package - $isInstalled = isset($this->installedMap[$package->id]); + foreach ($this->addedPackagesByNames as $name => $packages) { + if (\count($packages) > 1) { + $reason = Rule::RULE_PACKAGE_SAME_NAME; + $this->addRule(RuleSet::TYPE_PACKAGE, $this->createMultiConflictRule($packages, $reason, $name)); + } + } + } - foreach ($package->getReplaces() as $link) { - if (!isset($this->addedPackagesByNames[$link->getTarget()])) { + protected function addRulesForRequest(Request $request, $ignorePlatformReqs) + { + foreach ($request->getFixedPackages() as $package) { + if ($package->id == -1) { + // fixed package was not added to the pool as it did not pass the stability requirements, this is fine + if ($this->pool->isUnacceptableFixedOrLockedPackage($package)) { continue; } - /** @var PackageInterface $possibleConflict */ - foreach ($this->addedPackagesByNames[$link->getTarget()] as $provider) { - if ($provider === $package) { - continue; - } - - if (!$this->obsoleteImpossibleForAlias($package, $provider)) { - $reason = $isInstalled ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES; - $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $provider, $reason, $link)); - } - } + // otherwise, looks like a bug + throw new \LogicException("Fixed package ".$package->getPrettyString()." was not added to solver pool."); } - } - } - protected function obsoleteImpossibleForAlias($package, $provider) - { - $packageIsAlias = $package instanceof AliasPackage; - $providerIsAlias = $provider instanceof AliasPackage; + $this->addRulesForPackage($package, $ignorePlatformReqs); - $impossible = ( - ($packageIsAlias && $package->getAliasOf() === $provider) || - ($providerIsAlias && $provider->getAliasOf() === $package) || - ($packageIsAlias && $providerIsAlias && $provider->getAliasOf() === $package->getAliasOf()) - ); + $rule = $this->createInstallOneOfRule(array($package), Rule::RULE_FIXED, array( + 'package' => $package, + )); + $this->addRule(RuleSet::TYPE_REQUEST, $rule); + } - return $impossible; - } + foreach ($request->getRequires() as $packageName => $constraint) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($packageName, $ignorePlatformReqs, true))) && PlatformRepository::isPlatformPackage($packageName)) { + continue; + } - protected function allowListFromJobs() - { - // call original method for BC - $this->whitelistFromJobs(); - } + $packages = $this->pool->whatProvides($packageName, $constraint); + if ($packages) { + foreach ($packages as $package) { + $this->addRulesForPackage($package, $ignorePlatformReqs); + } - /** - * @deprecated use allowListFromJobs instead - */ - protected function whitelistFromJobs() - { - foreach ($this->jobs as $job) { - switch ($job['cmd']) { - case 'install': - $packages = $this->pool->whatProvides($job['packageName'], $job['constraint'], true); - foreach ($packages as $package) { - $this->allowListFromPackage($package); - } - break; + $rule = $this->createInstallOneOfRule($packages, Rule::RULE_ROOT_REQUIRE, array( + 'packageName' => $packageName, + 'constraint' => $constraint, + )); + $this->addRule(RuleSet::TYPE_REQUEST, $rule); } } } - protected function addRulesForJobs($ignorePlatformReqs) + protected function addRulesForRootAliases($ignorePlatformReqs) { - foreach ($this->jobs as $job) { - switch ($job['cmd']) { - case 'install': - if (!$job['fixed'] && $ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $job['packageName'])) { - break; - } - - $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']); - if ($packages) { - foreach ($packages as $package) { - if (!isset($this->installedMap[$package->id])) { - $this->addRulesForPackage($package, $ignorePlatformReqs); - } - } - - $rule = $this->createInstallOneOfRule($packages, Rule::RULE_JOB_INSTALL, $job); - $this->addRule(RuleSet::TYPE_JOB, $rule); - } - break; - case 'remove': - // remove all packages with this name including uninstalled - // ones to make sure none of them are picked as replacements - $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']); - foreach ($packages as $package) { - $rule = $this->createRemoveRule($package, Rule::RULE_JOB_REMOVE, $job); - $this->addRule(RuleSet::TYPE_JOB, $rule); - } - break; + foreach ($this->pool->getPackages() as $package) { + // ensure that rules for root alias packages and aliases of packages which were loaded are also loaded + // even if the alias itself isn't required, otherwise a package could be installed without its alias which + // leads to unexpected behavior + if (!isset($this->addedMap[$package->id]) && + $package instanceof AliasPackage && + ($package->isRootPackageAlias() || isset($this->addedMap[$package->getAliasOf()->id])) + ) { + $this->addRulesForPackage($package, $ignorePlatformReqs); } } } - public function getRulesFor($jobs, $installedMap, $ignorePlatformReqs = false) + /** + * @param bool|array $ignorePlatformReqs + */ + public function getRulesFor(Request $request, $ignorePlatformReqs = false) { - $this->jobs = $jobs; - $this->rules = new RuleSet; - $this->installedMap = $installedMap; - - $this->allowListedMap = array(); - foreach ($this->installedMap as $package) { - $this->allowListFromPackage($package); - } - $this->allowListFromJobs(); - - $this->pool->setAllowList($this->allowListedMap); + $this->addRulesForRequest($request, $ignorePlatformReqs); - $this->addedMap = array(); - $this->conflictAddedMap = array(); - $this->addedPackages = array(); - $this->addedPackagesByNames = array(); - foreach ($this->installedMap as $package) { - $this->addRulesForPackage($package, $ignorePlatformReqs); - } - - $this->addRulesForJobs($ignorePlatformReqs); + $this->addRulesForRootAliases($ignorePlatformReqs); $this->addConflictRules($ignorePlatformReqs); // Remove references to packages - $this->addedPackages = $this->addedPackagesByNames = null; + $this->addedMap = $this->addedPackagesByNames = array(); + + $rules = $this->rules; + + $this->rules = new RuleSet; - return $this->rules; + return $rules; } } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleSetIterator.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleSetIterator.php index 8c048624f..2726253d2 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleSetIterator.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleSetIterator.php @@ -14,16 +14,25 @@ /** * @author Nils Adermann + * @implements \Iterator */ class RuleSetIterator implements \Iterator { + /** @var array */ protected $rules; + /** @var array */ protected $types; + /** @var int */ protected $currentOffset; + /** @var RuleSet::TYPE_*|-1 */ protected $currentType; + /** @var int */ protected $currentTypeOffset; + /** + * @param array $rules + */ public function __construct(array $rules) { $this->rules = $rules; @@ -33,16 +42,19 @@ public function __construct(array $rules) $this->rewind(); } + #[\ReturnTypeWillChange] public function current() { return $this->rules[$this->currentType][$this->currentOffset]; } + #[\ReturnTypeWillChange] public function key() { return $this->currentType; } + #[\ReturnTypeWillChange] public function next() { $this->currentOffset++; @@ -51,7 +63,7 @@ public function next() return; } - if ($this->currentOffset >= count($this->rules[$this->currentType])) { + if ($this->currentOffset >= \count($this->rules[$this->currentType])) { $this->currentOffset = 0; do { @@ -63,10 +75,11 @@ public function next() } $this->currentType = $this->types[$this->currentTypeOffset]; - } while (isset($this->types[$this->currentTypeOffset]) && !count($this->rules[$this->currentType])); + } while (isset($this->types[$this->currentTypeOffset]) && !\count($this->rules[$this->currentType])); } } + #[\ReturnTypeWillChange] public function rewind() { $this->currentOffset = 0; @@ -83,12 +96,12 @@ public function rewind() } $this->currentType = $this->types[$this->currentTypeOffset]; - } while (isset($this->types[$this->currentTypeOffset]) && !count($this->rules[$this->currentType])); + } while (isset($this->types[$this->currentTypeOffset]) && !\count($this->rules[$this->currentType])); } + #[\ReturnTypeWillChange] public function valid() { - return isset($this->rules[$this->currentType]) - && isset($this->rules[$this->currentType][$this->currentOffset]); + return isset($this->rules[$this->currentType], $this->rules[$this->currentType][$this->currentOffset]); } } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleWatchChain.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleWatchChain.php index 2fea0d6ee..52795ea89 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleWatchChain.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleWatchChain.php @@ -19,11 +19,10 @@ * method to set the internal iterator to a particular offset. * * @author Nils Adermann + * @extends \SplDoublyLinkedList */ class RuleWatchChain extends \SplDoublyLinkedList { - protected $offset = 0; - /** * Moves the internal iterator to the specified offset * diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleWatchGraph.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleWatchGraph.php index 31a22414d..76b6abb55 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleWatchGraph.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleWatchGraph.php @@ -24,6 +24,7 @@ */ class RuleWatchGraph { + /** @var array */ protected $watchChains = array(); /** @@ -44,12 +45,22 @@ public function insert(RuleWatchNode $node) return; } - foreach (array($node->watch1, $node->watch2) as $literal) { - if (!isset($this->watchChains[$literal])) { - $this->watchChains[$literal] = new RuleWatchChain; + if (!$node->getRule() instanceof MultiConflictRule) { + foreach (array($node->watch1, $node->watch2) as $literal) { + if (!isset($this->watchChains[$literal])) { + $this->watchChains[$literal] = new RuleWatchChain; + } + + $this->watchChains[$literal]->unshift($node); } + } else { + foreach ($node->getRule()->getLiterals() as $literal) { + if (!isset($this->watchChains[$literal])) { + $this->watchChains[$literal] = new RuleWatchChain; + } - $this->watchChains[$literal]->unshift($node); + $this->watchChains[$literal]->unshift($node); + } } } @@ -92,28 +103,40 @@ public function propagateLiteral($decidedLiteral, $level, $decisions) $chain->rewind(); while ($chain->valid()) { $node = $chain->current(); - $otherWatch = $node->getOtherWatch($literal); + if (!$node->getRule() instanceof MultiConflictRule) { + $otherWatch = $node->getOtherWatch($literal); - if (!$node->getRule()->isDisabled() && !$decisions->satisfy($otherWatch)) { - $ruleLiterals = $node->getRule()->getLiterals(); + if (!$node->getRule()->isDisabled() && !$decisions->satisfy($otherWatch)) { + $ruleLiterals = $node->getRule()->getLiterals(); - $alternativeLiterals = array_filter($ruleLiterals, function ($ruleLiteral) use ($literal, $otherWatch, $decisions) { - return $literal !== $ruleLiteral && - $otherWatch !== $ruleLiteral && - !$decisions->conflict($ruleLiteral); - }); + $alternativeLiterals = array_filter($ruleLiterals, function ($ruleLiteral) use ($literal, $otherWatch, $decisions) { + return $literal !== $ruleLiteral && + $otherWatch !== $ruleLiteral && + !$decisions->conflict($ruleLiteral); + }); - if ($alternativeLiterals) { - reset($alternativeLiterals); - $this->moveWatch($literal, current($alternativeLiterals), $node); - continue; - } + if ($alternativeLiterals) { + reset($alternativeLiterals); + $this->moveWatch($literal, current($alternativeLiterals), $node); + continue; + } - if ($decisions->conflict($otherWatch)) { - return $node->getRule(); - } + if ($decisions->conflict($otherWatch)) { + return $node->getRule(); + } - $decisions->decide($otherWatch, $level, $node->getRule()); + $decisions->decide($otherWatch, $level, $node->getRule()); + } + } else { + foreach ($node->getRule()->getLiterals() as $otherLiteral) { + if ($literal !== $otherLiteral && !$decisions->satisfy($otherLiteral)) { + if ($decisions->conflict($otherLiteral)) { + return $node->getRule(); + } + + $decisions->decide($otherLiteral, $level, $node->getRule()); + } + } } $chain->next(); @@ -128,10 +151,10 @@ public function propagateLiteral($decidedLiteral, $level, $decisions) * The rule node's watched literals are updated accordingly. * * @param int $fromLiteral A literal the node used to watch - * @param int $toLiteral A literal the node should watch now - * @param RuleWatchNode $node The rule node to be moved + * @param int $toLiteral A literal the node should watch now + * @param RuleWatchNode $node The rule node to be moved */ - protected function moveWatch($fromLiteral, $toLiteral, $node) + protected function moveWatch($fromLiteral, $toLiteral, RuleWatchNode $node) { if (!isset($this->watchChains[$toLiteral])) { $this->watchChains[$toLiteral] = new RuleWatchChain; diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleWatchNode.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleWatchNode.php index eeaa54162..24fccd8d3 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleWatchNode.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/RuleWatchNode.php @@ -21,9 +21,12 @@ */ class RuleWatchNode { + /** @var int */ public $watch1; + /** @var int */ public $watch2; + /** @var Rule */ protected $rule; /** @@ -31,13 +34,13 @@ class RuleWatchNode * * @param Rule $rule The rule to wrap */ - public function __construct($rule) + public function __construct(Rule $rule) { $this->rule = $rule; $literals = $rule->getLiterals(); - $literalCount = count($literals); + $literalCount = \count($literals); $this->watch1 = $literalCount > 0 ? $literals[0] : 0; $this->watch2 = $literalCount > 1 ? $literals[1] : 0; } @@ -55,7 +58,7 @@ public function watch2OnHighest(Decisions $decisions) $literals = $this->rule->getLiterals(); // if there are only 2 elements, both are being watched anyway - if (count($literals) < 3) { + if (\count($literals) < 3 || $this->rule instanceof MultiConflictRule) { return; } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Solver.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Solver.php index a046a7a92..7454ea74e 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Solver.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Solver.php @@ -13,7 +13,7 @@ namespace Composer\DependencyResolver; use Composer\IO\IOInterface; -use Composer\Repository\RepositoryInterface; +use Composer\Package\PackageInterface; use Composer\Repository\PlatformRepository; /** @@ -28,23 +28,16 @@ class Solver protected $policy; /** @var Pool */ protected $pool; - /** @var RepositoryInterface */ - protected $installed; + /** @var RuleSet */ protected $rules; - /** @var RuleSetGenerator */ - protected $ruleSetGenerator; - /** @var array */ - protected $jobs; - /** @var int[] */ - protected $updateMap = array(); /** @var RuleWatchGraph */ protected $watchGraph; /** @var Decisions */ protected $decisions; - /** @var int[] */ - protected $installedMap; + /** @var PackageInterface[] */ + protected $fixedMap; /** @var int */ protected $propagateIndex; @@ -52,9 +45,9 @@ class Solver protected $branches = array(); /** @var Problem[] */ protected $problems = array(); - /** @var array */ + /** @var array */ protected $learnedPool = array(); - /** @var array */ + /** @var array */ protected $learnedWhy = array(); /** @var bool */ @@ -64,18 +57,15 @@ class Solver protected $io; /** - * @param PolicyInterface $policy - * @param Pool $pool - * @param RepositoryInterface $installed - * @param IOInterface $io + * @param PolicyInterface $policy + * @param Pool $pool + * @param IOInterface $io */ - public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed, IOInterface $io) + public function __construct(PolicyInterface $policy, Pool $pool, IOInterface $io) { $this->io = $io; $this->policy = $policy; $this->pool = $pool; - $this->installed = $installed; - $this->ruleSetGenerator = new RuleSetGenerator($policy, $pool); } /** @@ -83,16 +73,21 @@ public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInter */ public function getRuleSetSize() { - return count($this->rules); + return \count($this->rules); + } + + public function getPool() + { + return $this->pool; } // aka solver_makeruledecisions private function makeAssertionRuleDecisions() { - $decisionStart = count($this->decisions) - 1; + $decisionStart = \count($this->decisions) - 1; - $rulesCount = count($this->rules); + $rulesCount = \count($this->rules); for ($ruleIndex = 0; $ruleIndex < $rulesCount; $ruleIndex++) { $rule = $this->rules->ruleById[$ruleIndex]; @@ -121,23 +116,23 @@ private function makeAssertionRuleDecisions() $conflict = $this->decisions->decisionRule($literal); if ($conflict && RuleSet::TYPE_PACKAGE === $conflict->getType()) { - $problem = new Problem($this->pool); + $problem = new Problem(); $problem->addRule($rule); $problem->addRule($conflict); - $this->disableProblem($rule); + $rule->disable(); $this->problems[] = $problem; continue; } - // conflict with another job - $problem = new Problem($this->pool); + // conflict with another root require/fixed package + $problem = new Problem(); $problem->addRule($rule); $problem->addRule($conflict); - // push all of our rules (can only be job rules) + // push all of our rules (can only be root require/fixed package rules) // asserting this literal on the problem stack - foreach ($this->rules->getIteratorFor(RuleSet::TYPE_JOB) as $assertRule) { + foreach ($this->rules->getIteratorFor(RuleSet::TYPE_REQUEST) as $assertRule) { if ($assertRule->isDisabled() || !$assertRule->isAssertion()) { continue; } @@ -148,9 +143,8 @@ private function makeAssertionRuleDecisions() if (abs($literal) !== abs($assertRuleLiteral)) { continue; } - $problem->addRule($assertRule); - $this->disableProblem($assertRule); + $assertRule->disable(); } $this->problems[] = $problem; @@ -159,63 +153,47 @@ private function makeAssertionRuleDecisions() } } - protected function setupInstalledMap() + protected function setupFixedMap(Request $request) { - $this->installedMap = array(); - foreach ($this->installed->getPackages() as $package) { - $this->installedMap[$package->id] = $package; + $this->fixedMap = array(); + foreach ($request->getFixedPackages() as $package) { + $this->fixedMap[$package->id] = $package; } } /** - * @param bool $ignorePlatformReqs + * @param Request $request + * @param bool|array $ignorePlatformReqs */ - protected function checkForRootRequireProblems($ignorePlatformReqs) + protected function checkForRootRequireProblems(Request $request, $ignorePlatformReqs) { - foreach ($this->jobs as $job) { - switch ($job['cmd']) { - case 'update': - $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']); - foreach ($packages as $package) { - if (isset($this->installedMap[$package->id])) { - $this->updateMap[$package->id] = true; - } - } - break; - - case 'update-all': - foreach ($this->installedMap as $package) { - $this->updateMap[$package->id] = true; - } - break; - - case 'install': - if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $job['packageName'])) { - break; - } + foreach ($request->getRequires() as $packageName => $constraint) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($packageName, $ignorePlatformReqs, true))) && PlatformRepository::isPlatformPackage($packageName)) { + continue; + } - if (!$this->pool->whatProvides($job['packageName'], $job['constraint'])) { - $problem = new Problem($this->pool); - $problem->addRule(new GenericRule(array(), null, null, $job)); - $this->problems[] = $problem; - } - break; + if (!$this->pool->whatProvides($packageName, $constraint)) { + $problem = new Problem(); + $problem->addRule(new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, array('packageName' => $packageName, 'constraint' => $constraint))); + $this->problems[] = $problem; } } } /** - * @param Request $request - * @param bool $ignorePlatformReqs - * @return array + * @param Request $request + * @param bool|array $ignorePlatformReqs + * @return LockTransaction */ public function solve(Request $request, $ignorePlatformReqs = false) { - $this->jobs = $request->getJobs(); + $this->setupFixedMap($request); - $this->setupInstalledMap(); - $this->rules = $this->ruleSetGenerator->getRulesFor($this->jobs, $this->installedMap, $ignorePlatformReqs); - $this->checkForRootRequireProblems($ignorePlatformReqs); + $this->io->writeError('Generating rules', true, IOInterface::DEBUG); + $ruleSetGenerator = new RuleSetGenerator($this->policy, $this->pool); + $this->rules = $ruleSetGenerator->getRulesFor($request, $ignorePlatformReqs); + unset($ruleSetGenerator); + $this->checkForRootRequireProblems($request, $ignorePlatformReqs); $this->decisions = new Decisions($this->pool); $this->watchGraph = new RuleWatchGraph; @@ -223,29 +201,20 @@ public function solve(Request $request, $ignorePlatformReqs = false) $this->watchGraph->insert(new RuleWatchNode($rule)); } - /* make decisions based on job/update assertions */ + /* make decisions based on root require/fix assertions */ $this->makeAssertionRuleDecisions(); $this->io->writeError('Resolving dependencies through SAT', true, IOInterface::DEBUG); $before = microtime(true); - $this->runSat(true); + $this->runSat(); $this->io->writeError('', true, IOInterface::DEBUG); $this->io->writeError(sprintf('Dependency resolution completed in %.3f seconds', microtime(true) - $before), true, IOInterface::VERBOSE); - // decide to remove everything that's installed and undecided - foreach ($this->installedMap as $packageId => $void) { - if ($this->decisions->undecided($packageId)) { - $this->decisions->decide(-$packageId, 1, null); - } - } - if ($this->problems) { - throw new SolverProblemsException($this->problems, $this->installedMap); + throw new SolverProblemsException($this->problems, $this->learnedPool); } - $transaction = new Transaction($this->policy, $this->pool, $this->installedMap, $this->decisions); - - return $transaction->getOperations(); + return new LockTransaction($this->pool, $request->getPresentMap(), $request->getFixedPackagesMap(), $this->decisions); } /** @@ -299,10 +268,10 @@ private function revert($level) } $this->decisions->revertLast(); - $this->propagateIndex = count($this->decisions); + $this->propagateIndex = \count($this->decisions); } - while (!empty($this->branches) && $this->branches[count($this->branches) - 1][self::BRANCH_LEVEL] >= $level) { + while (!empty($this->branches) && $this->branches[\count($this->branches) - 1][self::BRANCH_LEVEL] >= $level) { array_pop($this->branches); } } @@ -322,11 +291,10 @@ private function revert($level) * * @param int $level * @param string|int $literal - * @param bool $disableRules * @param Rule $rule * @return int */ - private function setPropagateLearn($level, $literal, $disableRules, Rule $rule) + private function setPropagateLearn($level, $literal, Rule $rule) { $level++; @@ -340,7 +308,7 @@ private function setPropagateLearn($level, $literal, $disableRules, Rule $rule) } if ($level == 1) { - return $this->analyzeUnsolvable($rule, $disableRules); + return $this->analyzeUnsolvable($rule); } // conflict @@ -350,7 +318,8 @@ private function setPropagateLearn($level, $literal, $disableRules, Rule $rule) throw new SolverBugException( "Trying to revert to invalid level ".(int) $newLevel." from level ".(int) $level."." ); - } elseif (!$newRule) { + } + if (!$newRule) { throw new SolverBugException( "No rule was learned from analyzing $rule at level $level." ); @@ -377,23 +346,22 @@ private function setPropagateLearn($level, $literal, $disableRules, Rule $rule) /** * @param int $level * @param array $decisionQueue - * @param bool $disableRules * @param Rule $rule * @return int */ - private function selectAndInstall($level, array $decisionQueue, $disableRules, Rule $rule) + private function selectAndInstall($level, array $decisionQueue, Rule $rule) { // choose best package to install from decisionQueue - $literals = $this->policy->selectPreferredPackages($this->pool, $this->installedMap, $decisionQueue, $rule->getRequiredPackage()); + $literals = $this->policy->selectPreferredPackages($this->pool, $decisionQueue, $rule->getRequiredPackage()); $selectedLiteral = array_shift($literals); // if there are multiple candidates, then branch - if (count($literals)) { + if (\count($literals)) { $this->branches[] = array($literals, $level); } - return $this->setPropagateLearn($level, $selectedLiteral, $disableRules, $rule); + return $this->setPropagateLearn($level, $selectedLiteral, $rule); } /** @@ -410,14 +378,19 @@ protected function analyze($level, Rule $rule) $seen = array(); $learnedLiterals = array(null); - $decisionId = count($this->decisions); + $decisionId = \count($this->decisions); $this->learnedPool[] = array(); while (true) { - $this->learnedPool[count($this->learnedPool) - 1][] = $rule; + $this->learnedPool[\count($this->learnedPool) - 1][] = $rule; foreach ($rule->getLiterals() as $literal) { + // multiconflictrule is really a bunch of rules in one, so some may not have finished propagating yet + if ($rule instanceof MultiConflictRule && !$this->decisions->decided($literal)) { + continue; + } + // skip the one true literal if ($this->decisions->satisfy($literal)) { continue; @@ -443,6 +416,7 @@ protected function analyze($level, Rule $rule) } } } + unset($literal); $l1retry = true; while ($l1retry) { @@ -490,6 +464,35 @@ protected function analyze($level, Rule $rule) // only level 1 marks left $l1num++; $l1retry = true; + } else { + $decision = $this->decisions->atOffset($decisionId); + $rule = $decision[Decisions::DECISION_REASON]; + + if ($rule instanceof MultiConflictRule) { + // there is only ever exactly one positive decision in a multiconflict rule + foreach ($rule->getLiterals() as $literal) { + if (!isset($seen[abs($literal)]) && $this->decisions->satisfy(-$literal)) { + $this->learnedPool[\count($this->learnedPool) - 1][] = $rule; + $l = $this->decisions->decisionLevel($literal); + if (1 === $l) { + $l1num++; + } elseif ($level === $l) { + $num++; + } else { + // not level1 or conflict level, add to new rule + $learnedLiterals[] = $literal; + + if ($l > $ruleLevel) { + $ruleLevel = $l; + } + } + $seen[abs($literal)] = true; + break; + } + } + + $l1retry = true; + } } } @@ -497,7 +500,7 @@ protected function analyze($level, Rule $rule) $rule = $decision[Decisions::DECISION_REASON]; } - $why = count($this->learnedPool) - 1; + $why = \count($this->learnedPool) - 1; if (!$learnedLiterals[0]) { throw new SolverBugException( @@ -510,19 +513,19 @@ protected function analyze($level, Rule $rule) return array($learnedLiterals[0], $ruleLevel, $newRule, $why); } - /** - * @param Problem $problem - * @param Rule $conflictRule - */ - private function analyzeUnsolvableRule(Problem $problem, Rule $conflictRule) + private function analyzeUnsolvableRule(Problem $problem, Rule $conflictRule, array &$ruleSeen) { + $why = spl_object_hash($conflictRule); + $ruleSeen[$why] = true; + if ($conflictRule->getType() == RuleSet::TYPE_LEARNED) { - $why = spl_object_hash($conflictRule); $learnedWhy = $this->learnedWhy[$why]; $problemRules = $this->learnedPool[$learnedWhy]; foreach ($problemRules as $problemRule) { - $this->analyzeUnsolvableRule($problem, $problemRule); + if (!isset($ruleSeen[spl_object_hash($problemRule)])) { + $this->analyzeUnsolvableRule($problem, $problemRule, $ruleSeen); + } } return; @@ -539,15 +542,16 @@ private function analyzeUnsolvableRule(Problem $problem, Rule $conflictRule) /** * @param Rule $conflictRule - * @param bool $disableRules * @return int */ - private function analyzeUnsolvable(Rule $conflictRule, $disableRules) + private function analyzeUnsolvable(Rule $conflictRule) { - $problem = new Problem($this->pool); + $problem = new Problem(); $problem->addRule($conflictRule); - $this->analyzeUnsolvableRule($problem, $conflictRule); + $ruleSeen = array(); + + $this->analyzeUnsolvableRule($problem, $conflictRule, $ruleSeen); $this->problems[] = $problem; @@ -573,7 +577,7 @@ private function analyzeUnsolvable(Rule $conflictRule, $disableRules) $why = $decision[Decisions::DECISION_REASON]; $problem->addRule($why); - $this->analyzeUnsolvableRule($problem, $why); + $this->analyzeUnsolvableRule($problem, $why, $ruleSeen); $literals = $why->getLiterals(); @@ -586,52 +590,9 @@ private function analyzeUnsolvable(Rule $conflictRule, $disableRules) } } - if ($disableRules) { - foreach ($this->problems[count($this->problems) - 1] as $reason) { - $this->disableProblem($reason['rule']); - } - - $this->resetSolver(); - - return 1; - } - return 0; } - /** - * @param Rule $why - */ - private function disableProblem(Rule $why) - { - $job = $why->getJob(); - - if (!$job) { - $why->disable(); - - return; - } - - // disable all rules of this job - foreach ($this->rules as $rule) { - /** @var Rule $rule */ - if ($job === $rule->getJob()) { - $rule->disable(); - } - } - } - - private function resetSolver() - { - $this->decisions->reset(); - - $this->propagateIndex = 0; - $this->branches = array(); - - $this->enableDisableLearnedRules(); - $this->makeAssertionRuleDecisions(); - } - /** * enable/disable learnt rules * @@ -661,29 +622,20 @@ private function enableDisableLearnedRules() } } - /** - * @param bool $disableRules - */ - private function runSat($disableRules = true) + private function runSat() { $this->propagateIndex = 0; /* * here's the main loop: * 1) propagate new decisions (only needed once) - * 2) fulfill jobs + * 2) fulfill root requires/fixed packages * 3) fulfill all unresolved rules * 4) minimalize solution if we had choices * if we encounter a problem, we rewind to a safe level and restart * with step 1 */ - $decisionQueue = array(); - /** - * @todo this makes $disableRules always false; determine the rationale and possibly remove dead code? - */ - $disableRules = array(); - $level = 1; $systemLevel = $level + 1; @@ -691,7 +643,7 @@ private function runSat($disableRules = true) if (1 === $level) { $conflictRule = $this->propagate($level); if (null !== $conflictRule) { - if ($this->analyzeUnsolvable($conflictRule, $disableRules)) { + if ($this->analyzeUnsolvable($conflictRule)) { continue; } @@ -699,9 +651,9 @@ private function runSat($disableRules = true) } } - // handle job rules + // handle root require/fixed package rules if ($level < $systemLevel) { - $iterator = $this->rules->getIteratorFor(RuleSet::TYPE_JOB); + $iterator = $this->rules->getIteratorFor(RuleSet::TYPE_REQUEST); foreach ($iterator as $rule) { if ($rule->isEnabled()) { $decisionQueue = array(); @@ -717,27 +669,22 @@ private function runSat($disableRules = true) } } - if ($noneSatisfied && count($decisionQueue)) { - // prune all update packages until installed version - // except for requested updates - if (count($this->installed) != count($this->updateMap)) { - $prunedQueue = array(); - foreach ($decisionQueue as $literal) { - if (isset($this->installedMap[abs($literal)])) { - $prunedQueue[] = $literal; - if (isset($this->updateMap[abs($literal)])) { - $prunedQueue = $decisionQueue; - break; - } - } + if ($noneSatisfied && \count($decisionQueue)) { + // if any of the options in the decision queue are fixed, only use those + $prunedQueue = array(); + foreach ($decisionQueue as $literal) { + if (isset($this->fixedMap[abs($literal)])) { + $prunedQueue[] = $literal; } + } + if (!empty($prunedQueue)) { $decisionQueue = $prunedQueue; } } - if ($noneSatisfied && count($decisionQueue)) { + if ($noneSatisfied && \count($decisionQueue)) { $oLevel = $level; - $level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule); + $level = $this->selectAndInstall($level, $decisionQueue, $rule); if (0 === $level) { return; @@ -751,7 +698,7 @@ private function runSat($disableRules = true) $systemLevel = $level + 1; - // jobs left + // root requires/fixed packages left $iterator->next(); if ($iterator->valid()) { continue; @@ -762,7 +709,7 @@ private function runSat($disableRules = true) $systemLevel = $level; } - $rulesCount = count($this->rules); + $rulesCount = \count($this->rules); $pass = 1; $this->io->writeError('Looking at all rules.', true, IOInterface::DEBUG); @@ -809,18 +756,18 @@ private function runSat($disableRules = true) } // need to have at least 2 item to pick from - if (count($decisionQueue) < 2) { + if (\count($decisionQueue) < 2) { continue; } - $level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule); + $level = $this->selectAndInstall($level, $decisionQueue, $rule); if (0 === $level) { return; } // something changed, so look at all rules again - $rulesCount = count($this->rules); + $rulesCount = \count($this->rules); $n = -1; } @@ -829,13 +776,13 @@ private function runSat($disableRules = true) } // minimization step - if (count($this->branches)) { + if (\count($this->branches)) { $lastLiteral = null; $lastLevel = null; $lastBranchIndex = 0; $lastBranchOffset = 0; - for ($i = count($this->branches) - 1; $i >= 0; $i--) { + for ($i = \count($this->branches) - 1; $i >= 0; $i--) { list($literals, $l) = $this->branches[$i]; foreach ($literals as $offset => $literal) { @@ -856,7 +803,7 @@ private function runSat($disableRules = true) $why = $this->decisions->lastReason(); - $level = $this->setPropagateLearn($level, $lastLiteral, $disableRules, $why); + $level = $this->setPropagateLearn($level, $lastLiteral, $why); if ($level == 0) { return; diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/SolverProblemsException.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/SolverProblemsException.php index 142895697..93eab3e63 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/SolverProblemsException.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/SolverProblemsException.php @@ -13,41 +13,79 @@ namespace Composer\DependencyResolver; use Composer\Util\IniHelper; +use Composer\Repository\RepositorySet; +use Composer\Package\PackageInterface; /** * @author Nils Adermann */ class SolverProblemsException extends \RuntimeException { + /** @var Problem[] */ protected $problems; - protected $installedMap; - - public function __construct(array $problems, array $installedMap) + /** @var array */ + protected $learnedPool; + + /** + * @param Problem[] $problems + * @param array $learnedPool + */ + public function __construct(array $problems, array $learnedPool) { $this->problems = $problems; - $this->installedMap = $installedMap; + $this->learnedPool = $learnedPool; - parent::__construct($this->createMessage(), 2); + parent::__construct('Failed resolving dependencies with '.count($problems).' problems, call getPrettyString to get formatted details', 2); } - protected function createMessage() + public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, $isDevExtraction = false) { - $text = "\n"; + $installedMap = $request->getPresentMap(true); $hasExtensionProblems = false; - foreach ($this->problems as $i => $problem) { - $text .= " Problem ".($i + 1).$problem->getPrettyString($this->installedMap)."\n"; + $isCausedByLock = false; + + $problems = array(); + foreach ($this->problems as $problem) { + $problems[] = $problem->getPrettyString($repositorySet, $request, $pool, $isVerbose, $installedMap, $this->learnedPool)."\n"; if (!$hasExtensionProblems && $this->hasExtensionProblems($problem->getReasons())) { $hasExtensionProblems = true; } + + $isCausedByLock |= $problem->isCausedByLock($repositorySet, $request, $pool); } - if (strpos($text, 'could not be found') || strpos($text, 'no matching package found')) { - $text .= "\nPotential causes:\n - A typo in the package name\n - The package is not available in a stable-enough version according to your minimum-stability setting\n see for more details.\n - It's a private package and you forgot to add a custom repository to find it\n\nRead for further common problems."; + $i = 1; + $text = "\n"; + foreach (array_unique($problems) as $problem) { + $text .= " Problem ".($i++).$problem; + } + + $hints = array(); + if (!$isDevExtraction && (strpos($text, 'could not be found') || strpos($text, 'no matching package found'))) { + $hints[] = "Potential causes:\n - A typo in the package name\n - The package is not available in a stable-enough version according to your minimum-stability setting\n see for more details.\n - It's a private package and you forgot to add a custom repository to find it\n\nRead for further common problems."; } if ($hasExtensionProblems) { - $text .= $this->createExtensionHint(); + $hints[] = $this->createExtensionHint(); + } + + if ($isCausedByLock && !$isDevExtraction && !$request->getUpdateAllowTransitiveRootDependencies()) { + $hints[] = "Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions."; + } + + if (strpos($text, 'found composer-plugin-api[2.0.0] but it does not match') && strpos($text, '- ocramius/package-versions')) { + $hints[] = "ocramius/package-versions only provides support for Composer 2 in 1.8+, which requires PHP 7.4.\nIf you can not upgrade PHP you can require composer/package-versions-deprecated to resolve this with PHP 7.0+."; + } + + if (!class_exists('PHPUnit\Framework\TestCase', false)) { + if (strpos($text, 'found composer-plugin-api[2.0.0] but it does not match')) { + $hints[] = "You are using Composer 2, which some of your plugins seem to be incompatible with. Make sure you update your plugins or report a plugin-issue to ask them to support Composer 2."; + } + } + + if ($hints) { + $text .= "\n" . implode("\n\n", $hints); } return $text; @@ -66,9 +104,9 @@ private function createExtensionHint() return ''; } - $text = "\n To enable extensions, verify that they are enabled in your .ini files:\n - "; + $text = "To enable extensions, verify that they are enabled in your .ini files:\n - "; $text .= implode("\n - ", $paths); - $text .= "\n You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode."; + $text .= "\nYou can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode."; return $text; } @@ -76,8 +114,9 @@ private function createExtensionHint() private function hasExtensionProblems(array $reasonSets) { foreach ($reasonSets as $reasonSet) { - foreach ($reasonSet as $reason) { - if (isset($reason["rule"]) && 0 === strpos($reason["rule"]->getRequiredPackage(), 'ext-')) { + foreach ($reasonSet as $rule) { + $required = $rule->getRequiredPackage(); + if (null !== $required && 0 === strpos($required, 'ext-')) { return true; } } diff --git a/app/vendor/composer/composer/src/Composer/DependencyResolver/Transaction.php b/app/vendor/composer/composer/src/Composer/DependencyResolver/Transaction.php index c8d3bbe53..1cda16927 100644 --- a/app/vendor/composer/composer/src/Composer/DependencyResolver/Transaction.php +++ b/app/vendor/composer/composer/src/Composer/DependencyResolver/Transaction.php @@ -13,161 +13,213 @@ namespace Composer\DependencyResolver; use Composer\Package\AliasPackage; +use Composer\Package\Link; +use Composer\Package\PackageInterface; +use Composer\Repository\PlatformRepository; +use Composer\DependencyResolver\Operation\OperationInterface; /** * @author Nils Adermann + * @internal */ class Transaction { - protected $policy; - protected $pool; - protected $installedMap; - protected $decisions; - protected $transaction; - - public function __construct($policy, $pool, $installedMap, $decisions) + /** + * @var OperationInterface[] + */ + protected $operations; + + /** + * Packages present at the beginning of the transaction + * @var PackageInterface[] + */ + protected $presentPackages; + + /** + * Package set resulting from this transaction + * @var array + */ + protected $resultPackageMap; + + /** + * @var array + */ + protected $resultPackagesByName = array(); + + public function __construct($presentPackages, $resultPackages) { - $this->policy = $policy; - $this->pool = $pool; - $this->installedMap = $installedMap; - $this->decisions = $decisions; - $this->transaction = array(); + $this->presentPackages = $presentPackages; + $this->setResultPackageMaps($resultPackages); + $this->operations = $this->calculateOperations(); } + /** @return OperationInterface[] */ public function getOperations() { - $installMeansUpdateMap = $this->findUpdates(); - - $updateMap = array(); - $installMap = array(); - $uninstallMap = array(); - - foreach ($this->decisions as $i => $decision) { - $literal = $decision[Decisions::DECISION_LITERAL]; - $reason = $decision[Decisions::DECISION_REASON]; - - $package = $this->pool->literalToPackage($literal); + return $this->operations; + } - // wanted & installed || !wanted & !installed - if (($literal > 0) == isset($this->installedMap[$package->id])) { - continue; + private function setResultPackageMaps($resultPackages) + { + $packageSort = function (PackageInterface $a, PackageInterface $b) { + // sort alias packages by the same name behind their non alias version + if ($a->getName() == $b->getName()) { + if ($a instanceof AliasPackage != $b instanceof AliasPackage) { + return $a instanceof AliasPackage ? -1 : 1; + } + // if names are the same, compare version, e.g. to sort aliases reliably, actual order does not matter + return strcmp($b->getVersion(), $a->getVersion()); } - if ($literal > 0) { - if (isset($installMeansUpdateMap[abs($literal)]) && !$package instanceof AliasPackage) { - $source = $installMeansUpdateMap[abs($literal)]; + return strcmp($b->getName(), $a->getName()); + }; - $updateMap[$package->id] = array( - 'package' => $package, - 'source' => $source, - 'reason' => $reason, - ); - - // avoid updates to one package from multiple origins - unset($installMeansUpdateMap[abs($literal)]); - $ignoreRemove[$source->id] = true; - } else { - $installMap[$package->id] = array( - 'package' => $package, - 'reason' => $reason, - ); - } + $this->resultPackageMap = array(); + foreach ($resultPackages as $package) { + $this->resultPackageMap[spl_object_hash($package)] = $package; + foreach ($package->getNames() as $name) { + $this->resultPackagesByName[$name][] = $package; } } - foreach ($this->decisions as $i => $decision) { - $literal = $decision[Decisions::DECISION_LITERAL]; - $reason = $decision[Decisions::DECISION_REASON]; - $package = $this->pool->literalToPackage($literal); - - if ($literal <= 0 && - isset($this->installedMap[$package->id]) && - !isset($ignoreRemove[$package->id])) { - $uninstallMap[$package->id] = array( - 'package' => $package, - 'reason' => $reason, - ); - } + uasort($this->resultPackageMap, $packageSort); + foreach ($this->resultPackagesByName as $name => $packages) { + uasort($this->resultPackagesByName[$name], $packageSort); } - - $this->transactionFromMaps($installMap, $updateMap, $uninstallMap); - - return $this->transaction; } - protected function transactionFromMaps($installMap, $updateMap, $uninstallMap) + protected function calculateOperations() { - $queue = array_map( - function ($operation) { - return $operation['package']; - }, - $this->findRootPackages($installMap, $updateMap) - ); + $operations = array(); + + $presentPackageMap = array(); + $removeMap = array(); + $presentAliasMap = array(); + $removeAliasMap = array(); + foreach ($this->presentPackages as $package) { + if ($package instanceof AliasPackage) { + $presentAliasMap[$package->getName().'::'.$package->getVersion()] = $package; + $removeAliasMap[$package->getName().'::'.$package->getVersion()] = $package; + } else { + $presentPackageMap[$package->getName()] = $package; + $removeMap[$package->getName()] = $package; + } + } + + $stack = $this->getRootPackages(); $visited = array(); + $processed = array(); - while (!empty($queue)) { - $package = array_pop($queue); - $packageId = $package->id; + while (!empty($stack)) { + $package = array_pop($stack); - if (!isset($visited[$packageId])) { - $queue[] = $package; + if (isset($processed[spl_object_hash($package)])) { + continue; + } + if (!isset($visited[spl_object_hash($package)])) { + $visited[spl_object_hash($package)] = true; + + $stack[] = $package; if ($package instanceof AliasPackage) { - $queue[] = $package->getAliasOf(); + $stack[] = $package->getAliasOf(); } else { foreach ($package->getRequires() as $link) { - $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint()); + $possibleRequires = $this->getProvidersInResult($link); foreach ($possibleRequires as $require) { - $queue[] = $require; + $stack[] = $require; } } } + } elseif (!isset($processed[spl_object_hash($package)])) { + $processed[spl_object_hash($package)] = true; - $visited[$package->id] = true; - } else { - if (isset($installMap[$packageId])) { - $this->install( - $installMap[$packageId]['package'], - $installMap[$packageId]['reason'] - ); - unset($installMap[$packageId]); - } - if (isset($updateMap[$packageId])) { - $this->update( - $updateMap[$packageId]['source'], - $updateMap[$packageId]['package'], - $updateMap[$packageId]['reason'] - ); - unset($updateMap[$packageId]); + if ($package instanceof AliasPackage) { + $aliasKey = $package->getName().'::'.$package->getVersion(); + if (isset($presentAliasMap[$aliasKey])) { + unset($removeAliasMap[$aliasKey]); + } else { + $operations[] = new Operation\MarkAliasInstalledOperation($package); + } + } else { + if (isset($presentPackageMap[$package->getName()])) { + $source = $presentPackageMap[$package->getName()]; + + // do we need to update? + // TODO different for lock? + if ($package->getVersion() != $presentPackageMap[$package->getName()]->getVersion() || + $package->getDistReference() !== $presentPackageMap[$package->getName()]->getDistReference() || + $package->getSourceReference() !== $presentPackageMap[$package->getName()]->getSourceReference() + ) { + $operations[] = new Operation\UpdateOperation($source, $package); + } + unset($removeMap[$package->getName()]); + } else { + $operations[] = new Operation\InstallOperation($package); + unset($removeMap[$package->getName()]); + } } } } - foreach ($uninstallMap as $uninstall) { - $this->uninstall($uninstall['package'], $uninstall['reason']); + foreach ($removeMap as $name => $package) { + array_unshift($operations, new Operation\UninstallOperation($package)); + } + foreach ($removeAliasMap as $nameVersion => $package) { + $operations[] = new Operation\MarkAliasUninstalledOperation($package); } + + $operations = $this->movePluginsToFront($operations); + // TODO fix this: + // we have to do this again here even though the above stack code did it because moving plugins moves them before uninstalls + $operations = $this->moveUninstallsToFront($operations); + + // TODO skip updates which don't update? is this needed? we shouldn't schedule this update in the first place? + /* + if ('update' === $opType) { + $targetPackage = $operation->getTargetPackage(); + if ($targetPackage->isDev()) { + $initialPackage = $operation->getInitialPackage(); + if ($targetPackage->getVersion() === $initialPackage->getVersion() + && (!$targetPackage->getSourceReference() || $targetPackage->getSourceReference() === $initialPackage->getSourceReference()) + && (!$targetPackage->getDistReference() || $targetPackage->getDistReference() === $initialPackage->getDistReference()) + ) { + $this->io->writeError(' - Skipping update of ' . $targetPackage->getPrettyName() . ' to the same reference-locked version', true, IOInterface::DEBUG); + $this->io->writeError('', true, IOInterface::DEBUG); + + continue; + } + } + }*/ + + return $this->operations = $operations; } - protected function findRootPackages($installMap, $updateMap) + /** + * Determine which packages in the result are not required by any other packages in it. + * + * These serve as a starting point to enumerate packages in a topological order despite potential cycles. + * If there are packages with a cycle on the top level the package with the lowest name gets picked + * + * @return array + */ + protected function getRootPackages() { - $packages = $installMap + $updateMap; - $roots = $packages; + $roots = $this->resultPackageMap; - foreach ($packages as $packageId => $operation) { - $package = $operation['package']; - - if (!isset($roots[$packageId])) { + foreach ($this->resultPackageMap as $packageHash => $package) { + if (!isset($roots[$packageHash])) { continue; } foreach ($package->getRequires() as $link) { - $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint()); + $possibleRequires = $this->getProvidersInResult($link); foreach ($possibleRequires as $require) { if ($require !== $package) { - unset($roots[$require->id]); + unset($roots[spl_object_hash($require)]); } } } @@ -176,69 +228,115 @@ protected function findRootPackages($installMap, $updateMap) return $roots; } - protected function findUpdates() + protected function getProvidersInResult(Link $link) { - $installMeansUpdateMap = array(); + if (!isset($this->resultPackagesByName[$link->getTarget()])) { + return array(); + } - foreach ($this->decisions as $i => $decision) { - $literal = $decision[Decisions::DECISION_LITERAL]; - $package = $this->pool->literalToPackage($literal); + return $this->resultPackagesByName[$link->getTarget()]; + } - if ($package instanceof AliasPackage) { + /** + * Workaround: if your packages depend on plugins, we must be sure + * that those are installed / updated first; else it would lead to packages + * being installed multiple times in different folders, when running Composer + * twice. + * + * While this does not fix the root-causes of https://github.com/composer/composer/issues/1147, + * it at least fixes the symptoms and makes usage of composer possible (again) + * in such scenarios. + * + * @param OperationInterface[] $operations + * @return OperationInterface[] reordered operation list + */ + private function movePluginsToFront(array $operations) + { + $dlModifyingPluginsNoDeps = array(); + $dlModifyingPluginsWithDeps = array(); + $dlModifyingPluginRequires = array(); + $pluginsNoDeps = array(); + $pluginsWithDeps = array(); + $pluginRequires = array(); + + foreach (array_reverse($operations, true) as $idx => $op) { + if ($op instanceof Operation\InstallOperation) { + $package = $op->getPackage(); + } elseif ($op instanceof Operation\UpdateOperation) { + $package = $op->getTargetPackage(); + } else { continue; } - // !wanted & installed - if ($literal <= 0 && isset($this->installedMap[$package->id])) { - $updates = $this->policy->findUpdatePackages($this->pool, $this->installedMap, $package); + $isDownloadsModifyingPlugin = $package->getType() === 'composer-plugin' && ($extra = $package->getExtra()) && isset($extra['plugin-modifies-downloads']) && $extra['plugin-modifies-downloads'] === true; - $literals = array($package->id); + // is this a downloads modifying plugin or a dependency of one? + if ($isDownloadsModifyingPlugin || count(array_intersect($package->getNames(), $dlModifyingPluginRequires))) { + // get the package's requires, but filter out any platform requirements + $requires = array_filter(array_keys($package->getRequires()), function ($req) { + return !PlatformRepository::isPlatformPackage($req); + }); - foreach ($updates as $update) { - $literals[] = $update->id; + // is this a plugin with no meaningful dependencies? + if ($isDownloadsModifyingPlugin && !count($requires)) { + // plugins with no dependencies go to the very front + array_unshift($dlModifyingPluginsNoDeps, $op); + } else { + // capture the requirements for this package so those packages will be moved up as well + $dlModifyingPluginRequires = array_merge($dlModifyingPluginRequires, $requires); + // move the operation to the front + array_unshift($dlModifyingPluginsWithDeps, $op); } - foreach ($literals as $updateLiteral) { - if ($updateLiteral !== $literal) { - $installMeansUpdateMap[abs($updateLiteral)] = $package; - } - } + unset($operations[$idx]); + continue; } - } - return $installMeansUpdateMap; - } - - protected function install($package, $reason) - { - if ($package instanceof AliasPackage) { - return $this->markAliasInstalled($package, $reason); - } + // is this package a plugin? + $isPlugin = $package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer'; - $this->transaction[] = new Operation\InstallOperation($package, $reason); - } + // is this a plugin or a dependency of a plugin? + if ($isPlugin || count(array_intersect($package->getNames(), $pluginRequires))) { + // get the package's requires, but filter out any platform requirements + $requires = array_filter(array_keys($package->getRequires()), function ($req) { + return !PlatformRepository::isPlatformPackage($req); + }); - protected function update($from, $to, $reason) - { - $this->transaction[] = new Operation\UpdateOperation($from, $to, $reason); - } + // is this a plugin with no meaningful dependencies? + if ($isPlugin && !count($requires)) { + // plugins with no dependencies go to the very front + array_unshift($pluginsNoDeps, $op); + } else { + // capture the requirements for this package so those packages will be moved up as well + $pluginRequires = array_merge($pluginRequires, $requires); + // move the operation to the front + array_unshift($pluginsWithDeps, $op); + } - protected function uninstall($package, $reason) - { - if ($package instanceof AliasPackage) { - return $this->markAliasUninstalled($package, $reason); + unset($operations[$idx]); + } } - $this->transaction[] = new Operation\UninstallOperation($package, $reason); + return array_merge($dlModifyingPluginsNoDeps, $dlModifyingPluginsWithDeps, $pluginsNoDeps, $pluginsWithDeps, $operations); } - protected function markAliasInstalled($package, $reason) + /** + * Removals of packages should be executed before installations in + * case two packages resolve to the same path (due to custom installers) + * + * @param OperationInterface[] $operations + * @return OperationInterface[] reordered operation list + */ + private function moveUninstallsToFront(array $operations) { - $this->transaction[] = new Operation\MarkAliasInstalledOperation($package, $reason); - } + $uninstOps = array(); + foreach ($operations as $idx => $op) { + if ($op instanceof Operation\UninstallOperation || $op instanceof Operation\MarkAliasUninstalledOperation) { + $uninstOps[] = $op; + unset($operations[$idx]); + } + } - protected function markAliasUninstalled($package, $reason) - { - $this->transaction[] = new Operation\MarkAliasUninstalledOperation($package, $reason); + return array_merge($uninstOps, $operations); } } diff --git a/app/vendor/composer/composer/src/Composer/Downloader/ArchiveDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/ArchiveDownloader.php index d041a7f88..f3e444d59 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/ArchiveDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/ArchiveDownloader.php @@ -14,7 +14,8 @@ use Composer\Package\PackageInterface; use Symfony\Component\Finder\Finder; -use Composer\IO\IOInterface; +use React\Promise\PromiseInterface; +use Composer\DependencyResolver\Operation\InstallOperation; /** * Base downloader for archives @@ -30,79 +31,164 @@ abstract class ArchiveDownloader extends FileDownloader * @throws \RuntimeException * @throws \UnexpectedValueException */ - public function download(PackageInterface $package, $path, $output = true) + public function install(PackageInterface $package, $path, $output = true) { - $temporaryDir = $this->config->get('vendor-dir').'/composer/'.substr(md5(uniqid('', true)), 0, 8); - $retries = 3; - while ($retries--) { - $fileName = parent::download($package, $path, $output); + if ($output) { + $this->io->writeError(" - " . InstallOperation::format($package) . $this->getInstallOperationAppendix($package, $path)); + } - if ($output) { - $this->io->writeError(' Extracting archive', false, IOInterface::VERBOSE); - } + $vendorDir = $this->config->get('vendor-dir'); - try { - $this->filesystem->ensureDirectoryExists($temporaryDir); - try { - $this->extract($fileName, $temporaryDir); - } catch (\Exception $e) { - // remove cache if the file was corrupted - parent::clearLastCacheWrite($package); - throw $e; - } + // clean up the target directory, unless it contains the vendor dir, as the vendor dir contains + // the archive to be extracted. This is the case when installing with create-project in the current directory + // but in that case we ensure the directory is empty already in ProjectInstaller so no need to empty it here. + if (false === strpos($this->filesystem->normalizePath($vendorDir), $this->filesystem->normalizePath($path.DIRECTORY_SEPARATOR))) { + $this->filesystem->emptyDirectory($path); + } - $this->filesystem->unlink($fileName); + do { + $temporaryDir = $vendorDir.'/composer/'.substr(md5(uniqid('', true)), 0, 8); + } while (is_dir($temporaryDir)); - $contentDir = $this->getFolderContent($temporaryDir); + $this->addCleanupPath($package, $temporaryDir); + // avoid cleaning up $path if installing in "." for eg create-project as we can not + // delete the directory we are currently in on windows + if (!is_dir($path) || realpath($path) !== getcwd()) { + $this->addCleanupPath($package, $path); + } - // only one dir in the archive, extract its contents out of it - if (1 === count($contentDir) && is_dir(reset($contentDir))) { - $contentDir = $this->getFolderContent((string) reset($contentDir)); - } + $this->filesystem->ensureDirectoryExists($temporaryDir); + $fileName = $this->getFileName($package, $path); + + $filesystem = $this->filesystem; + $self = $this; + + $cleanup = function () use ($path, $filesystem, $temporaryDir, $package, $self) { + // remove cache if the file was corrupted + $self->clearLastCacheWrite($package); + + // clean up + $filesystem->removeDirectory($temporaryDir); + if (is_dir($path) && realpath($path) !== getcwd()) { + $filesystem->removeDirectory($path); + } + $self->removeCleanupPath($package, $temporaryDir); + $self->removeCleanupPath($package, realpath($path)); + }; + + $promise = null; + try { + $promise = $this->extract($package, $fileName, $temporaryDir); + } catch (\Exception $e) { + $cleanup(); + throw $e; + } + + if (!$promise instanceof PromiseInterface) { + $promise = \React\Promise\resolve(); + } + + return $promise->then(function () use ($self, $package, $filesystem, $fileName, $temporaryDir, $path) { + $filesystem->unlink($fileName); + + /** + * Returns the folder content, excluding .DS_Store + * + * @param string $dir Directory + * @return \SplFileInfo[] + */ + $getFolderContent = function ($dir) { + $finder = Finder::create() + ->ignoreVCS(false) + ->ignoreDotFiles(false) + ->notName('.DS_Store') + ->depth(0) + ->in($dir); + + return iterator_to_array($finder); + }; + $renameRecursively = null; + /** + * Renames (and recursively merges if needed) a folder into another one + * + * For custom installers, where packages may share paths, and given Composer 2's parallelism, we need to make sure + * that the source directory gets merged into the target one if the target exists. Otherwise rename() by default would + * put the source into the target e.g. src/ => target/src/ (assuming target exists) instead of src/ => target/ + * + * @param string $from Directory + * @param string $to Directory + * @return void + */ + $renameRecursively = function ($from, $to) use ($filesystem, $getFolderContent, $package, &$renameRecursively) { + $contentDir = $getFolderContent($from); // move files back out of the temp dir foreach ($contentDir as $file) { $file = (string) $file; - $this->filesystem->rename($file, $path . '/' . basename($file)); + if (is_dir($to . '/' . basename($file))) { + if (!is_dir($file)) { + throw new \RuntimeException('Installing '.$package.' would lead to overwriting the '.$to.'/'.basename($file).' directory with a file from the package, invalid operation.'); + } + $renameRecursively($file, $to . '/' . basename($file)); + } else { + $filesystem->rename($file, $to . '/' . basename($file)); + } } + }; - $this->filesystem->removeDirectory($temporaryDir); - if ($this->filesystem->isDirEmpty($this->config->get('vendor-dir').'/composer/')) { - $this->filesystem->removeDirectory($this->config->get('vendor-dir').'/composer/'); + $renameAsOne = false; + if (!file_exists($path)) { + $renameAsOne = true; + } elseif ($filesystem->isDirEmpty($path)) { + try { + if ($filesystem->removeDirectoryPhp($path)) { + $renameAsOne = true; + } + } catch (\RuntimeException $e) { + // ignore error, and simply do not renameAsOne } - if ($this->filesystem->isDirEmpty($this->config->get('vendor-dir'))) { - $this->filesystem->removeDirectory($this->config->get('vendor-dir')); + } + + $contentDir = $getFolderContent($temporaryDir); + $singleDirAtTopLevel = 1 === count($contentDir) && is_dir(reset($contentDir)); + + if ($renameAsOne) { + // if the target $path is clear, we can rename the whole package in one go instead of looping over the contents + if ($singleDirAtTopLevel) { + $extractedDir = (string) reset($contentDir); + } else { + $extractedDir = $temporaryDir; } - } catch (\Exception $e) { - // clean up - $this->filesystem->removeDirectory($path); - $this->filesystem->removeDirectory($temporaryDir); - - // retry downloading if we have an invalid zip file - if ($retries && $e instanceof \UnexpectedValueException && class_exists('ZipArchive') && $e->getCode() === \ZipArchive::ER_NOZIP) { - $this->io->writeError(''); - if ($this->io->isDebug()) { - $this->io->writeError(' Invalid zip file ('.$e->getMessage().'), retrying...'); - } else { - $this->io->writeError(' Invalid zip file, retrying...'); - } - usleep(500000); - continue; + $filesystem->rename($extractedDir, $path); + } else { + // only one dir in the archive, extract its contents out of it + $from = $temporaryDir; + if ($singleDirAtTopLevel) { + $from = (string) reset($contentDir); } - throw $e; + $renameRecursively($from, $path); } - break; - } + $promise = $filesystem->removeDirectoryAsync($temporaryDir); + + return $promise->then(function () use ($self, $package, $path, $temporaryDir) { + $self->removeCleanupPath($package, $temporaryDir); + $self->removeCleanupPath($package, $path); + }); + }, function ($e) use ($cleanup) { + $cleanup(); + + throw $e; + }); } /** - * {@inheritdoc} + * {@inheritDoc} */ - protected function getFileName(PackageInterface $package, $path) + protected function getInstallOperationAppendix(PackageInterface $package, $path) { - return rtrim($path.'/'.md5($path.spl_object_hash($package)).'.'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_EXTENSION), '.'); + return ': Extracting archive'; } /** @@ -112,24 +198,7 @@ protected function getFileName(PackageInterface $package, $path) * @param string $path Directory * * @throws \UnexpectedValueException If can not extract downloaded file to path + * @return PromiseInterface|null */ - abstract protected function extract($file, $path); - - /** - * Returns the folder content, excluding dotfiles - * - * @param string $dir Directory - * @return \SplFileInfo[] - */ - private function getFolderContent($dir) - { - $finder = Finder::create() - ->ignoreVCS(false) - ->ignoreDotFiles(false) - ->notName('.DS_Store') - ->depth(0) - ->in($dir); - - return iterator_to_array($finder); - } + abstract protected function extract(PackageInterface $package, $file, $path); } diff --git a/app/vendor/composer/composer/src/Composer/Downloader/DownloadManager.php b/app/vendor/composer/composer/src/Composer/Downloader/DownloadManager.php index 15c00a6e6..7616229cb 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/DownloadManager.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/DownloadManager.php @@ -15,6 +15,8 @@ use Composer\Package\PackageInterface; use Composer\IO\IOInterface; use Composer\Util\Filesystem; +use Composer\Exception\IrrecoverableDownloadException; +use React\Promise\PromiseInterface; /** * Downloaders manager. @@ -23,11 +25,17 @@ */ class DownloadManager { + /** @var IOInterface */ private $io; + /** @var bool */ private $preferDist = false; - private $preferSource = false; + /** @var bool */ + private $preferSource; + /** @var array */ private $packagePreferences = array(); + /** @var Filesystem */ private $filesystem; + /** @var array */ private $downloaders = array(); /** @@ -83,22 +91,6 @@ public function setPreferences(array $preferences) return $this; } - /** - * Sets whether to output download progress information for all registered - * downloaders - * - * @param bool $outputProgress - * @return DownloadManager - */ - public function setOutputProgress($outputProgress) - { - foreach ($this->downloaders as $downloader) { - $downloader->setOutputProgress($outputProgress); - } - - return $this; - } - /** * Sets installer downloader for a specific installation type. * @@ -140,12 +132,12 @@ public function getDownloader($type) * wrong type * @return DownloaderInterface|null */ - public function getDownloaderForInstalledPackage(PackageInterface $package) + public function getDownloaderForPackage(PackageInterface $package) { $installationSource = $package->getInstallationSource(); if ('metapackage' === $package->getType()) { - return; + return null; } if ('dist' === $installationSource) { @@ -154,7 +146,7 @@ public function getDownloaderForInstalledPackage(PackageInterface $package) $downloader = $this->getDownloader($package->getSourceType()); } else { throw new \InvalidArgumentException( - 'Package '.$package.' seems not been installed properly' + 'Package '.$package.' does not have an installation source set' ); } @@ -171,64 +163,122 @@ public function getDownloaderForInstalledPackage(PackageInterface $package) return $downloader; } + public function getDownloaderType(DownloaderInterface $downloader) + { + return array_search($downloader, $this->downloaders); + } + /** * Downloads package into target dir. * - * @param PackageInterface $package package instance - * @param string $targetDir target dir - * @param bool $preferSource prefer installation from source + * @param PackageInterface $package package instance + * @param string $targetDir target dir + * @param PackageInterface|null $prevPackage previous package instance in case of updates * * @throws \InvalidArgumentException if package have no urls to download from * @throws \RuntimeException + * @return PromiseInterface */ - public function download(PackageInterface $package, $targetDir, $preferSource = null) + public function download(PackageInterface $package, $targetDir, PackageInterface $prevPackage = null) { - $preferSource = null !== $preferSource ? $preferSource : $this->preferSource; - $sourceType = $package->getSourceType(); - $distType = $package->getDistType(); - - $sources = array(); - if ($sourceType) { - $sources[] = 'source'; - } - if ($distType) { - $sources[] = 'dist'; - } - - if (empty($sources)) { - throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified'); - } + $targetDir = $this->normalizeTargetDir($targetDir); + $this->filesystem->ensureDirectoryExists(dirname($targetDir)); - if (!$preferSource && ($this->preferDist || 'dist' === $this->resolvePackageInstallPreference($package))) { - $sources = array_reverse($sources); - } + $sources = $this->getAvailableSources($package, $prevPackage); - $this->filesystem->ensureDirectoryExists($targetDir); + $io = $this->io; + $self = $this; - foreach ($sources as $i => $source) { - if (isset($e)) { - $this->io->writeError(' Now trying to download from ' . $source . ''); + $download = function ($retry = false) use (&$sources, $io, $package, $self, $targetDir, &$download, $prevPackage) { + $source = array_shift($sources); + if ($retry) { + $io->writeError(' Now trying to download from ' . $source . ''); } $package->setInstallationSource($source); - try { - $downloader = $this->getDownloaderForInstalledPackage($package); - if ($downloader) { - $downloader->download($package, $targetDir); - } - break; - } catch (\RuntimeException $e) { - if ($i === count($sources) - 1) { - throw $e; + + $downloader = $self->getDownloaderForPackage($package); + if (!$downloader) { + return \React\Promise\resolve(); + } + + $handleError = function ($e) use ($sources, $source, $package, $io, $download) { + if ($e instanceof \RuntimeException && !$e instanceof IrrecoverableDownloadException) { + if (!$sources) { + throw $e; + } + + $io->writeError( + ' Failed to download '. + $package->getPrettyName(). + ' from ' . $source . ': '. + $e->getMessage().'' + ); + + return $download(true); } - $this->io->writeError( - ' Failed to download '. - $package->getPrettyName(). - ' from ' . $source . ': '. - $e->getMessage().'' - ); + throw $e; + }; + + try { + $result = $downloader->download($package, $targetDir, $prevPackage); + } catch (\Exception $e) { + return $handleError($e); } + if (!$result instanceof PromiseInterface) { + return \React\Promise\resolve($result); + } + + $res = $result->then(function ($res) { + return $res; + }, $handleError); + + return $res; + }; + + return $download(); + } + + /** + * Prepares an operation execution + * + * @param string $type one of install/update/uninstall + * @param PackageInterface $package package instance + * @param string $targetDir target dir + * @param PackageInterface|null $prevPackage previous package instance in case of updates + * + * @return PromiseInterface|null + */ + public function prepare($type, PackageInterface $package, $targetDir, PackageInterface $prevPackage = null) + { + $targetDir = $this->normalizeTargetDir($targetDir); + $downloader = $this->getDownloaderForPackage($package); + if ($downloader) { + return $downloader->prepare($type, $package, $targetDir, $prevPackage); } + + return \React\Promise\resolve(); + } + + /** + * Installs package into target dir. + * + * @param PackageInterface $package package instance + * @param string $targetDir target dir + * + * @throws \InvalidArgumentException if package have no urls to download from + * @throws \RuntimeException + * @return PromiseInterface|null + */ + public function install(PackageInterface $package, $targetDir) + { + $targetDir = $this->normalizeTargetDir($targetDir); + $downloader = $this->getDownloaderForPackage($package); + if ($downloader) { + return $downloader->install($package, $targetDir); + } + + return \React\Promise\resolve(); } /** @@ -239,51 +289,52 @@ public function download(PackageInterface $package, $targetDir, $preferSource = * @param string $targetDir target dir * * @throws \InvalidArgumentException if initial package is not installed + * @return PromiseInterface|null */ public function update(PackageInterface $initial, PackageInterface $target, $targetDir) { - $downloader = $this->getDownloaderForInstalledPackage($initial); - if (!$downloader) { - return; - } + $targetDir = $this->normalizeTargetDir($targetDir); + $downloader = $this->getDownloaderForPackage($target); + $initialDownloader = $this->getDownloaderForPackage($initial); - $installationSource = $initial->getInstallationSource(); - - if ('dist' === $installationSource) { - $initialType = $initial->getDistType(); - $targetType = $target->getDistType(); - } else { - $initialType = $initial->getSourceType(); - $targetType = $target->getSourceType(); + // no downloaders present means update from metapackage to metapackage, nothing to do + if (!$initialDownloader && !$downloader) { + return \React\Promise\resolve(); } - // upgrading from a dist stable package to a dev package, force source reinstall - if ($target->isDev() && 'dist' === $installationSource) { - $downloader->remove($initial, $targetDir); - $this->download($target, $targetDir); - - return; + // if we have a downloader present before, but not after, the package became a metapackage and its files should be removed + if (!$downloader) { + return $initialDownloader->remove($initial, $targetDir); } + $initialType = $this->getDownloaderType($initialDownloader); + $targetType = $this->getDownloaderType($downloader); if ($initialType === $targetType) { - $target->setInstallationSource($installationSource); try { - $downloader->update($initial, $target, $targetDir); - - return; + return $downloader->update($initial, $target, $targetDir); } catch (\RuntimeException $e) { if (!$this->io->isInteractive()) { throw $e; } $this->io->writeError(' Update failed ('.$e->getMessage().')'); - if (!$this->io->askConfirmation(' Would you like to try reinstalling the package instead [yes]? ', true)) { + if (!$this->io->askConfirmation(' Would you like to try reinstalling the package instead [yes]? ')) { throw $e; } } } - $downloader->remove($initial, $targetDir); - $this->download($target, $targetDir, 'source' === $installationSource); + // if downloader type changed, or update failed and user asks for reinstall, + // we wipe the dir and do a new install instead of updating it + $promise = $initialDownloader->remove($initial, $targetDir); + if ($promise) { + $self = $this; + + return $promise->then(function ($res) use ($self, $target, $targetDir) { + return $self->install($target, $targetDir); + }); + } + + return $this->install($target, $targetDir); } /** @@ -291,13 +342,39 @@ public function update(PackageInterface $initial, PackageInterface $target, $tar * * @param PackageInterface $package package instance * @param string $targetDir target dir + * + * @return PromiseInterface|null */ public function remove(PackageInterface $package, $targetDir) { - $downloader = $this->getDownloaderForInstalledPackage($package); + $targetDir = $this->normalizeTargetDir($targetDir); + $downloader = $this->getDownloaderForPackage($package); if ($downloader) { - $downloader->remove($package, $targetDir); + return $downloader->remove($package, $targetDir); } + + return \React\Promise\resolve(); + } + + /** + * Cleans up a failed operation + * + * @param string $type one of install/update/uninstall + * @param PackageInterface $package package instance + * @param string $targetDir target dir + * @param PackageInterface|null $prevPackage previous package instance in case of updates + * + * @return PromiseInterface|null + */ + public function cleanup($type, PackageInterface $package, $targetDir, PackageInterface $prevPackage = null) + { + $targetDir = $this->normalizeTargetDir($targetDir); + $downloader = $this->getDownloaderForPackage($package); + if ($downloader) { + return $downloader->cleanup($type, $package, $targetDir, $prevPackage); + } + + return \React\Promise\resolve(); } /** @@ -322,4 +399,65 @@ protected function resolvePackageInstallPreference(PackageInterface $package) return $package->isDev() ? 'source' : 'dist'; } + + /** + * @return string[] + * @phpstan-return array<'dist'|'source'>&non-empty-array + */ + private function getAvailableSources(PackageInterface $package, PackageInterface $prevPackage = null) + { + $sourceType = $package->getSourceType(); + $distType = $package->getDistType(); + + // add source before dist by default + $sources = array(); + if ($sourceType) { + $sources[] = 'source'; + } + if ($distType) { + $sources[] = 'dist'; + } + + if (empty($sources)) { + throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified'); + } + + if ( + $prevPackage + // if we are updating, we want to keep the same source as the previously installed package (if available in the new one) + && in_array($prevPackage->getInstallationSource(), $sources, true) + // unless the previous package was stable dist (by default) and the new package is dev, then we allow the new default to take over + && !(!$prevPackage->isDev() && $prevPackage->getInstallationSource() === 'dist' && $package->isDev()) + ) { + $prevSource = $prevPackage->getInstallationSource(); + usort($sources, function ($a, $b) use ($prevSource) { + return $a === $prevSource ? -1 : 1; + }); + + return $sources; + } + + // reverse sources in case dist is the preferred source for this package + if (!$this->preferSource && ($this->preferDist || 'dist' === $this->resolvePackageInstallPreference($package))) { + $sources = array_reverse($sources); + } + + return $sources; + } + + /** + * Downloaders expect a /path/to/dir without trailing slash + * + * If any Installer provides a path with a trailing slash, this can cause bugs so make sure we remove them + * + * @return string + */ + private function normalizeTargetDir($dir) + { + if ($dir === '\\' || $dir === '/') { + return $dir; + } + + return rtrim($dir, '\\/'); + } } diff --git a/app/vendor/composer/composer/src/Composer/Downloader/DownloaderInterface.php b/app/vendor/composer/composer/src/Composer/Downloader/DownloaderInterface.php index 713bf36dc..ef2357958 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/DownloaderInterface.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/DownloaderInterface.php @@ -13,6 +13,7 @@ namespace Composer\Downloader; use Composer\Package\PackageInterface; +use React\Promise\PromiseInterface; /** * Downloader interface. @@ -30,35 +31,68 @@ interface DownloaderInterface public function getInstallationSource(); /** - * Downloads specific package into specific folder. + * This should do any network-related tasks to prepare for an upcoming install/update * - * @param PackageInterface $package package instance - * @param string $path download path + * @return PromiseInterface|null */ - public function download(PackageInterface $package, $path); + public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null); + + /** + * Do anything that needs to be done between all downloads have been completed and the actual operation is executed + * + * All packages get first downloaded, then all together prepared, then all together installed/updated/uninstalled. Therefore + * for error recovery it is important to avoid failing during install/update/uninstall as much as possible, and risky things or + * user prompts should happen in the prepare step rather. In case of failure, cleanup() will be called so that changes can + * be undone as much as possible. + * + * @param string $type one of install/update/uninstall + * @param PackageInterface $package package instance + * @param string $path download path + * @param PackageInterface $prevPackage previous package instance in case of an update + * @return PromiseInterface|null + */ + public function prepare($type, PackageInterface $package, $path, PackageInterface $prevPackage = null); + + /** + * Installs specific package into specific folder. + * + * @param PackageInterface $package package instance + * @param string $path download path + * @return PromiseInterface|null + */ + public function install(PackageInterface $package, $path); /** * Updates specific package in specific folder from initial to target version. * - * @param PackageInterface $initial initial package - * @param PackageInterface $target updated package - * @param string $path download path + * @param PackageInterface $initial initial package + * @param PackageInterface $target updated package + * @param string $path download path + * @return PromiseInterface|null */ public function update(PackageInterface $initial, PackageInterface $target, $path); /** * Removes specific package from specific folder. * - * @param PackageInterface $package package instance - * @param string $path download path + * @param PackageInterface $package package instance + * @param string $path download path + * @return PromiseInterface|null */ public function remove(PackageInterface $package, $path); /** - * Sets whether to output download progress information or not + * Do anything to cleanup changes applied in the prepare or install/update/uninstall steps + * + * Note that cleanup will be called for all packages, either after install/update/uninstall is complete, + * or if any package failed any operation. This is to give all installers a change to cleanup things + * they did previously, so you need to keep track of changes applied in the installer/downloader themselves. * - * @param bool $outputProgress - * @return DownloaderInterface + * @param string $type one of install/update/uninstall + * @param PackageInterface $package package instance + * @param string $path download path + * @param PackageInterface $prevPackage previous package instance in case of an update + * @return PromiseInterface|null */ - public function setOutputProgress($outputProgress); + public function cleanup($type, PackageInterface $package, $path, PackageInterface $prevPackage = null); } diff --git a/app/vendor/composer/composer/src/Composer/Downloader/FileDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/FileDownloader.php index 819fbcefb..e915d6995 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/FileDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/FileDownloader.php @@ -14,18 +14,24 @@ use Composer\Config; use Composer\Cache; -use Composer\Factory; use Composer\IO\IOInterface; use Composer\IO\NullIO; +use Composer\Exception\IrrecoverableDownloadException; use Composer\Package\Comparer\Comparer; +use Composer\DependencyResolver\Operation\UpdateOperation; +use Composer\DependencyResolver\Operation\InstallOperation; +use Composer\DependencyResolver\Operation\UninstallOperation; use Composer\Package\PackageInterface; -use Composer\Package\Version\VersionParser; use Composer\Plugin\PluginEvents; +use Composer\Plugin\PostFileDownloadEvent; use Composer\Plugin\PreFileDownloadEvent; use Composer\EventDispatcher\EventDispatcher; use Composer\Util\Filesystem; -use Composer\Util\RemoteFilesystem; +use Composer\Util\Silencer; +use Composer\Util\HttpDownloader; use Composer\Util\Url as UrlUtil; +use Composer\Util\ProcessExecutor; +use React\Promise\PromiseInterface; /** * Base downloader for files @@ -37,35 +43,51 @@ */ class FileDownloader implements DownloaderInterface, ChangeReportInterface { + /** @var IOInterface */ protected $io; + /** @var Config */ protected $config; - protected $rfs; + /** @var HttpDownloader */ + protected $httpDownloader; + /** @var Filesystem */ protected $filesystem; + /** @var ?Cache */ protected $cache; - protected $outputProgress = true; - private $lastCacheWrites = array(); - private $eventDispatcher; + /** @var ?EventDispatcher */ + protected $eventDispatcher; + /** @var ProcessExecutor */ + protected $process; + /** + * @private this is only public for php 5.3 support in closures + * + * @var array Map of package name to cache key + */ + public $lastCacheWrites = array(); + /** @var array Map of package name to list of paths */ + private $additionalCleanupPaths = array(); /** * Constructor. * - * @param IOInterface $io The IO instance - * @param Config $config The config - * @param EventDispatcher $eventDispatcher The event dispatcher - * @param Cache $cache Optional cache instance - * @param RemoteFilesystem $rfs The remote filesystem - * @param Filesystem $filesystem The filesystem + * @param IOInterface $io The IO instance + * @param Config $config The config + * @param HttpDownloader $httpDownloader The remote filesystem + * @param EventDispatcher $eventDispatcher The event dispatcher + * @param Cache $cache Cache instance + * @param Filesystem $filesystem The filesystem */ - public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, RemoteFilesystem $rfs = null, Filesystem $filesystem = null) + public function __construct(IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, Filesystem $filesystem = null, ProcessExecutor $process = null) { $this->io = $io; $this->config = $config; $this->eventDispatcher = $eventDispatcher; - $this->rfs = $rfs ?: Factory::createRemoteFilesystem($this->io, $config); - $this->filesystem = $filesystem ?: new Filesystem(); + $this->httpDownloader = $httpDownloader; $this->cache = $cache; + $this->process = $process ?: new ProcessExecutor($io); + $this->filesystem = $filesystem ?: new Filesystem($this->process); if ($this->cache && $this->cache->gcIsNecessary()) { + $this->io->writeError('Running cache garbage collection', true, IOInterface::VERY_VERBOSE); $this->cache->gc($config->get('cache-files-ttl'), $config->get('cache-files-maxsize')); } } @@ -81,127 +103,256 @@ public function getInstallationSource() /** * {@inheritDoc} */ - public function download(PackageInterface $package, $path, $output = true) + public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null, $output = true) { if (!$package->getDistUrl()) { throw new \InvalidArgumentException('The given package is missing url information'); } - if ($output) { - $this->io->writeError(" - Installing " . $package->getName() . " (" . $package->getFullPrettyVersion() . "): ", false); + $cacheKeyGenerator = function (PackageInterface $package, $key) { + $cacheKey = sha1($key); + + return $package->getName().'/'.$cacheKey.'.'.$package->getDistType(); + }; + + $retries = 3; + $distUrls = $package->getDistUrls(); + /** @var array $urls */ + $urls = array(); + foreach ($distUrls as $index => $url) { + $processedUrl = $this->processUrl($package, $url); + $urls[$index] = array( + 'base' => $url, + 'processed' => $processedUrl, + // we use the complete download url here to avoid conflicting entries + // from different packages, which would potentially allow a given package + // in a third party repo to pre-populate the cache for the same package in + // packagist for example. + 'cacheKey' => $cacheKeyGenerator($package, $processedUrl), + ); } - $urls = $package->getDistUrls(); - while ($url = array_shift($urls)) { - try { - $fileName = $this->doDownload($package, $path, $url); - break; - } catch (\Exception $e) { - if ($this->io->isDebug()) { - $this->io->writeError(''); - $this->io->writeError('Failed: ['.get_class($e).'] '.$e->getCode().': '.$e->getMessage()); - } elseif (count($urls)) { - $this->io->writeError(''); - $this->io->writeError(' Failed, trying the next URL ('.$e->getCode().': '.$e->getMessage().')', false); + $fileName = $this->getFileName($package, $path); + $this->filesystem->ensureDirectoryExists($path); + $this->filesystem->ensureDirectoryExists(dirname($fileName)); + + $io = $this->io; + $cache = $this->cache; + $httpDownloader = $this->httpDownloader; + $eventDispatcher = $this->eventDispatcher; + $filesystem = $this->filesystem; + $self = $this; + + $accept = null; + $reject = null; + $download = function () use ($io, $output, $httpDownloader, $cache, $cacheKeyGenerator, $eventDispatcher, $package, $fileName, &$urls, &$accept, &$reject, $self) { + /** @var array{base: string, processed: string, cacheKey: string} $url */ + $url = reset($urls); + $index = key($urls); + + if ($eventDispatcher) { + $preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $httpDownloader, $url['processed'], 'package', $package); + $eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent); + if ($preFileDownloadEvent->getCustomCacheKey() !== null) { + $url['cacheKey'] = $cacheKeyGenerator($package, $preFileDownloadEvent->getCustomCacheKey()); + } elseif ($preFileDownloadEvent->getProcessedUrl() !== $url['processed']) { + $url['cacheKey'] = $cacheKeyGenerator($package, $preFileDownloadEvent->getProcessedUrl()); } + $url['processed'] = $preFileDownloadEvent->getProcessedUrl(); + } + + $urls[$index] = $url; - if (!count($urls)) { - throw $e; + $checksum = $package->getDistSha1Checksum(); + $cacheKey = $url['cacheKey']; + + // use from cache if it is present and has a valid checksum or we have no checksum to check against + if ($cache && (!$checksum || $checksum === $cache->sha1($cacheKey)) && $cache->copyTo($cacheKey, $fileName)) { + if ($output) { + $io->writeError(" - Loading " . $package->getName() . " (" . $package->getFullPrettyVersion() . ") from cache", true, IOInterface::VERY_VERBOSE); + } + // mark the file as having been written in cache even though it is only read from cache, so that if + // the cache is corrupt the archive will be deleted and the next attempt will re-download it + // see https://github.com/composer/composer/issues/10028 + if (!$cache->isReadOnly()) { + $self->lastCacheWrites[$package->getName()] = $cacheKey; } + $result = \React\Promise\resolve($fileName); + } else { + if ($output) { + $io->writeError(" - Downloading " . $package->getName() . " (" . $package->getFullPrettyVersion() . ")"); + } + + $result = $httpDownloader->addCopy($url['processed'], $fileName, $package->getTransportOptions()) + ->then($accept, $reject); } - } - if ($output) { - $this->io->writeError(''); - } + return $result->then(function ($result) use ($fileName, $checksum, $url, $package, $eventDispatcher) { + // in case of retry, the first call's Promise chain finally calls this twice at the end, + // once with $result being the returned $fileName from $accept, and then once for every + // failed request with a null result, which can be skipped. + if (null === $result) { + return $fileName; + } - return $fileName; - } + if (!file_exists($fileName)) { + throw new \UnexpectedValueException($url['base'].' could not be saved to '.$fileName.', make sure the' + .' directory is writable and you have internet connectivity'); + } - protected function doDownload(PackageInterface $package, $path, $url) - { - $this->filesystem->emptyDirectory($path); + if ($checksum && hash_file('sha1', $fileName) !== $checksum) { + throw new \UnexpectedValueException('The checksum verification of the file failed (downloaded from '.$url['base'].')'); + } - $fileName = $this->getFileName($package, $path); + if ($eventDispatcher) { + $postFileDownloadEvent = new PostFileDownloadEvent(PluginEvents::POST_FILE_DOWNLOAD, $fileName, $checksum, $url['processed'], 'package', $package); + $eventDispatcher->dispatch($postFileDownloadEvent->getName(), $postFileDownloadEvent); + } - $processedUrl = $this->processUrl($package, $url); - $origin = RemoteFilesystem::getOrigin($processedUrl); + return $fileName; + }); + }; - $preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->rfs, $processedUrl); - if ($this->eventDispatcher) { - $this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent); - } - $rfs = $preFileDownloadEvent->getRemoteFilesystem(); + $accept = function ($response) use ($cache, $package, $fileName, $self, &$urls) { + $url = reset($urls); + $cacheKey = $url['cacheKey']; - try { - $checksum = $package->getDistSha1Checksum(); - $cacheKey = $this->getCacheKey($package, $processedUrl); + if ($cache && !$cache->isReadOnly()) { + $self->lastCacheWrites[$package->getName()] = $cacheKey; + $cache->copyFrom($cacheKey, $fileName); + } - // use from cache if it is present and has a valid checksum or we have no checksum to check against - if ($this->cache && (!$checksum || $checksum === $this->cache->sha1($cacheKey)) && $this->cache->copyTo($cacheKey, $fileName)) { - $this->io->writeError('Loading from cache', false); - } else { - // download if cache restore failed - if (!$this->outputProgress) { - $this->io->writeError('Downloading', false); - } + $response->collect(); - // try to download 3 times then fail hard - $retries = 3; - while ($retries--) { - try { - $rfs->copy($origin, $processedUrl, $fileName, $this->outputProgress, $package->getTransportOptions()); - break; - } catch (TransportException $e) { - // if we got an http response with a proper code, then requesting again will probably not help, abort - if ((0 !== $e->getCode() && !in_array($e->getCode(), array(500, 502, 503, 504))) || !$retries) { - throw $e; - } - $this->io->writeError(''); - $this->io->writeError(' Download failed, retrying...', true, IOInterface::VERBOSE); - usleep(500000); - } - } + return $fileName; + }; - if (!$this->outputProgress) { - $this->io->writeError(' (100%)', false); - } + $reject = function ($e) use ($io, &$urls, $download, $fileName, $package, &$retries, $filesystem, $self) { + // clean up + if (file_exists($fileName)) { + $filesystem->unlink($fileName); + } + $self->clearLastCacheWrite($package); + + if ($e instanceof IrrecoverableDownloadException) { + throw $e; + } - if ($this->cache) { - $this->lastCacheWrites[$package->getName()] = $cacheKey; - $this->cache->copyFrom($cacheKey, $fileName); + if ($e instanceof MaxFileSizeExceededException) { + throw $e; + } + + if ($e instanceof TransportException) { + // if we got an http response with a proper code, then requesting again will probably not help, abort + if ((0 !== $e->getCode() && !in_array($e->getCode(), array(500, 502, 503, 504))) || !$retries) { + $retries = 0; } } - if (!file_exists($fileName)) { - throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the' - .' directory is writable and you have internet connectivity'); + // special error code returned when network is being artificially disabled + if ($e instanceof TransportException && $e->getStatusCode() === 499) { + $retries = 0; + $urls = array(); + } + + if ($retries) { + usleep(500000); + $retries--; + + return $download(); } - if ($checksum && hash_file('sha1', $fileName) !== $checksum) { - throw new \UnexpectedValueException('The checksum verification of the file failed (downloaded from '.$url.')'); + array_shift($urls); + if ($urls) { + if ($io->isDebug()) { + $io->writeError(' Failed downloading '.$package->getName().': ['.get_class($e).'] '.$e->getCode().': '.$e->getMessage()); + $io->writeError(' Trying the next URL for '.$package->getName()); + } else { + $io->writeError(' Failed downloading '.$package->getName().', trying the next URL ('.$e->getCode().': '.$e->getMessage().')'); + } + + $retries = 3; + usleep(100000); + + return $download(); } - } catch (\Exception $e) { - // clean up - $this->filesystem->removeDirectory($path); - $this->clearLastCacheWrite($package); + throw $e; + }; + + return $download(); + } + + /** + * {@inheritDoc} + */ + public function prepare($type, PackageInterface $package, $path, PackageInterface $prevPackage = null) + { + return \React\Promise\resolve(); + } + + /** + * {@inheritDoc} + */ + public function cleanup($type, PackageInterface $package, $path, PackageInterface $prevPackage = null) + { + $fileName = $this->getFileName($package, $path); + if (file_exists($fileName)) { + $this->filesystem->unlink($fileName); + } + + $dirsToCleanUp = array( + $this->config->get('vendor-dir').'/composer/', + $this->config->get('vendor-dir'), + $path, + ); + + if (isset($this->additionalCleanupPaths[$package->getName()])) { + foreach ($this->additionalCleanupPaths[$package->getName()] as $path) { + $this->filesystem->remove($path); + } + } + + foreach ($dirsToCleanUp as $dir) { + if (is_dir($dir) && $this->filesystem->isDirEmpty($dir) && realpath($dir) !== getcwd()) { + $this->filesystem->removeDirectoryPhp($dir); + } } - return $fileName; + return \React\Promise\resolve(); } /** * {@inheritDoc} */ - public function setOutputProgress($outputProgress) + public function install(PackageInterface $package, $path, $output = true) { - $this->outputProgress = $outputProgress; + if ($output) { + $this->io->writeError(" - " . InstallOperation::format($package)); + } + + $this->filesystem->emptyDirectory($path); + $this->filesystem->ensureDirectoryExists($path); + $this->filesystem->rename($this->getFileName($package, $path), $path . '/' . pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME)); + + if ($package->getBinaries()) { + // Single files can not have a mode set like files in archives + // so we make sure if the file is a binary that it is executable + foreach ($package->getBinaries() as $bin) { + if (file_exists($path . '/' . $bin) && !is_executable($path . '/' . $bin)) { + Silencer::call('chmod', $path . '/' . $bin, 0777 & ~umask()); + } + } + } - return $this; + return \React\Promise\resolve(); } - protected function clearLastCacheWrite(PackageInterface $package) + /** + * TODO mark private in v3 + * @protected This is public due to PHP 5.3 + */ + public function clearLastCacheWrite(PackageInterface $package) { if ($this->cache && isset($this->lastCacheWrites[$package->getName()])) { $this->cache->remove($this->lastCacheWrites[$package->getName()]); @@ -209,22 +360,48 @@ protected function clearLastCacheWrite(PackageInterface $package) } } + /** + * TODO mark private in v3 + * @protected This is public due to PHP 5.3 + */ + public function addCleanupPath(PackageInterface $package, $path) + { + $this->additionalCleanupPaths[$package->getName()][] = $path; + } + + /** + * TODO mark private in v3 + * @protected This is public due to PHP 5.3 + */ + public function removeCleanupPath(PackageInterface $package, $path) + { + if (isset($this->additionalCleanupPaths[$package->getName()])) { + $idx = array_search($path, $this->additionalCleanupPaths[$package->getName()]); + if (false !== $idx) { + unset($this->additionalCleanupPaths[$package->getName()][$idx]); + } + } + } + /** * {@inheritDoc} */ public function update(PackageInterface $initial, PackageInterface $target, $path) { - $name = $target->getName(); - $from = $initial->getFullPrettyVersion(); - $to = $target->getFullPrettyVersion(); + $this->io->writeError(" - " . UpdateOperation::format($initial, $target) . $this->getInstallOperationAppendix($target, $path)); - $actionName = VersionParser::isUpgrade($initial->getVersion(), $target->getVersion()) ? 'Updating' : 'Downgrading'; - $this->io->writeError(" - " . $actionName . " " . $name . " (" . $from . " => " . $to . "): ", false); + $promise = $this->remove($initial, $path, false); + if (!$promise instanceof PromiseInterface) { + $promise = \React\Promise\resolve(); + } + $self = $this; + $io = $this->io; - $this->remove($initial, $path, false); - $this->download($target, $path, false); + return $promise->then(function () use ($self, $target, $path) { + $promise = $self->install($target, $path, false); - $this->io->writeError(''); + return $promise; + }); } /** @@ -233,11 +410,15 @@ public function update(PackageInterface $initial, PackageInterface $target, $pat public function remove(PackageInterface $package, $path, $output = true) { if ($output) { - $this->io->writeError(" - Removing " . $package->getName() . " (" . $package->getFullPrettyVersion() . ")"); - } - if (!$this->filesystem->removeDirectory($path)) { - throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); + $this->io->writeError(" - " . UninstallOperation::format($package)); } + $promise = $this->filesystem->removeDirectoryAsync($path); + + return $promise->then(function ($result) use ($path) { + if (!$result) { + throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); + } + }); } /** @@ -249,7 +430,19 @@ public function remove(PackageInterface $package, $path, $output = true) */ protected function getFileName(PackageInterface $package, $path) { - return $path.'/'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME); + return rtrim($this->config->get('vendor-dir').'/composer/tmp-'.md5($package.spl_object_hash($package)).'.'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_EXTENSION), '.'); + } + + /** + * Gets appendix message to add to the "- Upgrading x" string being output on update + * + * @param PackageInterface $package package instance + * @param string $path download path + * @return string + */ + protected function getInstallOperationAppendix(PackageInterface $package, $path) + { + return ''; } /** @@ -273,17 +466,6 @@ protected function processUrl(PackageInterface $package, $url) return $url; } - private function getCacheKey(PackageInterface $package, $processedUrl) - { - // we use the complete download url here to avoid conflicting entries - // from different packages, which would potentially allow a given package - // in a third party repo to pre-populate the cache for the same package in - // packagist for example. - $cacheKey = sha1($processedUrl); - - return $package->getName().'/'.$cacheKey.'.'.$package->getDistType(); - } - /** * {@inheritDoc} * @throws \RuntimeException @@ -291,15 +473,22 @@ private function getCacheKey(PackageInterface $package, $processedUrl) public function getLocalChanges(PackageInterface $package, $targetDir) { $prevIO = $this->io; - $prevProgress = $this->outputProgress; $this->io = new NullIO; $this->io->loadConfiguration($this->config); - $this->outputProgress = false; $e = null; + $output = ''; + $targetDir = Filesystem::trimTrailingSlash($targetDir); try { - $this->download($package, $targetDir.'_compare', false); + if (is_dir($targetDir.'_compare')) { + $this->filesystem->removeDirectory($targetDir.'_compare'); + } + + $this->download($package, $targetDir.'_compare', null, false); + $this->httpDownloader->wait(); + $this->install($package, $targetDir.'_compare', false); + $this->process->wait(); $comparer = new Comparer(); $comparer->setSource($targetDir.'_compare'); @@ -311,7 +500,6 @@ public function getLocalChanges(PackageInterface $package, $targetDir) } $this->io = $prevIO; - $this->outputProgress = $prevProgress; if ($e) { throw $e; diff --git a/app/vendor/composer/composer/src/Composer/Downloader/FossilDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/FossilDownloader.php index 57ee85965..d96c4f4ff 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/FossilDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/FossilDownloader.php @@ -23,7 +23,15 @@ class FossilDownloader extends VcsDownloader /** * {@inheritDoc} */ - public function doDownload(PackageInterface $package, $path, $url) + protected function doDownload(PackageInterface $package, $path, $url, PackageInterface $prevPackage = null) + { + return \React\Promise\resolve(); + } + + /** + * {@inheritDoc} + */ + protected function doInstall(PackageInterface $package, $path, $url) { // Ensure we are allowed to use this URL by config $this->config->prohibitUrlByConfig($url, $this->io); @@ -44,17 +52,18 @@ public function doDownload(PackageInterface $package, $path, $url) if (0 !== $this->process->execute($command, $ignoredOutput, realpath($path))) { throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); } + + return \React\Promise\resolve(); } /** * {@inheritDoc} */ - public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) + protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { // Ensure we are allowed to use this URL by config $this->config->prohibitUrlByConfig($url, $this->io); - $url = ProcessExecutor::escape($url); $ref = ProcessExecutor::escape($target->getSourceReference()); $this->io->writeError(" Updating to ".$target->getSourceReference()); @@ -66,6 +75,8 @@ public function doUpdate(PackageInterface $initial, PackageInterface $target, $p if (0 !== $this->process->execute($command, $ignoredOutput, realpath($path))) { throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); } + + return \React\Promise\resolve(); } /** diff --git a/app/vendor/composer/composer/src/Composer/Downloader/GitDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/GitDownloader.php index 88cb4b475..f8e157894 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/GitDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/GitDownloader.php @@ -17,6 +17,7 @@ use Composer\Package\PackageInterface; use Composer\Util\Filesystem; use Composer\Util\Git as GitUtil; +use Composer\Util\Url; use Composer\Util\Platform; use Composer\Util\ProcessExecutor; use Composer\Cache; @@ -26,9 +27,25 @@ */ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface { - private $hasStashedChanges = false; - private $hasDiscardedChanges = false; + /** + * @var bool[] + * @phpstan-var array + */ + private $hasStashedChanges = array(); + /** + * @var bool[] + * @phpstan-var array + */ + private $hasDiscardedChanges = array(); + /** + * @var GitUtil + */ private $gitUtil; + /** + * @var array + * @phpstan-var array> + */ + private $cachedPackages = array(); public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, Filesystem $fs = null) { @@ -39,39 +56,53 @@ public function __construct(IOInterface $io, Config $config, ProcessExecutor $pr /** * {@inheritDoc} */ - public function doDownload(PackageInterface $package, $path, $url) + protected function doDownload(PackageInterface $package, $path, $url, PackageInterface $prevPackage = null) { GitUtil::cleanEnv(); - $path = $this->normalizePath($path); - $cachePath = $this->config->get('cache-vcs-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $url).'/'; - $ref = $package->getSourceReference(); - $flag = Platform::isWindows() ? '/D ' : ''; - // --dissociate option is only available since git 2.3.0-rc0 + $cachePath = $this->config->get('cache-vcs-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $url).'/'; $gitVersion = GitUtil::getVersion($this->process); - $msg = "Cloning ".$this->getShortHash($ref); - $command = 'git clone --no-checkout -- %url% %path% && cd '.$flag.'%path% && git remote add composer -- %url% && git fetch composer && git remote set-url origin -- %sanitizedUrl% && git remote set-url composer -- %sanitizedUrl%'; + // --dissociate option is only available since git 2.3.0-rc0 if ($gitVersion && version_compare($gitVersion, '2.3.0-rc0', '>=') && Cache::isUsable($cachePath)) { - $this->io->writeError('', true, IOInterface::DEBUG); + $this->io->writeError(" - Syncing " . $package->getName() . " (" . $package->getFullPrettyVersion() . ") into cache"); $this->io->writeError(sprintf(' Cloning to cache at %s', ProcessExecutor::escape($cachePath)), true, IOInterface::DEBUG); - try { - if (!$this->gitUtil->fetchRefOrSyncMirror($url, $cachePath, $ref)) { - $this->io->writeError('Failed to update '.$url.' in cache, package installation for '.$package->getPrettyName().' might fail.'); - } - if (is_dir($cachePath)) { - $command = - 'git clone --no-checkout %cachePath% %path% --dissociate --reference %cachePath% ' - . '&& cd '.$flag.'%path% ' - . '&& git remote set-url origin -- %sanitizedUrl% && git remote add composer -- %sanitizedUrl%'; - $msg = "Cloning ".$this->getShortHash($ref).' from cache'; - } - } catch (\RuntimeException $e) { - if (0 === strpos(get_class($e), 'PHPUnit')) { - throw $e; - } + $ref = $package->getSourceReference(); + if ($this->gitUtil->fetchRefOrSyncMirror($url, $cachePath, $ref) && is_dir($cachePath)) { + $this->cachedPackages[$package->getId()][$ref] = true; } + } elseif (null === $gitVersion) { + throw new \RuntimeException('git was not found in your PATH, skipping source download'); } + + return \React\Promise\resolve(); + } + + /** + * {@inheritDoc} + */ + protected function doInstall(PackageInterface $package, $path, $url) + { + GitUtil::cleanEnv(); + $path = $this->normalizePath($path); + $cachePath = $this->config->get('cache-vcs-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $url).'/'; + $ref = $package->getSourceReference(); + $flag = Platform::isWindows() ? '/D ' : ''; + + if (!empty($this->cachedPackages[$package->getId()][$ref])) { + $msg = "Cloning ".$this->getShortHash($ref).' from cache'; + $command = + 'git clone --no-checkout %cachePath% %path% --dissociate --reference %cachePath% ' + . '&& cd '.$flag.'%path% ' + . '&& git remote set-url origin -- %sanitizedUrl% && git remote add composer -- %sanitizedUrl%'; + } else { + $msg = "Cloning ".$this->getShortHash($ref); + $command = 'git clone --no-checkout -- %url% %path% && cd '.$flag.'%path% && git remote add composer -- %url% && git fetch composer && git remote set-url origin -- %sanitizedUrl% && git remote set-url composer -- %sanitizedUrl%'; + if (getenv('COMPOSER_DISABLE_NETWORK')) { + throw new \RuntimeException('The required git reference for '.$package->getName().' is not in cache and network is disabled, aborting'); + } + } + $this->io->writeError($msg); $commandCallable = function ($url) use ($path, $command, $cachePath) { @@ -100,39 +131,47 @@ public function doDownload(PackageInterface $package, $path, $url) } $package->setSourceReference($newRef); } + + return \React\Promise\resolve(); } /** * {@inheritDoc} */ - public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) + protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { GitUtil::cleanEnv(); + $path = $this->normalizePath($path); if (!$this->hasMetadataRepository($path)) { throw new \RuntimeException('The .git directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); } - $updateOriginUrl = false; - if ( - 0 === $this->process->execute('git remote -v', $output, $path) - && preg_match('{^origin\s+(?P\S+)}m', $output, $originMatch) - && preg_match('{^composer\s+(?P\S+)}m', $output, $composerMatch) - ) { - if ($originMatch['url'] === $composerMatch['url'] && $composerMatch['url'] !== $target->getSourceUrl()) { - $updateOriginUrl = true; + $cachePath = $this->config->get('cache-vcs-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $url).'/'; + $ref = $target->getSourceReference(); + + if (!empty($this->cachedPackages[$target->getId()][$ref])) { + $msg = "Checking out ".$this->getShortHash($ref).' from cache'; + $command = '(git rev-parse --quiet --verify %ref% || (git remote set-url composer -- %cachePath% && git fetch composer && git fetch --tags composer)) && git remote set-url composer -- %sanitizedUrl%'; + } else { + $msg = "Checking out ".$this->getShortHash($ref); + $command = '(git remote set-url composer -- %url% && git rev-parse --quiet --verify %ref% || (git fetch composer && git fetch --tags composer)) && git remote set-url composer -- %sanitizedUrl%'; + if (getenv('COMPOSER_DISABLE_NETWORK')) { + throw new \RuntimeException('The required git reference for '.$target->getName().' is not in cache and network is disabled, aborting'); } } - $ref = $target->getSourceReference(); - $this->io->writeError(" Checking out ".$this->getShortHash($ref)); - $command = '(git remote set-url composer -- %s && git rev-parse --quiet --verify %s || (git fetch composer && git fetch --tags composer)) && git remote set-url composer -- %s'; - - $commandCallable = function ($url) use ($command, $ref) { - return sprintf( - $command, - ProcessExecutor::escape($url), - ProcessExecutor::escape($ref.'^{commit}'), - ProcessExecutor::escape(preg_replace('{://([^@]+?):(.+?)@}', '://', $url)) + $this->io->writeError($msg); + + $commandCallable = function ($url) use ($ref, $command, $cachePath) { + return str_replace( + array('%url%', '%ref%', '%cachePath%', '%sanitizedUrl%'), + array( + ProcessExecutor::escape($url), + ProcessExecutor::escape($ref.'^{commit}'), + ProcessExecutor::escape($cachePath), + ProcessExecutor::escape(preg_replace('{://([^@]+?):(.+?)@}', '://', $url)), + ), + $command ); }; @@ -144,9 +183,21 @@ public function doUpdate(PackageInterface $initial, PackageInterface $target, $p $target->setSourceReference($newRef); } + $updateOriginUrl = false; + if ( + 0 === $this->process->execute('git remote -v', $output, $path) + && preg_match('{^origin\s+(?P\S+)}m', $output, $originMatch) + && preg_match('{^composer\s+(?P\S+)}m', $output, $composerMatch) + ) { + if ($originMatch['url'] === $composerMatch['url'] && $composerMatch['url'] !== $target->getSourceUrl()) { + $updateOriginUrl = true; + } + } if ($updateOriginUrl) { $this->updateOriginUrl($path, $target->getSourceUrl()); } + + return \React\Promise\resolve(); } /** @@ -156,7 +207,7 @@ public function getLocalChanges(PackageInterface $package, $path) { GitUtil::cleanEnv(); if (!$this->hasMetadataRepository($path)) { - return; + return null; } $command = 'git status --porcelain --untracked-files=no'; @@ -172,7 +223,7 @@ public function getUnpushedChanges(PackageInterface $package, $path) GitUtil::cleanEnv(); $path = $this->normalizePath($path); if (!$this->hasMetadataRepository($path)) { - return; + return null; } $command = 'git show-ref --head -d'; @@ -183,26 +234,32 @@ public function getUnpushedChanges(PackageInterface $package, $path) $refs = trim($output); if (!preg_match('{^([a-f0-9]+) HEAD$}mi', $refs, $match)) { // could not match the HEAD for some reason - return; + return null; } $headRef = $match[1]; if (!preg_match_all('{^'.$headRef.' refs/heads/(.+)$}mi', $refs, $matches)) { // not on a branch, we are either on a not-modified tag or some sort of detached head, so skip this - return; + return null; } + $candidateBranches = $matches[1]; // use the first match as branch name for now - $branch = $matches[1][0]; + $branch = $candidateBranches[0]; $unpushedChanges = null; + $branchNotFoundError = false; // do two passes, as if we find anything we want to fetch and then re-try for ($i = 0; $i <= 1; $i++) { - // try to find the a matching branch name in the composer remote - foreach ($matches[1] as $candidate) { - if (preg_match('{^[a-f0-9]+ refs/remotes/((?:composer|origin)/'.preg_quote($candidate).')$}mi', $refs, $match)) { - $branch = $candidate; - $remoteBranch = $match[1]; + $remoteBranches = array(); + + // try to find matching branch names in remote repos + foreach ($candidateBranches as $candidate) { + if (preg_match_all('{^[a-f0-9]+ refs/remotes/((?:[^/]+)/'.preg_quote($candidate).')$}mi', $refs, $matches)) { + foreach ($matches[1] as $match) { + $branch = $candidate; + $remoteBranches[] = $match; + } break; } } @@ -210,21 +267,40 @@ public function getUnpushedChanges(PackageInterface $package, $path) // if it doesn't exist, then we assume it is an unpushed branch // this is bad as we have no reference point to do a diff so we just bail listing // the branch as being unpushed - if (!isset($remoteBranch)) { - $unpushedChanges = 'Branch ' . $branch . ' could not be found on the origin remote and appears to be unpushed'; + if (!$remoteBranches) { + $unpushedChanges = 'Branch ' . $branch . ' could not be found on any remote and appears to be unpushed'; + $branchNotFoundError = true; } else { - $command = sprintf('git diff --name-status %s...%s --', $remoteBranch, $branch); - if (0 !== $this->process->execute($command, $output, $path)) { - throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); + // if first iteration found no remote branch but it has now found some, reset $unpushedChanges + // so we get the real diff output no matter its length + if ($branchNotFoundError) { + $unpushedChanges = null; } + foreach ($remoteBranches as $remoteBranch) { + $command = sprintf('git diff --name-status %s...%s --', $remoteBranch, $branch); + if (0 !== $this->process->execute($command, $output, $path)) { + throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); + } - $unpushedChanges = trim($output) ?: null; + $output = trim($output); + // keep the shortest diff from all remote branches we compare against + if ($unpushedChanges === null || strlen($output) < strlen($unpushedChanges)) { + $unpushedChanges = $output; + } + } } - // first pass and we found unpushed changes, fetch from both remotes to make sure we have up to date + // first pass and we found unpushed changes, fetch from all remotes to make sure we have up to date // remotes and then try again as outdated remotes can sometimes cause false-positives if ($unpushedChanges && $i === 0) { - $this->process->execute('git fetch composer && git fetch origin', $output, $path); + $this->process->execute('git fetch --all', $output, $path); + + // update list of refs after fetching + $command = 'git show-ref --head -d'; + if (0 !== $this->process->execute($command, $output, $path)) { + throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); + } + $refs = trim($output); } // abort after first pass if we didn't find anything @@ -272,7 +348,7 @@ protected function cleanChanges(PackageInterface $package, $path, $update) $changes = array_map(function ($elem) { return ' '.$elem; }, preg_split('{\s*\r?\n\s*}', $changes)); - $this->io->writeError(' The package has modified files:'); + $this->io->writeError(' '.$package->getPrettyName().' has modified files:'); $this->io->writeError(array_slice($changes, 0, 10)); if (count($changes) > 10) { $this->io->writeError(' ' . (count($changes) - 10) . ' more files modified, choose "v" to view the full list'); @@ -305,7 +381,7 @@ protected function cleanChanges(PackageInterface $package, $path, $update) case '?': default: - help: + help : $this->io->writeError(array( ' y - discard changes and apply the '.($update ? 'update' : 'uninstall'), ' n - abort the '.($update ? 'update' : 'uninstall').' and let you manually clean things up', @@ -327,15 +403,15 @@ protected function cleanChanges(PackageInterface $package, $path, $update) protected function reapplyChanges($path) { $path = $this->normalizePath($path); - if ($this->hasStashedChanges) { - $this->hasStashedChanges = false; + if (!empty($this->hasStashedChanges[$path])) { + unset($this->hasStashedChanges[$path]); $this->io->writeError(' Re-applying stashed changes'); if (0 !== $this->process->execute('git stash pop', $output, $path)) { throw new \RuntimeException("Failed to apply stashed changes:\n\n".$this->process->getErrorOutput()); } } - $this->hasDiscardedChanges = false; + unset($this->hasDiscardedChanges[$path]); } /** @@ -350,7 +426,7 @@ protected function reapplyChanges($path) */ protected function updateToCommit($path, $reference, $branch, $date) { - $force = $this->hasDiscardedChanges || $this->hasStashedChanges ? '-f ' : ''; + $force = !empty($this->hasDiscardedChanges[$path]) || !empty($this->hasStashedChanges[$path]) ? '-f ' : ''; // This uses the "--" sequence to separate branch from file parameters. // @@ -373,7 +449,7 @@ protected function updateToCommit($path, $reference, $branch, $date) ) { $command = sprintf('git checkout '.$force.'-B %s %s -- && git reset --hard %2$s --', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$reference)); if (0 === $this->process->execute($command, $output, $path)) { - return; + return null; } } @@ -386,19 +462,16 @@ protected function updateToCommit($path, $reference, $branch, $date) $command = sprintf('git checkout %s --', ProcessExecutor::escape($branch)); $fallbackCommand = sprintf('git checkout '.$force.'-B %s %s --', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$branch)); - if (0 === $this->process->execute($command, $output, $path) - || 0 === $this->process->execute($fallbackCommand, $output, $path) - ) { - $command = sprintf('git reset --hard %s --', ProcessExecutor::escape($reference)); - if (0 === $this->process->execute($command, $output, $path)) { - return; - } + $resetCommand = sprintf('git reset --hard %s --', ProcessExecutor::escape($reference)); + + if (0 === $this->process->execute("($command || $fallbackCommand) && $resetCommand", $output, $path)) { + return null; } } $command = sprintf($template, ProcessExecutor::escape($gitRef)); if (0 === $this->process->execute($command, $output, $path)) { - return; + return null; } // reference was not found (prints "fatal: reference is not a tree: $ref") @@ -406,7 +479,7 @@ protected function updateToCommit($path, $reference, $branch, $date) $this->io->writeError(' '.$reference.' is gone (history was rewritten?)'); } - throw new \RuntimeException(GitUtil::sanitizeUrl('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput())); + throw new \RuntimeException(Url::sanitize('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput())); } protected function updateOriginUrl($path, $url) @@ -445,42 +518,42 @@ protected function getCommitLogs($fromReference, $toReference, $path) } /** - * @param string $path + * @param string $path * @throws \RuntimeException */ protected function discardChanges($path) { $path = $this->normalizePath($path); if (0 !== $this->process->execute('git clean -df && git reset --hard', $output, $path)) { - throw new \RuntimeException("Could not reset changes\n\n:".$this->process->getErrorOutput()); + throw new \RuntimeException("Could not reset changes\n\n:".$output); } - $this->hasDiscardedChanges = true; + $this->hasDiscardedChanges[$path] = true; } /** - * @param string $path + * @param string $path * @throws \RuntimeException */ protected function stashChanges($path) { $path = $this->normalizePath($path); if (0 !== $this->process->execute('git stash --include-untracked', $output, $path)) { - throw new \RuntimeException("Could not stash changes\n\n:".$this->process->getErrorOutput()); + throw new \RuntimeException("Could not stash changes\n\n:".$output); } - $this->hasStashedChanges = true; + $this->hasStashedChanges[$path] = true; } /** - * @param string $path + * @param string $path * @throws \RuntimeException */ protected function viewDiff($path) { $path = $this->normalizePath($path); if (0 !== $this->process->execute('git diff HEAD', $output, $path)) { - throw new \RuntimeException("Could not view diff\n\n:".$this->process->getErrorOutput()); + throw new \RuntimeException("Could not view diff\n\n:".$output); } $this->io->writeError($output); diff --git a/app/vendor/composer/composer/src/Composer/Downloader/GzipDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/GzipDownloader.php index b7c6de3f7..df0cc908e 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/GzipDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/GzipDownloader.php @@ -12,14 +12,9 @@ namespace Composer\Downloader; -use Composer\Config; -use Composer\Cache; -use Composer\EventDispatcher\EventDispatcher; use Composer\Package\PackageInterface; use Composer\Util\Platform; use Composer\Util\ProcessExecutor; -use Composer\Util\RemoteFilesystem; -use Composer\IO\IOInterface; /** * GZip archive downloader. @@ -28,31 +23,24 @@ */ class GzipDownloader extends ArchiveDownloader { - protected $process; - - public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null, RemoteFilesystem $rfs = null) - { - $this->process = $process ?: new ProcessExecutor($io); - parent::__construct($io, $config, $eventDispatcher, $cache, $rfs); - } - - protected function extract($file, $path) + protected function extract(PackageInterface $package, $file, $path) { - $targetFilepath = $path . DIRECTORY_SEPARATOR . basename(substr($file, 0, -3)); + $filename = pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_FILENAME); + $targetFilepath = $path . DIRECTORY_SEPARATOR . $filename; // Try to use gunzip on *nix if (!Platform::isWindows()) { $command = 'gzip -cd -- ' . ProcessExecutor::escape($file) . ' > ' . ProcessExecutor::escape($targetFilepath); if (0 === $this->process->execute($command, $ignoredOutput)) { - return; + return \React\Promise\resolve(); } if (extension_loaded('zlib')) { // Fallback to using the PHP extension. $this->extractUsingExt($file, $targetFilepath); - return; + return \React\Promise\resolve(); } $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); @@ -61,14 +49,8 @@ protected function extract($file, $path) // Windows version of PHP has built-in support of gzip functions $this->extractUsingExt($file, $targetFilepath); - } - /** - * {@inheritdoc} - */ - protected function getFileName(PackageInterface $package, $path) - { - return $path.'/'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME); + return \React\Promise\resolve(); } private function extractUsingExt($file, $targetFilepath) diff --git a/app/vendor/composer/composer/src/Composer/Downloader/HgDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/HgDownloader.php index 0be3374f6..f68ed8c8f 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/HgDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/HgDownloader.php @@ -24,7 +24,19 @@ class HgDownloader extends VcsDownloader /** * {@inheritDoc} */ - public function doDownload(PackageInterface $package, $path, $url) + protected function doDownload(PackageInterface $package, $path, $url, PackageInterface $prevPackage = null) + { + if (null === HgUtils::getVersion($this->process)) { + throw new \RuntimeException('hg was not found in your PATH, skipping source download'); + } + + return \React\Promise\resolve(); + } + + /** + * {@inheritDoc} + */ + protected function doInstall(PackageInterface $package, $path, $url) { $hgUtils = new HgUtils($this->io, $this->config, $this->process); @@ -39,12 +51,14 @@ public function doDownload(PackageInterface $package, $path, $url) if (0 !== $this->process->execute($command, $ignoredOutput, realpath($path))) { throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); } + + return \React\Promise\resolve(); } /** * {@inheritDoc} */ - public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) + protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { $hgUtils = new HgUtils($this->io, $this->config, $this->process); @@ -60,6 +74,8 @@ public function doUpdate(PackageInterface $initial, PackageInterface $target, $p }; $hgUtils->runCommand($command, $url, $path); + + return \React\Promise\resolve(); } /** diff --git a/app/vendor/composer/composer/src/Composer/Downloader/MaxFileSizeExceededException.php b/app/vendor/composer/composer/src/Composer/Downloader/MaxFileSizeExceededException.php new file mode 100644 index 000000000..b7adad1b6 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Downloader/MaxFileSizeExceededException.php @@ -0,0 +1,17 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Downloader; + +class MaxFileSizeExceededException extends TransportException +{ +} diff --git a/app/vendor/composer/composer/src/Composer/Downloader/PathDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/PathDownloader.php index 1e614b6bd..be1b1262d 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/PathDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/PathDownloader.php @@ -18,10 +18,11 @@ use Composer\Package\Version\VersionGuesser; use Composer\Package\Version\VersionParser; use Composer\Util\Platform; -use Composer\Util\ProcessExecutor; -use Composer\Util\Filesystem as ComposerFilesystem; +use Composer\Util\Filesystem; use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem; +use Composer\DependencyResolver\Operation\InstallOperation; +use Composer\DependencyResolver\Operation\UninstallOperation; /** * Download a package from a local path. @@ -37,8 +38,9 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter /** * {@inheritdoc} */ - public function download(PackageInterface $package, $path, $output = true) + public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null, $output = true) { + $path = Filesystem::trimTrailingSlash($path); $url = $package->getDistUrl(); $realUrl = realpath($url); if (false === $realUrl || !file_exists($realUrl) || !is_dir($realUrl)) { @@ -50,15 +52,7 @@ public function download(PackageInterface $package, $path, $output = true) } if (realpath($path) === $realUrl) { - if ($output) { - $this->io->writeError(sprintf( - ' - Installing %s (%s): Source already present', - $package->getName(), - $package->getFullPrettyVersion() - )); - } - - return; + return \React\Promise\resolve(); } if (strpos(realpath($path) . DIRECTORY_SEPARATOR, $realUrl . DIRECTORY_SEPARATOR) === 0) { @@ -74,49 +68,46 @@ public function download(PackageInterface $package, $path, $output = true) )); } - // Get the transport options with default values - $transportOptions = $package->getTransportOptions() + array('symlink' => null, 'relative' => true); + return \React\Promise\resolve(); + } - // When symlink transport option is null, both symlink and mirror are allowed - $currentStrategy = self::STRATEGY_SYMLINK; - $allowedStrategies = array(self::STRATEGY_SYMLINK, self::STRATEGY_MIRROR); + /** + * {@inheritdoc} + */ + public function install(PackageInterface $package, $path, $output = true) + { + $path = Filesystem::trimTrailingSlash($path); + $url = $package->getDistUrl(); + $realUrl = realpath($url); - $mirrorPathRepos = getenv('COMPOSER_MIRROR_PATH_REPOS'); - if ($mirrorPathRepos) { - $currentStrategy = self::STRATEGY_MIRROR; - } + if (realpath($path) === $realUrl) { + if ($output) { + $this->io->writeError(" - " . InstallOperation::format($package) . $this->getInstallOperationAppendix($package, $path)); + } - if (true === $transportOptions['symlink']) { - $currentStrategy = self::STRATEGY_SYMLINK; - $allowedStrategies = array(self::STRATEGY_SYMLINK); - } elseif (false === $transportOptions['symlink']) { - $currentStrategy = self::STRATEGY_MIRROR; - $allowedStrategies = array(self::STRATEGY_MIRROR); + return \React\Promise\resolve(); } - // Check we can use junctions safely if we are on Windows - if (Platform::isWindows() && self::STRATEGY_SYMLINK === $currentStrategy && !$this->safeJunctions()) { - $currentStrategy = self::STRATEGY_MIRROR; - $allowedStrategies = array(self::STRATEGY_MIRROR); - } + // Get the transport options with default values + $transportOptions = $package->getTransportOptions() + array('relative' => true); - $fileSystem = new Filesystem(); + list($currentStrategy, $allowedStrategies) = $this->computeAllowedStrategies($transportOptions); + + $symfonyFilesystem = new SymfonyFilesystem(); $this->filesystem->removeDirectory($path); if ($output) { - $this->io->writeError(sprintf( - ' - Installing %s (%s): ', - $package->getName(), - $package->getFullPrettyVersion() - ), false); + $this->io->writeError(" - " . InstallOperation::format($package).': ', false); } $isFallback = false; - if (self::STRATEGY_SYMLINK == $currentStrategy) { + if (self::STRATEGY_SYMLINK === $currentStrategy) { try { if (Platform::isWindows()) { // Implement symlinks as NTFS junctions on Windows - $this->io->writeError(sprintf('Junctioning from %s', $url), false); + if ($output) { + $this->io->writeError(sprintf('Junctioning from %s', $url), false); + } $this->filesystem->junction($realUrl, $path); } else { $absolutePath = $path; @@ -125,17 +116,21 @@ public function download(PackageInterface $package, $path, $output = true) } $shortestPath = $this->filesystem->findShortestPath($absolutePath, $realUrl); $path = rtrim($path, "/"); - $this->io->writeError(sprintf('Symlinking from %s', $url), false); + if ($output) { + $this->io->writeError(sprintf('Symlinking from %s', $url), false); + } if ($transportOptions['relative']) { - $fileSystem->symlink($shortestPath, $path); + $symfonyFilesystem->symlink($shortestPath, $path); } else { - $fileSystem->symlink($realUrl, $path); + $symfonyFilesystem->symlink($realUrl, $path); } } } catch (IOException $e) { if (in_array(self::STRATEGY_MIRROR, $allowedStrategies)) { - $this->io->writeError(''); - $this->io->writeError(' Symlink failed, fallback to use mirroring!'); + if ($output) { + $this->io->writeError(''); + $this->io->writeError(' Symlink failed, fallback to use mirroring!'); + } $currentStrategy = self::STRATEGY_MIRROR; $isFallback = true; } else { @@ -145,16 +140,21 @@ public function download(PackageInterface $package, $path, $output = true) } // Fallback if symlink failed or if symlink is not allowed for the package - if (self::STRATEGY_MIRROR == $currentStrategy) { - $fs = new ComposerFilesystem(); - $realUrl = $fs->normalizePath($realUrl); + if (self::STRATEGY_MIRROR === $currentStrategy) { + $realUrl = $this->filesystem->normalizePath($realUrl); - $this->io->writeError(sprintf('%sMirroring from %s', $isFallback ? ' ' : '', $url), false); + if ($output) { + $this->io->writeError(sprintf('%sMirroring from %s', $isFallback ? ' ' : '', $url), false); + } $iterator = new ArchivableFilesFinder($realUrl, array()); - $fileSystem->mirror($realUrl, $path, $iterator); + $symfonyFilesystem->mirror($realUrl, $path, $iterator); + } + + if ($output) { + $this->io->writeError(''); } - $this->io->writeError(''); + return \React\Promise\resolve(); } /** @@ -162,32 +162,44 @@ public function download(PackageInterface $package, $path, $output = true) */ public function remove(PackageInterface $package, $path, $output = true) { - $realUrl = realpath($package->getDistUrl()); - - if (realpath($path) === $realUrl) { - if ($output) { - $this->io->writeError(" - Removing " . $package->getName() . " (" . $package->getFullPrettyVersion() . "), source is still present in $path"); - } - - return; - } - + $path = Filesystem::trimTrailingSlash($path); /** + * realpath() may resolve Windows junctions to the source path, so we'll check for a junction first + * to prevent a false positive when checking if the dist and install paths are the same. + * See https://bugs.php.net/bug.php?id=77639 + * * For junctions don't blindly rely on Filesystem::removeDirectory as it may be overzealous. If a process * inadvertently locks the file the removal will fail, but it would fall back to recursive delete which * is disastrous within a junction. So in that case we have no other real choice but to fail hard. */ if (Platform::isWindows() && $this->filesystem->isJunction($path)) { if ($output) { - $this->io->writeError(" - Removing junction for " . $package->getName() . " (" . $package->getFullPrettyVersion() . ")"); + $this->io->writeError(" - " . UninstallOperation::format($package).", source is still present in $path"); } if (!$this->filesystem->removeJunction($path)) { $this->io->writeError(" Could not remove junction at " . $path . " - is another process locking it?"); throw new \RuntimeException('Could not reliably remove junction for package ' . $package->getName()); } - } else { - parent::remove($package, $path, $output); + + return \React\Promise\resolve(); } + + // ensure that the source path (dist url) is not the same as the install path, which + // can happen when using custom installers, see https://github.com/composer/composer/pull/9116 + // not using realpath here as we do not want to resolve the symlink to the original dist url + // it points to + $fs = new Filesystem; + $absPath = $fs->isAbsolutePath($path) ? $path : getcwd() . '/' . $path; + $absDistUrl = $fs->isAbsolutePath($package->getDistUrl()) ? $package->getDistUrl() : getcwd() . '/' . $package->getDistUrl(); + if ($fs->normalizePath($absPath) === $fs->normalizePath($absDistUrl)) { + if ($output) { + $this->io->writeError(" - " . UninstallOperation::format($package).", source is still present in $path"); + } + + return \React\Promise\resolve(); + } + + return parent::remove($package, $path, $output); } /** @@ -195,14 +207,71 @@ public function remove(PackageInterface $package, $path, $output = true) */ public function getVcsReference(PackageInterface $package, $path) { + $path = Filesystem::trimTrailingSlash($path); $parser = new VersionParser; - $guesser = new VersionGuesser($this->config, new ProcessExecutor($this->io), $parser); + $guesser = new VersionGuesser($this->config, $this->process, $parser); $dumper = new ArrayDumper; $packageConfig = $dumper->dump($package); if ($packageVersion = $guesser->guessVersion($packageConfig, $path)) { return $packageVersion['commit']; } + + return null; + } + + /** + * {@inheritDoc} + */ + protected function getInstallOperationAppendix(PackageInterface $package, $path) + { + $realUrl = realpath($package->getDistUrl()); + + if (realpath($path) === $realUrl) { + return ': Source already present'; + } + + list($currentStrategy) = $this->computeAllowedStrategies($package->getTransportOptions()); + + if ($currentStrategy === self::STRATEGY_SYMLINK) { + if (Platform::isWindows()) { + return ': Junctioning from '.$package->getDistUrl(); + } + + return ': Symlinking from '.$package->getDistUrl(); + } + + return ': Mirroring from '.$package->getDistUrl(); + } + + private function computeAllowedStrategies(array $transportOptions) + { + // When symlink transport option is null, both symlink and mirror are allowed + $currentStrategy = self::STRATEGY_SYMLINK; + $allowedStrategies = array(self::STRATEGY_SYMLINK, self::STRATEGY_MIRROR); + + $mirrorPathRepos = getenv('COMPOSER_MIRROR_PATH_REPOS'); + if ($mirrorPathRepos) { + $currentStrategy = self::STRATEGY_MIRROR; + } + + $symlinkOption = isset($transportOptions['symlink']) ? $transportOptions['symlink'] : null; + + if (true === $symlinkOption) { + $currentStrategy = self::STRATEGY_SYMLINK; + $allowedStrategies = array(self::STRATEGY_SYMLINK); + } elseif (false === $symlinkOption) { + $currentStrategy = self::STRATEGY_MIRROR; + $allowedStrategies = array(self::STRATEGY_MIRROR); + } + + // Check we can use junctions safely if we are on Windows + if (Platform::isWindows() && self::STRATEGY_SYMLINK === $currentStrategy && !$this->safeJunctions()) { + $currentStrategy = self::STRATEGY_MIRROR; + $allowedStrategies = array(self::STRATEGY_MIRROR); + } + + return array($currentStrategy, $allowedStrategies); } /** diff --git a/app/vendor/composer/composer/src/Composer/Downloader/PearPackageExtractor.php b/app/vendor/composer/composer/src/Composer/Downloader/PearPackageExtractor.php deleted file mode 100644 index 5eaf3edcd..000000000 --- a/app/vendor/composer/composer/src/Composer/Downloader/PearPackageExtractor.php +++ /dev/null @@ -1,266 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Downloader; - -use Composer\Util\Filesystem; - -/** - * Extractor for pear packages. - * - * Composer cannot rely on tar files structure when place it inside package target dir. Correct source files - * disposition must be read from package.xml - * This extract pear package source files to target dir. - * - * @author Alexey Prilipko - */ -class PearPackageExtractor -{ - private static $rolesWithoutPackageNamePrefix = array('php', 'script', 'www'); - /** @var Filesystem */ - private $filesystem; - private $file; - - public function __construct($file) - { - if (!is_file($file)) { - throw new \UnexpectedValueException('PEAR package file is not found at '.$file); - } - - $this->filesystem = new Filesystem(); - $this->file = $file; - } - - /** - * Installs PEAR source files according to package.xml definitions and removes extracted files - * - * @param string $target target install location. all source installation would be performed relative to target path. - * @param array $roles types of files to install. default role for PEAR source files are 'php'. - * @param array $vars used for replacement tasks - * @throws \RuntimeException - * @throws \UnexpectedValueException - */ - public function extractTo($target, array $roles = array('php' => '/', 'script' => '/bin'), $vars = array()) - { - $extractionPath = $target.'/tarball'; - - try { - $archive = new \PharData($this->file); - $archive->extractTo($extractionPath, null, true); - - if (!is_file($this->combine($extractionPath, '/package.xml'))) { - throw new \RuntimeException('Invalid PEAR package. It must contain package.xml file.'); - } - - $fileCopyActions = $this->buildCopyActions($extractionPath, $roles, $vars); - $this->copyFiles($fileCopyActions, $extractionPath, $target, $roles, $vars); - $this->filesystem->removeDirectory($extractionPath); - } catch (\Exception $exception) { - throw new \UnexpectedValueException(sprintf('Failed to extract PEAR package %s to %s. Reason: %s', $this->file, $target, $exception->getMessage()), 0, $exception); - } - } - - /** - * Perform copy actions on files - * - * @param array $files array of copy actions ('from', 'to') with relative paths - * @param string $source path to source dir. - * @param string $target path to destination dir - * @param array $roles array [role => roleRoot] relative root for files having that role - * @param array $vars list of values can be used for replacement tasks - */ - private function copyFiles($files, $source, $target, $roles, $vars) - { - foreach ($files as $file) { - $from = $this->combine($source, $file['from']); - $to = $this->combine($target, $roles[$file['role']]); - $to = $this->combine($to, $file['to']); - $tasks = $file['tasks']; - $this->copyFile($from, $to, $tasks, $vars); - } - } - - private function copyFile($from, $to, $tasks, $vars) - { - if (!is_file($from)) { - throw new \RuntimeException('Invalid PEAR package. package.xml defines file that is not located inside tarball.'); - } - - $this->filesystem->ensureDirectoryExists(dirname($to)); - - if (0 == count($tasks)) { - $copied = copy($from, $to); - } else { - $content = file_get_contents($from); - $replacements = array(); - foreach ($tasks as $task) { - $pattern = $task['from']; - $varName = $task['to']; - if (isset($vars[$varName])) { - if ($varName === 'php_bin' && false === strpos($to, '.bat')) { - $replacements[$pattern] = preg_replace('{\.bat$}', '', $vars[$varName]); - } else { - $replacements[$pattern] = $vars[$varName]; - } - } - } - $content = strtr($content, $replacements); - - $copied = file_put_contents($to, $content); - } - - if (false === $copied) { - throw new \RuntimeException(sprintf('Failed to copy %s to %s', $from, $to)); - } - } - - /** - * Builds list of copy and list of remove actions that would transform extracted PEAR tarball into installed package. - * - * @param string $source string path to extracted files - * @param array $roles array [role => roleRoot] relative root for files having that role - * @param array $vars list of values can be used for replacement tasks - * @throws \RuntimeException - * @return array array of 'source' => 'target', where source is location of file in the tarball (relative to source - * path, and target is destination of file (also relative to $source path) - */ - private function buildCopyActions($source, array $roles, $vars) - { - /** @var \SimpleXmlElement $package */ - $package = simplexml_load_string(file_get_contents($this->combine($source, 'package.xml'))); - if (false === $package) { - throw new \RuntimeException('Package definition file is not valid.'); - } - - $packageSchemaVersion = $package['version']; - if ('1.0' == $packageSchemaVersion) { - $children = $package->release->filelist->children(); - $packageName = (string) $package->name; - $packageVersion = (string) $package->release->version; - $sourceDir = $packageName . '-' . $packageVersion; - $result = $this->buildSourceList10($children, $roles, $sourceDir, '', null, $packageName); - } elseif ('2.0' == $packageSchemaVersion || '2.1' == $packageSchemaVersion) { - $children = $package->contents->children(); - $packageName = (string) $package->name; - $packageVersion = (string) $package->version->release; - $sourceDir = $packageName . '-' . $packageVersion; - $result = $this->buildSourceList20($children, $roles, $sourceDir, '', null, $packageName); - - $namespaces = $package->getNamespaces(); - $package->registerXPathNamespace('ns', $namespaces['']); - $releaseNodes = $package->xpath('ns:phprelease'); - $this->applyRelease($result, $releaseNodes, $vars); - } else { - throw new \RuntimeException('Unsupported schema version of package definition file.'); - } - - return $result; - } - - private function applyRelease(&$actions, $releaseNodes, $vars) - { - foreach ($releaseNodes as $releaseNode) { - $requiredOs = $releaseNode->installconditions && $releaseNode->installconditions->os && $releaseNode->installconditions->os->name ? (string) $releaseNode->installconditions->os->name : ''; - if ($requiredOs && $vars['os'] != $requiredOs) { - continue; - } - - if ($releaseNode->filelist) { - foreach ($releaseNode->filelist->children() as $action) { - if ('install' == $action->getName()) { - $name = (string) $action['name']; - $as = (string) $action['as']; - if (isset($actions[$name])) { - $actions[$name]['to'] = $as; - } - } elseif ('ignore' == $action->getName()) { - $name = (string) $action['name']; - unset($actions[$name]); - } else { - // unknown action - } - } - } - break; - } - } - - private function buildSourceList10($children, $targetRoles, $source, $target, $role, $packageName) - { - $result = array(); - - // enumerating files - foreach ($children as $child) { - /** @var $child \SimpleXMLElement */ - if ($child->getName() == 'dir') { - $dirSource = $this->combine($source, (string) $child['name']); - $dirTarget = $child['baseinstalldir'] ?: $target; - $dirRole = $child['role'] ?: $role; - $dirFiles = $this->buildSourceList10($child->children(), $targetRoles, $dirSource, $dirTarget, $dirRole, $packageName); - $result = array_merge($result, $dirFiles); - } elseif ($child->getName() == 'file') { - $fileRole = (string) $child['role'] ?: $role; - if (isset($targetRoles[$fileRole])) { - $fileName = (string) ($child['name'] ?: $child[0]); // $child[0] means text content - $fileSource = $this->combine($source, $fileName); - $fileTarget = $this->combine((string) $child['baseinstalldir'] ?: $target, $fileName); - if (!in_array($fileRole, self::$rolesWithoutPackageNamePrefix)) { - $fileTarget = $packageName . '/' . $fileTarget; - } - $result[(string) $child['name']] = array('from' => $fileSource, 'to' => $fileTarget, 'role' => $fileRole, 'tasks' => array()); - } - } - } - - return $result; - } - - private function buildSourceList20($children, $targetRoles, $source, $target, $role, $packageName) - { - $result = array(); - - // enumerating files - foreach ($children as $child) { - /** @var $child \SimpleXMLElement */ - if ('dir' == $child->getName()) { - $dirSource = $this->combine($source, $child['name']); - $dirTarget = $child['baseinstalldir'] ?: $target; - $dirRole = $child['role'] ?: $role; - $dirFiles = $this->buildSourceList20($child->children(), $targetRoles, $dirSource, $dirTarget, $dirRole, $packageName); - $result = array_merge($result, $dirFiles); - } elseif ('file' == $child->getName()) { - $fileRole = (string) $child['role'] ?: $role; - if (isset($targetRoles[$fileRole])) { - $fileSource = $this->combine($source, (string) $child['name']); - $fileTarget = $this->combine((string) ($child['baseinstalldir'] ?: $target), (string) $child['name']); - $fileTasks = array(); - foreach ($child->children('http://pear.php.net/dtd/tasks-1.0') as $taskNode) { - if ('replace' == $taskNode->getName()) { - $fileTasks[] = array('from' => (string) $taskNode->attributes()->from, 'to' => (string) $taskNode->attributes()->to); - } - } - if (!in_array($fileRole, self::$rolesWithoutPackageNamePrefix)) { - $fileTarget = $packageName . '/' . $fileTarget; - } - $result[(string) $child['name']] = array('from' => $fileSource, 'to' => $fileTarget, 'role' => $fileRole, 'tasks' => $fileTasks); - } - } - } - - return $result; - } - - private function combine($left, $right) - { - return rtrim($left, '/') . '/' . ltrim($right, '/'); - } -} diff --git a/app/vendor/composer/composer/src/Composer/Downloader/PerforceDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/PerforceDownloader.php index 92091e2c9..dc30f8361 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/PerforceDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/PerforceDownloader.php @@ -27,7 +27,15 @@ class PerforceDownloader extends VcsDownloader /** * {@inheritDoc} */ - public function doDownload(PackageInterface $package, $path, $url) + protected function doDownload(PackageInterface $package, $path, $url, PackageInterface $prevPackage = null) + { + return \React\Promise\resolve(); + } + + /** + * {@inheritDoc} + */ + public function doInstall(PackageInterface $package, $path, $url) { $ref = $package->getSourceReference(); $label = $this->getLabelFromSourceReference($ref); @@ -40,6 +48,8 @@ public function doDownload(PackageInterface $package, $path, $url) $this->perforce->connectClient(); $this->perforce->syncCodeBase($label); $this->perforce->cleanupClientSpec(); + + return \React\Promise\resolve(); } private function getLabelFromSourceReference($ref) @@ -76,9 +86,9 @@ private function getRepoConfig(VcsRepository $repository) /** * {@inheritDoc} */ - public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) + protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { - $this->doDownload($target, $path, $url); + return $this->doInstall($target, $path, $url); } /** @@ -86,7 +96,9 @@ public function doUpdate(PackageInterface $initial, PackageInterface $target, $p */ public function getLocalChanges(PackageInterface $package, $path) { - $this->io->writeError('Perforce driver does not check for local changes before overriding', true); + $this->io->writeError('Perforce driver does not check for local changes before overriding'); + + return null; } /** diff --git a/app/vendor/composer/composer/src/Composer/Downloader/PharDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/PharDownloader.php index 13fec244b..89ef4363f 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/PharDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/PharDownloader.php @@ -12,6 +12,8 @@ namespace Composer\Downloader; +use Composer\Package\PackageInterface; + /** * Downloader for phar files * @@ -22,7 +24,7 @@ class PharDownloader extends ArchiveDownloader /** * {@inheritDoc} */ - protected function extract($file, $path) + protected function extract(PackageInterface $package, $file, $path) { // Can throw an UnexpectedValueException $archive = new \Phar($file); @@ -32,5 +34,7 @@ protected function extract($file, $path) * https://github.com/koto/phar-util * http://blog.kotowicz.net/2010/08/hardening-php-how-to-securely-include.html */ + + return \React\Promise\resolve(); } } diff --git a/app/vendor/composer/composer/src/Composer/Downloader/RarDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/RarDownloader.php index 1a4a39619..e11366421 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/RarDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/RarDownloader.php @@ -12,14 +12,10 @@ namespace Composer\Downloader; -use Composer\Config; -use Composer\Cache; -use Composer\EventDispatcher\EventDispatcher; use Composer\Util\IniHelper; use Composer\Util\Platform; use Composer\Util\ProcessExecutor; -use Composer\Util\RemoteFilesystem; -use Composer\IO\IOInterface; +use Composer\Package\PackageInterface; use RarArchive; /** @@ -31,15 +27,7 @@ */ class RarDownloader extends ArchiveDownloader { - protected $process; - - public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null, RemoteFilesystem $rfs = null) - { - $this->process = $process ?: new ProcessExecutor($io); - parent::__construct($io, $config, $eventDispatcher, $cache, $rfs); - } - - protected function extract($file, $path) + protected function extract(PackageInterface $package, $file, $path) { $processError = null; @@ -48,7 +36,7 @@ protected function extract($file, $path) $command = 'unrar x -- ' . ProcessExecutor::escape($file) . ' ' . ProcessExecutor::escape($path) . ' >/dev/null && chmod -R u+w ' . ProcessExecutor::escape($path); if (0 === $this->process->execute($command, $ignoredOutput)) { - return; + return \React\Promise\resolve(); } $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); @@ -87,5 +75,7 @@ protected function extract($file, $path) } $rarArchive->close(); + + return \React\Promise\resolve(); } } diff --git a/app/vendor/composer/composer/src/Composer/Downloader/SvnDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/SvnDownloader.php index 4c26a6050..f36b04917 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/SvnDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/SvnDownloader.php @@ -23,12 +23,27 @@ */ class SvnDownloader extends VcsDownloader { + /** @var bool */ protected $cacheCredentials = true; /** * {@inheritDoc} */ - public function doDownload(PackageInterface $package, $path, $url) + protected function doDownload(PackageInterface $package, $path, $url, PackageInterface $prevPackage = null) + { + SvnUtil::cleanEnv(); + $util = new SvnUtil($url, $this->io, $this->config, $this->process); + if (null === $util->binaryVersion()) { + throw new \RuntimeException('svn was not found in your PATH, skipping source download'); + } + + return \React\Promise\resolve(); + } + + /** + * {@inheritDoc} + */ + protected function doInstall(PackageInterface $package, $path, $url) { SvnUtil::cleanEnv(); $ref = $package->getSourceReference(); @@ -42,13 +57,15 @@ public function doDownload(PackageInterface $package, $path, $url) } $this->io->writeError(" Checking out ".$package->getSourceReference()); - $this->execute($url, "svn co", sprintf("%s/%s", $url, $ref), null, $path); + $this->execute($package, $url, "svn co", sprintf("%s/%s", $url, $ref), null, $path); + + return \React\Promise\resolve(); } /** * {@inheritDoc} */ - public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) + protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { SvnUtil::cleanEnv(); $ref = $target->getSourceReference(); @@ -57,14 +74,16 @@ public function doUpdate(PackageInterface $initial, PackageInterface $target, $p throw new \RuntimeException('The .svn directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); } - $util = new SvnUtil($url, $this->io, $this->config); + $util = new SvnUtil($url, $this->io, $this->config, $this->process); $flags = ""; if (version_compare($util->binaryVersion(), '1.7.0', '>=')) { $flags .= ' --ignore-ancestry'; } $this->io->writeError(" Checking out " . $ref); - $this->execute($url, "svn switch" . $flags, sprintf("%s/%s", $url, $ref), $path); + $this->execute($target, $url, "svn switch" . $flags, sprintf("%s/%s", $url, $ref), $path); + + return \React\Promise\resolve(); } /** @@ -93,15 +112,15 @@ public function getLocalChanges(PackageInterface $package, $path) * @throws \RuntimeException * @return string */ - protected function execute($baseUrl, $command, $url, $cwd = null, $path = null) + protected function execute(PackageInterface $package, $baseUrl, $command, $url, $cwd = null, $path = null) { - $util = new SvnUtil($baseUrl, $this->io, $this->config); + $util = new SvnUtil($baseUrl, $this->io, $this->config, $this->process); $util->setCacheCredentials($this->cacheCredentials); try { return $util->execute($command, $url, $cwd, $path, $this->io->isVerbose()); } catch (\RuntimeException $e) { throw new \RuntimeException( - 'Package could not be downloaded, '.$e->getMessage() + $package->getPrettyName().' could not be downloaded, '.$e->getMessage() ); } } @@ -127,7 +146,7 @@ protected function cleanChanges(PackageInterface $package, $path, $update) return ' '.$elem; }, preg_split('{\s*\r?\n\s*}', $changes)); $countChanges = count($changes); - $this->io->writeError(sprintf(' The package has modified file%s:', $countChanges === 1 ? '' : 's')); + $this->io->writeError(sprintf(' '.$package->getPrettyName().' has modified file%s:', $countChanges === 1 ? '' : 's')); $this->io->writeError(array_slice($changes, 0, 10)); if ($countChanges > 10) { $remainingChanges = $countChanges - 10; @@ -170,7 +189,7 @@ protected function cleanChanges(PackageInterface $package, $path, $update) */ protected function getCommitLogs($fromReference, $toReference, $path) { - if (preg_match('{.*@(\d+)$}', $fromReference) && preg_match('{.*@(\d+)$}', $toReference)) { + if (preg_match('{@(\d+)$}', $fromReference) && preg_match('{@(\d+)$}', $toReference)) { // retrieve the svn base url from the checkout folder $command = sprintf('svn info --non-interactive --xml -- %s', ProcessExecutor::escape($path)); if (0 !== $this->process->execute($command, $output, $path)) { @@ -194,7 +213,7 @@ protected function getCommitLogs($fromReference, $toReference, $path) $command = sprintf('svn log -r%s:%s --incremental', ProcessExecutor::escape($fromRevision), ProcessExecutor::escape($toRevision)); - $util = new SvnUtil($baseUrl, $this->io, $this->config); + $util = new SvnUtil($baseUrl, $this->io, $this->config, $this->process); $util->setCacheCredentials($this->cacheCredentials); try { return $util->executeLocal($command, $path, null, $this->io->isVerbose()); diff --git a/app/vendor/composer/composer/src/Composer/Downloader/TarDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/TarDownloader.php index 34c43da5f..65346030e 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/TarDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/TarDownloader.php @@ -12,6 +12,8 @@ namespace Composer\Downloader; +use Composer\Package\PackageInterface; + /** * Downloader for tar files: tar, tar.gz or tar.bz2 * @@ -22,10 +24,12 @@ class TarDownloader extends ArchiveDownloader /** * {@inheritDoc} */ - protected function extract($file, $path) + protected function extract(PackageInterface $package, $file, $path) { // Can throw an UnexpectedValueException $archive = new \PharData($file); $archive->extractTo($path, null, true); + + return \React\Promise\resolve(); } } diff --git a/app/vendor/composer/composer/src/Composer/Downloader/TransportException.php b/app/vendor/composer/composer/src/Composer/Downloader/TransportException.php index c682df080..a478e5807 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/TransportException.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/TransportException.php @@ -17,9 +17,14 @@ */ class TransportException extends \RuntimeException { + /** @var ?array */ protected $headers; + /** @var ?string */ protected $response; + /** @var ?int */ protected $statusCode; + /** @var ?array */ + protected $responseInfo = array(); public function setHeaders($headers) { @@ -50,4 +55,20 @@ public function getStatusCode() { return $this->statusCode; } + + /** + * @return array + */ + public function getResponseInfo() + { + return $this->responseInfo; + } + + /** + * @param array $responseInfo + */ + public function setResponseInfo(array $responseInfo) + { + $this->responseInfo = $responseInfo; + } } diff --git a/app/vendor/composer/composer/src/Composer/Downloader/VcsDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/VcsDownloader.php index aa666058e..53c573679 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/VcsDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/VcsDownloader.php @@ -20,6 +20,10 @@ use Composer\Util\ProcessExecutor; use Composer\IO\IOInterface; use Composer\Util\Filesystem; +use React\Promise\PromiseInterface; +use Composer\DependencyResolver\Operation\UpdateOperation; +use Composer\DependencyResolver\Operation\InstallOperation; +use Composer\DependencyResolver\Operation\UninstallOperation; /** * @author Jordi Boggiano @@ -34,6 +38,8 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa protected $process; /** @var Filesystem */ protected $filesystem; + /** @var array */ + protected $hasCleanedChanges = array(); public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, Filesystem $fs = null) { @@ -54,44 +60,85 @@ public function getInstallationSource() /** * {@inheritDoc} */ - public function download(PackageInterface $package, $path) + public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null) { if (!$package->getSourceReference()) { throw new \InvalidArgumentException('Package '.$package->getPrettyName().' is missing reference information'); } - $this->io->writeError(" - Installing " . $package->getName() . " (" . $package->getFullPrettyVersion() . "): ", false); - $this->filesystem->emptyDirectory($path); + $urls = $this->prepareUrls($package->getSourceUrls()); - $urls = $package->getSourceUrls(); while ($url = array_shift($urls)) { try { - if (Filesystem::isLocalPath($url)) { - // realpath() below will not understand - // url that starts with "file://" - $needle = 'file://'; - $isFileProtocol = false; - if (0 === strpos($url, $needle)) { - $url = substr($url, strlen($needle)); - $isFileProtocol = true; - } - - // realpath() below will not understand %20 spaces etc. - if (false !== strpos($url, '%')) { - $url = rawurldecode($url); - } - - $url = realpath($url); - - if ($isFileProtocol) { - $url = $needle . $url; - } + return $this->doDownload($package, $path, $url, $prevPackage); + } catch (\Exception $e) { + // rethrow phpunit exceptions to avoid hard to debug bug failures + if ($e instanceof \PHPUnit\Framework\Exception) { + throw $e; + } + if ($this->io->isDebug()) { + $this->io->writeError('Failed: ['.get_class($e).'] '.$e->getMessage()); + } elseif (count($urls)) { + $this->io->writeError(' Failed, trying the next URL'); + } + if (!count($urls)) { + throw $e; } - $this->doDownload($package, $path, $url); + } + } + + return \React\Promise\resolve(); + } + + /** + * {@inheritDoc} + */ + public function prepare($type, PackageInterface $package, $path, PackageInterface $prevPackage = null) + { + if ($type === 'update') { + $this->cleanChanges($prevPackage, $path, true); + $this->hasCleanedChanges[$prevPackage->getUniqueName()] = true; + } elseif ($type === 'install') { + $this->filesystem->emptyDirectory($path); + } elseif ($type === 'uninstall') { + $this->cleanChanges($package, $path, false); + } + + return \React\Promise\resolve(); + } + + /** + * {@inheritDoc} + */ + public function cleanup($type, PackageInterface $package, $path, PackageInterface $prevPackage = null) + { + if ($type === 'update' && isset($this->hasCleanedChanges[$prevPackage->getUniqueName()])) { + $this->reapplyChanges($path); + unset($this->hasCleanedChanges[$prevPackage->getUniqueName()]); + } + + return \React\Promise\resolve(); + } + + /** + * {@inheritDoc} + */ + public function install(PackageInterface $package, $path) + { + if (!$package->getSourceReference()) { + throw new \InvalidArgumentException('Package '.$package->getPrettyName().' is missing reference information'); + } + + $this->io->writeError(" - " . InstallOperation::format($package).': ', false); + + $urls = $this->prepareUrls($package->getSourceUrls()); + while ($url = array_shift($urls)) { + try { + $this->doInstall($package, $path, $url); break; } catch (\Exception $e) { // rethrow phpunit exceptions to avoid hard to debug bug failures - if ($e instanceof \PHPUnit_Framework_Exception) { + if ($e instanceof \PHPUnit\Framework\Exception) { throw $e; } if ($this->io->isDebug()) { @@ -104,6 +151,8 @@ public function download(PackageInterface $package, $path) } } } + + return \React\Promise\resolve(); } /** @@ -115,40 +164,20 @@ public function update(PackageInterface $initial, PackageInterface $target, $pat throw new \InvalidArgumentException('Package '.$target->getPrettyName().' is missing reference information'); } - $name = $target->getName(); - if ($initial->getPrettyVersion() == $target->getPrettyVersion()) { - if ($target->getSourceType() === 'svn') { - $from = $initial->getSourceReference(); - $to = $target->getSourceReference(); - } else { - $from = substr($initial->getSourceReference(), 0, 7); - $to = substr($target->getSourceReference(), 0, 7); - } - $name .= ' '.$initial->getPrettyVersion(); - } else { - $from = $initial->getFullPrettyVersion(); - $to = $target->getFullPrettyVersion(); - } - - $actionName = VersionParser::isUpgrade($initial->getVersion(), $target->getVersion()) ? 'Updating' : 'Downgrading'; - $this->io->writeError(" - " . $actionName . " " . $name . " (" . $from . " => " . $to . "): ", false); + $this->io->writeError(" - " . UpdateOperation::format($initial, $target).': ', false); - $this->cleanChanges($initial, $path, true); - $urls = $target->getSourceUrls(); + $urls = $this->prepareUrls($target->getSourceUrls()); $exception = null; while ($url = array_shift($urls)) { try { - if (Filesystem::isLocalPath($url)) { - $url = realpath($url); - } $this->doUpdate($initial, $target, $path, $url); $exception = null; break; } catch (\Exception $exception) { // rethrow phpunit exceptions to avoid hard to debug bug failures - if ($exception instanceof \PHPUnit_Framework_Exception) { + if ($exception instanceof \PHPUnit\Framework\Exception) { throw $exception; } if ($this->io->isDebug()) { @@ -159,8 +188,6 @@ public function update(PackageInterface $initial, PackageInterface $target, $pat } } - $this->reapplyChanges($path); - // print the commit logs if in verbose mode and VCS metadata is present // because in case of missing metadata code would trigger another exception if (!$exception && $this->io->isVerbose() && $this->hasMetadataRepository($path)) { @@ -188,6 +215,8 @@ public function update(PackageInterface $initial, PackageInterface $target, $pat if (!$urls && $exception) { throw $exception; } + + return \React\Promise\resolve(); } /** @@ -195,20 +224,15 @@ public function update(PackageInterface $initial, PackageInterface $target, $pat */ public function remove(PackageInterface $package, $path) { - $this->io->writeError(" - Removing " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); - $this->cleanChanges($package, $path, false); - if (!$this->filesystem->removeDirectory($path)) { - throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); - } - } + $this->io->writeError(" - " . UninstallOperation::format($package)); - /** - * Download progress information is not available for all VCS downloaders. - * {@inheritDoc} - */ - public function setOutputProgress($outputProgress) - { - return $this; + $promise = $this->filesystem->removeDirectoryAsync($path); + + return $promise->then(function ($result) use ($path) { + if (!$result) { + throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); + } + }); } /** @@ -224,6 +248,8 @@ public function getVcsReference(PackageInterface $package, $path) if ($packageVersion = $guesser->guessVersion($packageConfig, $path)) { return $packageVersion['commit']; } + + return null; } /** @@ -241,10 +267,12 @@ protected function cleanChanges(PackageInterface $package, $path, $update) if (null !== $this->getLocalChanges($package, $path)) { throw new \RuntimeException('Source directory ' . $path . ' has uncommitted changes.'); } + + return \React\Promise\resolve(); } /** - * Guarantee that no changes have been made to the local copy + * Reapply previously stashes changes if applicable, only called after an update (regardless if successful or not) * * @param string $path * @throws \RuntimeException in case the operation must be aborted or the patch does not apply cleanly @@ -253,14 +281,28 @@ protected function reapplyChanges($path) { } + /** + * Downloads data needed to run an install/update later + * + * @param PackageInterface $package package instance + * @param string $path download path + * @param string $url package url + * @param PackageInterface|null $prevPackage previous package (in case of an update) + * + * @return PromiseInterface|null + */ + abstract protected function doDownload(PackageInterface $package, $path, $url, PackageInterface $prevPackage = null); + /** * Downloads specific package into specific folder. * * @param PackageInterface $package package instance * @param string $path download path * @param string $url package url + * + * @return PromiseInterface|null */ - abstract protected function doDownload(PackageInterface $package, $path, $url); + abstract protected function doInstall(PackageInterface $package, $path, $url); /** * Updates specific package in specific folder from initial to target version. @@ -269,6 +311,8 @@ abstract protected function doDownload(PackageInterface $package, $path, $url); * @param PackageInterface $target updated package * @param string $path download path * @param string $url package url + * + * @return PromiseInterface|null */ abstract protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url); @@ -290,4 +334,36 @@ abstract protected function getCommitLogs($fromReference, $toReference, $path); * @return bool */ abstract protected function hasMetadataRepository($path); + + /** + * @return string[] + */ + private function prepareUrls(array $urls) + { + foreach ($urls as $index => $url) { + if (Filesystem::isLocalPath($url)) { + // realpath() below will not understand + // url that starts with "file://" + $fileProtocol = 'file://'; + $isFileProtocol = false; + if (0 === strpos($url, $fileProtocol)) { + $url = substr($url, strlen($fileProtocol)); + $isFileProtocol = true; + } + + // realpath() below will not understand %20 spaces etc. + if (false !== strpos($url, '%')) { + $url = rawurldecode($url); + } + + $urls[$index] = realpath($url); + + if ($isFileProtocol) { + $urls[$index] = $fileProtocol . $urls[$index]; + } + } + } + + return $urls; + } } diff --git a/app/vendor/composer/composer/src/Composer/Downloader/XzDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/XzDownloader.php index 4a9b854d3..7e69a52bc 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/XzDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/XzDownloader.php @@ -12,13 +12,8 @@ namespace Composer\Downloader; -use Composer\Config; -use Composer\Cache; -use Composer\EventDispatcher\EventDispatcher; use Composer\Package\PackageInterface; use Composer\Util\ProcessExecutor; -use Composer\Util\RemoteFilesystem; -use Composer\IO\IOInterface; /** * Xz archive downloader. @@ -28,33 +23,16 @@ */ class XzDownloader extends ArchiveDownloader { - protected $process; - - public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null, RemoteFilesystem $rfs = null) - { - $this->process = $process ?: new ProcessExecutor($io); - - parent::__construct($io, $config, $eventDispatcher, $cache, $rfs); - } - - protected function extract($file, $path) + protected function extract(PackageInterface $package, $file, $path) { $command = 'tar -xJf ' . ProcessExecutor::escape($file) . ' -C ' . ProcessExecutor::escape($path); if (0 === $this->process->execute($command, $ignoredOutput)) { - return; + return \React\Promise\resolve(); } $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(); throw new \RuntimeException($processError); } - - /** - * {@inheritdoc} - */ - protected function getFileName(PackageInterface $package, $path) - { - return $path.'/'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME); - } } diff --git a/app/vendor/composer/composer/src/Composer/Downloader/ZipDownloader.php b/app/vendor/composer/composer/src/Composer/Downloader/ZipDownloader.php index 8aca21e59..2a93c9f9c 100644 --- a/app/vendor/composer/composer/src/Composer/Downloader/ZipDownloader.php +++ b/app/vendor/composer/composer/src/Composer/Downloader/ZipDownloader.php @@ -12,16 +12,12 @@ namespace Composer\Downloader; -use Composer\Config; -use Composer\Cache; -use Composer\EventDispatcher\EventDispatcher; use Composer\Package\PackageInterface; use Composer\Util\IniHelper; use Composer\Util\Platform; use Composer\Util\ProcessExecutor; -use Composer\Util\RemoteFilesystem; -use Composer\IO\IOInterface; use Symfony\Component\Process\ExecutableFinder; +use React\Promise\PromiseInterface; use ZipArchive; /** @@ -29,37 +25,56 @@ */ class ZipDownloader extends ArchiveDownloader { - protected static $hasSystemUnzip; + /** @var array */ + private static $unzipCommands; + /** @var bool */ private static $hasZipArchive; + /** @var bool */ private static $isWindows; - protected $process; + /** @var ZipArchive|null */ private $zipArchiveObject; - public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null, RemoteFilesystem $rfs = null) - { - $this->process = $process ?: new ProcessExecutor($io); - parent::__construct($io, $config, $eventDispatcher, $cache, $rfs); - } - /** * {@inheritDoc} */ - public function download(PackageInterface $package, $path, $output = true) + public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null, $output = true) { - if (null === self::$hasSystemUnzip) { + if (null === self::$unzipCommands) { + self::$unzipCommands = array(); $finder = new ExecutableFinder; - self::$hasSystemUnzip = (bool) $finder->find('unzip'); + if (Platform::isWindows() && ($cmd = $finder->find('7z', null, array('C:\Program Files\7-Zip')))) { + self::$unzipCommands[] = array('7z', ProcessExecutor::escape($cmd).' x -bb0 -y %s -o%s'); + } + if ($cmd = $finder->find('unzip')) { + self::$unzipCommands[] = array('unzip', ProcessExecutor::escape($cmd).' -qq %s -d %s'); + } + if (!Platform::isWindows() && ($cmd = $finder->find('7z'))) { // 7z linux/macOS support is only used if unzip is not present + self::$unzipCommands[] = array('7z', ProcessExecutor::escape($cmd).' x -bb0 -y %s -o%s'); + } + if (!Platform::isWindows() && ($cmd = $finder->find('7zz'))) { // 7zz linux/macOS support is only used if unzip is not present + self::$unzipCommands[] = array('7zz', ProcessExecutor::escape($cmd).' x -bb0 -y %s -o%s'); + } + } + + $procOpenMissing = false; + if (!function_exists('proc_open')) { + self::$unzipCommands = array(); + $procOpenMissing = true; } if (null === self::$hasZipArchive) { self::$hasZipArchive = class_exists('ZipArchive'); } - if (!self::$hasZipArchive && !self::$hasSystemUnzip) { + if (!self::$hasZipArchive && !self::$unzipCommands) { // php.ini path is added to the error message to help users find the correct file $iniMessage = IniHelper::getMessage(); - $error = "The zip extension and unzip command are both missing, skipping.\n" . $iniMessage; + if ($procOpenMissing) { + $error = "The zip extension is missing and unzip/7z commands cannot be called as proc_open is disabled, skipping.\n" . $iniMessage; + } else { + $error = "The zip extension and unzip/7z commands are both missing, skipping.\n" . $iniMessage; + } throw new \RuntimeException($error); } @@ -67,85 +82,100 @@ public function download(PackageInterface $package, $path, $output = true) if (null === self::$isWindows) { self::$isWindows = Platform::isWindows(); - if (!self::$isWindows && !self::$hasSystemUnzip) { - $this->io->writeError("As there is no 'unzip' command installed zip files are being unpacked using the PHP zip extension."); - $this->io->writeError("This may cause invalid reports of corrupted archives. Besides, any UNIX permissions (e.g. executable) defined in the archives will be lost."); - $this->io->writeError("Installing 'unzip' may remediate them."); + if (!self::$isWindows && !self::$unzipCommands) { + if ($procOpenMissing) { + $this->io->writeError("proc_open is disabled so 'unzip' and '7z' commands cannot be used, zip files are being unpacked using the PHP zip extension."); + $this->io->writeError("This may cause invalid reports of corrupted archives. Besides, any UNIX permissions (e.g. executable) defined in the archives will be lost."); + $this->io->writeError("Enabling proc_open and installing 'unzip' or '7z' may remediate them."); + } else { + $this->io->writeError("As there is no 'unzip' nor '7z' command installed zip files are being unpacked using the PHP zip extension."); + $this->io->writeError("This may cause invalid reports of corrupted archives. Besides, any UNIX permissions (e.g. executable) defined in the archives will be lost."); + $this->io->writeError("Installing 'unzip' or '7z' may remediate them."); + } } } - return parent::download($package, $path, $output); + return parent::download($package, $path, $prevPackage, $output); } /** * extract $file to $path with "unzip" command * - * @param string $file File to extract - * @param string $path Path where to extract file - * @param bool $isLastChance If true it is called as a fallback and should throw an exception - * @return bool Success status + * @param string $file File to extract + * @param string $path Path where to extract file + * @return PromiseInterface */ - protected function extractWithSystemUnzip($file, $path, $isLastChance) + private function extractWithSystemUnzip(PackageInterface $package, $file, $path) { - if (!self::$hasZipArchive) { - // Force Exception throwing if the Other alternative is not available - $isLastChance = true; - } + // Force Exception throwing if the other alternative extraction method is not available + $isLastChance = !self::$hasZipArchive; - if (!self::$hasSystemUnzip && !$isLastChance) { + if (!self::$unzipCommands) { // This was call as the favorite extract way, but is not available // We switch to the alternative - return $this->extractWithZipArchive($file, $path, true); + return $this->extractWithZipArchive($package, $file, $path); } - $processError = null; - // When called after a ZipArchive failed, perhaps there is some files to overwrite - $overwrite = $isLastChance ? '-o' : ''; + $commandSpec = reset(self::$unzipCommands); + $command = sprintf($commandSpec[1], ProcessExecutor::escape($file), ProcessExecutor::escape($path)); + // normalize separators to backslashes to avoid problems with 7-zip on windows + // see https://github.com/composer/composer/issues/10058 + if (Platform::isWindows()) { + $command = sprintf($commandSpec[1], ProcessExecutor::escape(strtr($file, '/', '\\')), ProcessExecutor::escape(strtr($path, '/', '\\'))); + } - $command = 'unzip -qq '.$overwrite.' '.ProcessExecutor::escape($file).' -d '.ProcessExecutor::escape($path); + $executable = $commandSpec[0]; - try { - if (0 === $exitCode = $this->process->execute($command, $ignoredOutput)) { - return true; + $self = $this; + $io = $this->io; + $tryFallback = function ($processError) use ($isLastChance, $io, $self, $file, $path, $package, $executable) { + if ($isLastChance) { + throw $processError; } - $processError = new \RuntimeException('Failed to execute ('.$exitCode.') '.$command."\n\n".$this->process->getErrorOutput()); - } catch (\Exception $e) { - $processError = $e; - } + if (!is_file($file)) { + $io->writeError(' '.$processError->getMessage().''); + $io->writeError(' This most likely is due to a custom installer plugin not handling the returned Promise from the downloader'); + $io->writeError(' See https://github.com/composer/installers/commit/5006d0c28730ade233a8f42ec31ac68fb1c5c9bb for an example fix'); + } else { + $io->writeError(' '.$processError->getMessage().''); + $io->writeError(' The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems)'); + $io->writeError(' Unzip with '.$executable.' command failed, falling back to ZipArchive class'); + } - if ($isLastChance) { - throw $processError; - } + return $self->extractWithZipArchive($package, $file, $path); + }; - $this->io->writeError(' '.$processError->getMessage()); - $this->io->writeError(' The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems)'); - $this->io->writeError(' Unzip with unzip command failed, falling back to ZipArchive class'); + try { + $promise = $this->process->executeAsync($command); - return $this->extractWithZipArchive($file, $path, true); + return $promise->then(function ($process) use ($tryFallback, $command, $package, $file) { + if (!$process->isSuccessful()) { + $output = $process->getErrorOutput(); + $output = str_replace(', '.$file.'.zip or '.$file.'.ZIP', '', $output); + + return $tryFallback(new \RuntimeException('Failed to extract '.$package->getName().': ('.$process->getExitCode().') '.$command."\n\n".$output)); + } + }); + } catch (\Exception $e) { + return $tryFallback($e); + } catch (\Throwable $e) { + return $tryFallback($e); + } } /** * extract $file to $path with ZipArchive * - * @param string $file File to extract - * @param string $path Path where to extract file - * @param bool $isLastChance If true it is called as a fallback and should throw an exception - * @return bool Success status + * @param string $file File to extract + * @param string $path Path where to extract file + * @return PromiseInterface + * + * TODO v3 should make this private once we can drop PHP 5.3 support + * @protected */ - protected function extractWithZipArchive($file, $path, $isLastChance) + public function extractWithZipArchive(PackageInterface $package, $file, $path) { - if (!self::$hasSystemUnzip) { - // Force Exception throwing if the Other alternative is not available - $isLastChance = true; - } - - if (!self::$hasZipArchive && !$isLastChance) { - // This was call as the favorite extract way, but is not available - // We switch to the alternative - return $this->extractWithSystemUnzip($file, $path, true); - } - $processError = null; $zipArchive = $this->zipArchiveObject ?: new ZipArchive(); @@ -156,7 +186,7 @@ protected function extractWithZipArchive($file, $path, $isLastChance) if (true === $extractResult) { $zipArchive->close(); - return true; + return \React\Promise\resolve(); } $processError = new \RuntimeException(rtrim("There was an error extracting the ZIP file, it is either corrupted or using an invalid format.\n")); @@ -167,32 +197,26 @@ protected function extractWithZipArchive($file, $path, $isLastChance) $processError = new \RuntimeException('The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems): '.$e->getMessage(), 0, $e); } catch (\Exception $e) { $processError = $e; + } catch (\Throwable $e) { + $processError = $e; } - if ($isLastChance) { - throw $processError; - } - - $this->io->writeError(' '.$processError->getMessage()); - $this->io->writeError(' Unzip with ZipArchive class failed, falling back to unzip command'); - - return $this->extractWithSystemUnzip($file, $path, true); + throw $processError; } /** * extract $file to $path * - * @param string $file File to extract - * @param string $path Path where to extract file + * @param string $file File to extract + * @param string $path Path where to extract file + * @return PromiseInterface|null + * + * TODO v3 should make this private once we can drop PHP 5.3 support + * @protected */ - public function extract($file, $path) + public function extract(PackageInterface $package, $file, $path) { - // Each extract calls its alternative if not available or fails - if (self::$isWindows) { - $this->extractWithZipArchive($file, $path, false); - } else { - $this->extractWithSystemUnzip($file, $path, false); - } + return $this->extractWithSystemUnzip($package, $file, $path); } /** diff --git a/app/vendor/composer/composer/src/Composer/EventDispatcher/Event.php b/app/vendor/composer/composer/src/Composer/EventDispatcher/Event.php index 2091f6be0..51cfda4f8 100644 --- a/app/vendor/composer/composer/src/Composer/EventDispatcher/Event.php +++ b/app/vendor/composer/composer/src/Composer/EventDispatcher/Event.php @@ -25,12 +25,12 @@ class Event protected $name; /** - * @var array Arguments passed by the user, these will be forwarded to CLI script handlers + * @var string[] Arguments passed by the user, these will be forwarded to CLI script handlers */ protected $args; /** - * @var array Flags usable in PHP script handlers + * @var mixed[] Flags usable in PHP script handlers */ protected $flags; @@ -42,9 +42,9 @@ class Event /** * Constructor. * - * @param string $name The event name - * @param array $args Arguments passed by the user - * @param array $flags Optional flags to pass data not as argument + * @param string $name The event name + * @param string[] $args Arguments passed by the user + * @param mixed[] $flags Optional flags to pass data not as argument */ public function __construct($name, array $args = array(), array $flags = array()) { @@ -66,7 +66,7 @@ public function getName() /** * Returns the event's arguments. * - * @return array The event arguments + * @return string[] The event arguments */ public function getArguments() { @@ -76,7 +76,7 @@ public function getArguments() /** * Returns the event's flags. * - * @return array The event flags + * @return mixed[] The event flags */ public function getFlags() { diff --git a/app/vendor/composer/composer/src/Composer/EventDispatcher/EventDispatcher.php b/app/vendor/composer/composer/src/Composer/EventDispatcher/EventDispatcher.php index 52b26d98e..ca23a1bdd 100644 --- a/app/vendor/composer/composer/src/Composer/EventDispatcher/EventDispatcher.php +++ b/app/vendor/composer/composer/src/Composer/EventDispatcher/EventDispatcher.php @@ -12,20 +12,21 @@ namespace Composer\EventDispatcher; -use Composer\DependencyResolver\PolicyInterface; -use Composer\DependencyResolver\Pool; -use Composer\DependencyResolver\Request; +use Composer\DependencyResolver\Transaction; use Composer\Installer\InstallerEvent; use Composer\IO\IOInterface; use Composer\Composer; +use Composer\Util\Platform; use Composer\DependencyResolver\Operation\OperationInterface; -use Composer\Repository\CompositeRepository; +use Composer\Repository\RepositoryInterface; use Composer\Script; use Composer\Installer\PackageEvent; use Composer\Installer\BinaryInstaller; use Composer\Util\ProcessExecutor; use Composer\Script\Event as ScriptEvent; +use Composer\Autoload\ClassLoader; use Symfony\Component\Process\PhpExecutableFinder; +use Symfony\Component\Process\ExecutableFinder; /** * The Event Dispatcher. @@ -42,11 +43,19 @@ */ class EventDispatcher { + /** @var Composer */ protected $composer; + /** @var IOInterface */ protected $io; + /** @var ?ClassLoader */ protected $loader; + /** @var ProcessExecutor */ protected $process; - protected $listeners; + /** @var array>> */ + protected $listeners = array(); + /** @var bool */ + protected $runScripts = true; + /** @var list */ private $eventStack; /** @@ -64,6 +73,18 @@ public function __construct(Composer $composer, IOInterface $io, ProcessExecutor $this->eventStack = array(); } + /** + * Set whether script handlers are active or not + * + * @param bool $runScripts + */ + public function setRunScripts($runScripts = true) + { + $this->runScripts = (bool) $runScripts; + + return $this; + } + /** * Dispatch an event * @@ -99,40 +120,34 @@ public function dispatchScript($eventName, $devMode = false, $additionalArgs = a /** * Dispatch a package event. * - * @param string $eventName The constant in PackageEvents - * @param bool $devMode Whether or not we are in dev mode - * @param PolicyInterface $policy The policy - * @param Pool $pool The pool - * @param CompositeRepository $installedRepo The installed repository - * @param Request $request The request - * @param array $operations The list of operations - * @param OperationInterface $operation The package being installed/updated/removed + * @param string $eventName The constant in PackageEvents + * @param bool $devMode Whether or not we are in dev mode + * @param RepositoryInterface $localRepo The installed repository + * @param array $operations The list of operations + * @param OperationInterface $operation The package being installed/updated/removed * * @return int return code of the executed script if any, for php scripts a false return * value is changed to 1, anything else to 0 */ - public function dispatchPackageEvent($eventName, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation) + public function dispatchPackageEvent($eventName, $devMode, RepositoryInterface $localRepo, array $operations, OperationInterface $operation) { - return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $policy, $pool, $installedRepo, $request, $operations, $operation)); + return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $localRepo, $operations, $operation)); } /** * Dispatch a installer event. * - * @param string $eventName The constant in InstallerEvents - * @param bool $devMode Whether or not we are in dev mode - * @param PolicyInterface $policy The policy - * @param Pool $pool The pool - * @param CompositeRepository $installedRepo The installed repository - * @param Request $request The request - * @param array $operations The list of operations + * @param string $eventName The constant in InstallerEvents + * @param bool $devMode Whether or not we are in dev mode + * @param bool $executeOperations True if operations will be executed, false in --dry-run + * @param Transaction $transaction The transaction contains the list of operations * * @return int return code of the executed script if any, for php scripts a false return * value is changed to 1, anything else to 0 */ - public function dispatchInstallerEvent($eventName, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array()) + public function dispatchInstallerEvent($eventName, $devMode, $executeOperations, Transaction $transaction) { - return $this->doDispatch(new InstallerEvent($eventName, $this->composer, $this->io, $devMode, $policy, $pool, $installedRepo, $request, $operations)); + return $this->doDispatch(new InstallerEvent($eventName, $this->composer, $this->io, $devMode, $executeOperations, $transaction)); } /** @@ -145,132 +160,189 @@ public function dispatchInstallerEvent($eventName, $devMode, PolicyInterface $po */ protected function doDispatch(Event $event) { + if (getenv('COMPOSER_DEBUG_EVENTS')) { + $details = null; + if ($event instanceof PackageEvent) { + $details = (string) $event->getOperation(); + } + $this->io->writeError('Dispatching '.$event->getName().''.($details ? ' ('.$details.')' : '').' event'); + } + $listeners = $this->getListeners($event); $this->pushEvent($event); - $return = 0; - foreach ($listeners as $callable) { + try { + $returnMax = 0; + foreach ($listeners as $callable) { + $return = 0; + $this->ensureBinDirIsInPath(); - $this->ensureBinDirIsInPath(); + if (!is_string($callable)) { + if (!is_callable($callable)) { + $className = is_object($callable[0]) ? get_class($callable[0]) : $callable[0]; - if (!is_string($callable)) { - if (!is_callable($callable)) { - $className = is_object($callable[0]) ? get_class($callable[0]) : $callable[0]; + throw new \RuntimeException('Subscriber '.$className.'::'.$callable[1].' for event '.$event->getName().' is not callable, make sure the function is defined and public'); + } + if (is_array($callable) && (is_string($callable[0]) || is_object($callable[0])) && is_string($callable[1])) { + $this->io->writeError(sprintf('> %s: %s', $event->getName(), (is_object($callable[0]) ? get_class($callable[0]) : $callable[0]).'->'.$callable[1]), true, IOInterface::VERBOSE); + } + $return = false === call_user_func($callable, $event) ? 1 : 0; + } elseif ($this->isComposerScript($callable)) { + $this->io->writeError(sprintf('> %s: %s', $event->getName(), $callable), true, IOInterface::VERBOSE); + + $script = explode(' ', substr($callable, 1)); + $scriptName = $script[0]; + unset($script[0]); + + $args = array_merge($script, $event->getArguments()); + $flags = $event->getFlags(); + if (strpos($callable, '@composer ') === 0) { + $exec = $this->getPhpExecCommand() . ' ' . ProcessExecutor::escape(getenv('COMPOSER_BINARY')) . ' ' . implode(' ', $args); + if (0 !== ($exitCode = $this->executeTty($exec))) { + $this->io->writeError(sprintf('Script %s handling the %s event returned with error code '.$exitCode.'', $callable, $event->getName()), true, IOInterface::QUIET); + + throw new ScriptExecutionException('Error Output: '.$this->process->getErrorOutput(), $exitCode); + } + } else { + if (!$this->getListeners(new Event($scriptName))) { + $this->io->writeError(sprintf('You made a reference to a non-existent script %s', $callable), true, IOInterface::QUIET); + } - throw new \RuntimeException('Subscriber '.$className.'::'.$callable[1].' for event '.$event->getName().' is not callable, make sure the function is defined and public'); - } - $event = $this->checkListenerExpectedEvent($callable, $event); - $return = false === call_user_func($callable, $event) ? 1 : 0; - } elseif ($this->isComposerScript($callable)) { - $this->io->writeError(sprintf('> %s: %s', $event->getName(), $callable), true, IOInterface::VERBOSE); - - $script = explode(' ', substr($callable, 1)); - $scriptName = $script[0]; - unset($script[0]); - - $args = array_merge($script, $event->getArguments()); - $flags = $event->getFlags(); - if (substr($callable, 0, 10) === '@composer ') { - $exec = $this->getPhpExecCommand() . ' ' . ProcessExecutor::escape(getenv('COMPOSER_BINARY')) . substr($callable, 9); - if (0 !== ($exitCode = $this->process->execute($exec))) { - $this->io->writeError(sprintf('Script %s handling the %s event returned with error code '.$exitCode.'', $callable, $event->getName()), true, IOInterface::QUIET); + try { + /** @var InstallerEvent $event */ + $scriptEvent = new Script\Event($scriptName, $event->getComposer(), $event->getIO(), $event->isDevMode(), $args, $flags); + $scriptEvent->setOriginatingEvent($event); + $return = $this->dispatch($scriptName, $scriptEvent); + } catch (ScriptExecutionException $e) { + $this->io->writeError(sprintf('Script %s was called via %s', $callable, $event->getName()), true, IOInterface::QUIET); + throw $e; + } + } + } elseif ($this->isPhpScript($callable)) { + $className = substr($callable, 0, strpos($callable, '::')); + $methodName = substr($callable, strpos($callable, '::') + 2); - throw new ScriptExecutionException('Error Output: '.$this->process->getErrorOutput(), $exitCode); + if (!class_exists($className)) { + $this->io->writeError('Class '.$className.' is not autoloadable, can not call '.$event->getName().' script', true, IOInterface::QUIET); + continue; } - } else { - if (!$this->getListeners(new Event($scriptName))) { - $this->io->writeError(sprintf('You made a reference to a non-existent script %s', $callable), true, IOInterface::QUIET); + if (!is_callable($callable)) { + $this->io->writeError('Method '.$callable.' is not callable, can not call '.$event->getName().' script', true, IOInterface::QUIET); + continue; } try { - $scriptEvent = new Script\Event($scriptName, $event->getComposer(), $event->getIO(), $event->isDevMode(), $args, $flags); - $scriptEvent->setOriginatingEvent($event); - $return = $this->dispatch($scriptName, $scriptEvent); - } catch (ScriptExecutionException $e) { - $this->io->writeError(sprintf('Script %s was called via %s', $callable, $event->getName()), true, IOInterface::QUIET); + $return = false === $this->executeEventPhpScript($className, $methodName, $event) ? 1 : 0; + } catch (\Exception $e) { + $message = "Script %s handling the %s event terminated with an exception"; + $this->io->writeError(''.sprintf($message, $callable, $event->getName()).'', true, IOInterface::QUIET); throw $e; } - } - } elseif ($this->isPhpScript($callable)) { - $className = substr($callable, 0, strpos($callable, '::')); - $methodName = substr($callable, strpos($callable, '::') + 2); - - if (!class_exists($className)) { - $this->io->writeError('Class '.$className.' is not autoloadable, can not call '.$event->getName().' script', true, IOInterface::QUIET); - continue; - } - if (!is_callable($callable)) { - $this->io->writeError('Method '.$callable.' is not callable, can not call '.$event->getName().' script', true, IOInterface::QUIET); - continue; - } - - try { - $return = false === $this->executeEventPhpScript($className, $methodName, $event) ? 1 : 0; - } catch (\Exception $e) { - $message = "Script %s handling the %s event terminated with an exception"; - $this->io->writeError(''.sprintf($message, $callable, $event->getName()).'', true, IOInterface::QUIET); - throw $e; - } - } else { - $args = implode(' ', array_map(array('Composer\Util\ProcessExecutor', 'escape'), $event->getArguments())); - $exec = $callable . ($args === '' ? '' : ' '.$args); - if ($this->io->isVerbose()) { - $this->io->writeError(sprintf('> %s: %s', $event->getName(), $exec)); } else { - $this->io->writeError(sprintf('> %s', $exec)); - } + $args = implode(' ', array_map(array('Composer\Util\ProcessExecutor', 'escape'), $event->getArguments())); + $exec = $callable . ($args === '' ? '' : ' '.$args); + if ($this->io->isVerbose()) { + $this->io->writeError(sprintf('> %s: %s', $event->getName(), $exec)); + } elseif ($event->getName() !== '__exec_command') { + // do not output the command being run when using `composer exec` as it is fairly obvious the user is running it + $this->io->writeError(sprintf('> %s', $exec)); + } - $possibleLocalBinaries = $this->composer->getPackage()->getBinaries(); - if ($possibleLocalBinaries) { - foreach ($possibleLocalBinaries as $localExec) { - if (preg_match('{\b'.preg_quote($callable).'$}', $localExec)) { - $caller = BinaryInstaller::determineBinaryCaller($localExec); - $exec = preg_replace('{^'.preg_quote($callable).'}', $caller . ' ' . $localExec, $exec); - break; + $possibleLocalBinaries = $this->composer->getPackage()->getBinaries(); + if ($possibleLocalBinaries) { + foreach ($possibleLocalBinaries as $localExec) { + if (preg_match('{\b'.preg_quote($callable).'$}', $localExec)) { + $caller = BinaryInstaller::determineBinaryCaller($localExec); + $exec = preg_replace('{^'.preg_quote($callable).'}', $caller . ' ' . $localExec, $exec); + break; + } } } - } - if (substr($exec, 0, 8) === '@putenv ') { - putenv(substr($exec, 8)); - list($var, $value) = explode('=', substr($exec, 8), 2); - $_SERVER[$var] = $value; + if (strpos($exec, '@putenv ') === 0) { + if (false === strpos($exec, '=')) { + Platform::clearEnv(substr($exec, 8)); + } else { + list($var, $value) = explode('=', substr($exec, 8), 2); + Platform::putEnv($var, $value); + } - continue; - } elseif (substr($exec, 0, 5) === '@php ') { - $exec = $this->getPhpExecCommand() . ' ' . substr($exec, 5); - } else { - $finder = new PhpExecutableFinder(); - $phpPath = $finder->find(false); - if ($phpPath) { - $_SERVER['PHP_BINARY'] = $phpPath; - putenv('PHP_BINARY=' . $_SERVER['PHP_BINARY']); + continue; + } + if (strpos($exec, '@php ') === 0) { + $pathAndArgs = substr($exec, 5); + if (Platform::isWindows()) { + $pathAndArgs = preg_replace_callback('{^\S+}', function ($path) { + return str_replace('/', '\\', $path[0]); + }, $pathAndArgs); + } + // match somename (not in quote, and not a qualified path) and if it is not a valid path from CWD then try to find it + // in $PATH. This allows support for `@php foo` where foo is a binary name found in PATH but not an actual relative path + $matched = preg_match('{^[^\'"\s/\\\\]+}', $pathAndArgs, $match); + if ($matched && !file_exists($match[0])) { + $finder = new ExecutableFinder; + if ($pathToExec = $finder->find($match[0])) { + $pathAndArgs = $pathToExec . substr($pathAndArgs, strlen($match[0])); + } + } + $exec = $this->getPhpExecCommand() . ' ' . $pathAndArgs; + } else { + $finder = new PhpExecutableFinder(); + $phpPath = $finder->find(false); + if ($phpPath) { + Platform::putEnv('PHP_BINARY', $phpPath); + } + + if (Platform::isWindows()) { + $exec = preg_replace_callback('{^\S+}', function ($path) { + return str_replace('/', '\\', $path[0]); + }, $exec); + } } - } - // if composer is being executed, make sure it runs the expected composer from current path - // resolution, even if bin-dir contains composer too because the project requires composer/composer - // see https://github.com/composer/composer/issues/8748 - if (substr($exec, 0, 9) === 'composer ') { - $exec = $this->getPhpExecCommand() . ' ' . ProcessExecutor::escape(getenv('COMPOSER_BINARY')) . substr($exec, 8); + // if composer is being executed, make sure it runs the expected composer from current path + // resolution, even if bin-dir contains composer too because the project requires composer/composer + // see https://github.com/composer/composer/issues/8748 + if (strpos($exec, 'composer ') === 0) { + $exec = $this->getPhpExecCommand() . ' ' . ProcessExecutor::escape(getenv('COMPOSER_BINARY')) . substr($exec, 8); + } + + if (0 !== ($exitCode = $this->executeTty($exec))) { + $this->io->writeError(sprintf('Script %s handling the %s event returned with error code '.$exitCode.'', $callable, $event->getName()), true, IOInterface::QUIET); + + throw new ScriptExecutionException('Error Output: '.$this->process->getErrorOutput(), $exitCode); + } } - if (0 !== ($exitCode = $this->process->execute($exec))) { - $this->io->writeError(sprintf('Script %s handling the %s event returned with error code '.$exitCode.'', $callable, $event->getName()), true, IOInterface::QUIET); + $returnMax = max($returnMax, $return); - throw new ScriptExecutionException('Error Output: '.$this->process->getErrorOutput(), $exitCode); + if ($event->isPropagationStopped()) { + break; } } + } catch (\Exception $e) { // TODO Composer 2.2 turn all this into a finally + $this->popEvent(); - if ($event->isPropagationStopped()) { - break; - } + throw $e; + } catch (\Throwable $e) { + $this->popEvent(); + + throw $e; } $this->popEvent(); - return $return; + return $returnMax; + } + + protected function executeTty($exec) + { + if ($this->io->isInteractive()) { + return $this->process->executeTty($exec); + } + + return $this->process->execute($exec); } protected function getPhpExecCommand() @@ -296,8 +368,6 @@ protected function getPhpExecCommand() */ protected function executeEventPhpScript($className, $methodName, Event $event) { - $event = $this->checkListenerExpectedEvent(array($className, $methodName), $event); - if ($this->io->isVerbose()) { $this->io->writeError(sprintf('> %s: %s::%s', $event->getName(), $className, $methodName)); } else { @@ -307,102 +377,6 @@ protected function executeEventPhpScript($className, $methodName, Event $event) return $className::$methodName($event); } - /** - * @param mixed $target - * @param Event $event - * @return Event - */ - protected function checkListenerExpectedEvent($target, Event $event) - { - if (in_array($event->getName(), array( - 'init', - 'command', - 'pre-file-download', - ), true)) { - return $event; - } - - try { - $reflected = new \ReflectionParameter($target, 0); - } catch (\Exception $e) { - return $event; - } - - $expected = null; - $isClass = false; - if (\PHP_VERSION_ID >= 70000) { - $reflectionType = $reflected->getType(); - if ($reflectionType) { - $expected = $reflectionType instanceof \ReflectionNamedType ? $reflectionType->getName() : (string)$reflectionType; - $isClass = !$reflectionType->isBuiltin(); - } - } else { - $expected = $reflected->getClass() ? $reflected->getClass()->getName() : null; - $isClass = null !== $expected; - } - - if (!$isClass) { - return $event; - } - - // BC support - if (!$event instanceof $expected && $expected === 'Composer\Script\CommandEvent') { - trigger_error('The callback '.$this->serializeCallback($target).' declared at '.$reflected->getDeclaringFunction()->getFileName().' accepts a '.$expected.' but '.$event->getName().' events use a '.get_class($event).' instance. Please adjust your type hint accordingly, see https://getcomposer.org/doc/articles/scripts.md#event-classes', E_USER_DEPRECATED); - $event = new \Composer\Script\CommandEvent( - $event->getName(), - $event->getComposer(), - $event->getIO(), - $event->isDevMode(), - $event->getArguments() - ); - } - if (!$event instanceof $expected && $expected === 'Composer\Script\PackageEvent') { - trigger_error('The callback '.$this->serializeCallback($target).' declared at '.$reflected->getDeclaringFunction()->getFileName().' accepts a '.$expected.' but '.$event->getName().' events use a '.get_class($event).' instance. Please adjust your type hint accordingly, see https://getcomposer.org/doc/articles/scripts.md#event-classes', E_USER_DEPRECATED); - $event = new \Composer\Script\PackageEvent( - $event->getName(), - $event->getComposer(), - $event->getIO(), - $event->isDevMode(), - $event->getPolicy(), - $event->getPool(), - $event->getInstalledRepo(), - $event->getRequest(), - $event->getOperations(), - $event->getOperation() - ); - } - if (!$event instanceof $expected && $expected === 'Composer\Script\Event') { - trigger_error('The callback '.$this->serializeCallback($target).' declared at '.$reflected->getDeclaringFunction()->getFileName().' accepts a '.$expected.' but '.$event->getName().' events use a '.get_class($event).' instance. Please adjust your type hint accordingly, see https://getcomposer.org/doc/articles/scripts.md#event-classes', E_USER_DEPRECATED); - $event = new \Composer\Script\Event( - $event->getName(), - $event->getComposer(), - $event->getIO(), - $event->isDevMode(), - $event->getArguments(), - $event->getFlags() - ); - } - - return $event; - } - - private function serializeCallback($cb) - { - if (is_array($cb) && count($cb) === 2) { - if (is_object($cb[0])) { - $cb[0] = get_class($cb[0]); - } - if (is_string($cb[0]) && is_string($cb[1])) { - $cb = implode('::', $cb); - } - } - if (is_string($cb)) { - return $cb; - } - - return var_export($cb, true); - } - /** * Add a listener for a particular event * @@ -415,6 +389,22 @@ public function addListener($eventName, $listener, $priority = 0) $this->listeners[$eventName][$priority][] = $listener; } + /** + * @param callable|object $listener A callable or an object instance for which all listeners should be removed + */ + public function removeListener($listener) + { + foreach ($this->listeners as $eventName => $priorities) { + foreach ($priorities as $priority => $listeners) { + foreach ($listeners as $index => $candidate) { + if ($listener === $candidate || (is_array($candidate) && is_object($listener) && $candidate[0] === $listener)) { + unset($this->listeners[$eventName][$priority][$index]); + } + } + } + } + } + /** * Adds object methods as listeners for the events in getSubscribedEvents * @@ -445,7 +435,7 @@ public function addSubscriber(EventSubscriberInterface $subscriber) */ protected function getListeners(Event $event) { - $scriptListeners = $this->getScriptListeners($event); + $scriptListeners = $this->runScripts ? $this->getScriptListeners($event) : array(); if (!isset($this->listeners[$event->getName()][0])) { $this->listeners[$event->getName()][0] = array(); @@ -498,8 +488,8 @@ protected function getScriptListeners(Event $event) $packages = $this->composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages(); $packageMap = $generator->buildPackageMap($this->composer->getInstallationManager(), $package, $packages); $map = $generator->parseAutoloads($packageMap, $package); - $this->loader = $generator->createLoader($map); - $this->loader->register(); + $this->loader = $generator->createLoader($map, $this->composer->getConfig()->get('vendor-dir')); + $this->loader->register(false); return $scripts[$event->getName()]; } @@ -523,7 +513,7 @@ protected function isPhpScript($callable) */ protected function isComposerScript($callable) { - return '@' === substr($callable, 0, 1) && '@php ' !== substr($callable, 0, 5) && '@putenv ' !== substr($callable, 0, 8); + return strpos($callable, '@') === 0 && strpos($callable, '@php ') !== 0 && strpos($callable, '@putenv ') !== 0; } /** @@ -531,7 +521,7 @@ protected function isComposerScript($callable) * * @param Event $event * @throws \RuntimeException - * @return number + * @return int */ protected function pushEvent(Event $event) { @@ -565,8 +555,7 @@ private function ensureBinDirIsInPath() if (is_dir($binDir)) { $binDir = realpath($binDir); if (isset($_SERVER[$pathStr]) && !preg_match('{(^|'.PATH_SEPARATOR.')'.preg_quote($binDir).'($|'.PATH_SEPARATOR.')}', $_SERVER[$pathStr])) { - $_SERVER[$pathStr] = $binDir.PATH_SEPARATOR.getenv($pathStr); - putenv($pathStr.'='.$_SERVER[$pathStr]); + Platform::putEnv($pathStr, $binDir.PATH_SEPARATOR.getenv($pathStr)); } } } diff --git a/app/vendor/composer/composer/src/Composer/Exception/IrrecoverableDownloadException.php b/app/vendor/composer/composer/src/Composer/Exception/IrrecoverableDownloadException.php new file mode 100644 index 000000000..72a833c72 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Exception/IrrecoverableDownloadException.php @@ -0,0 +1,20 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Exception; + +/** + * @author Jordi Boggiano + */ +class IrrecoverableDownloadException extends \RuntimeException +{ +} diff --git a/app/vendor/composer/composer/src/Composer/Exception/NoSslException.php b/app/vendor/composer/composer/src/Composer/Exception/NoSslException.php index 017cac382..55c81fea9 100644 --- a/app/vendor/composer/composer/src/Composer/Exception/NoSslException.php +++ b/app/vendor/composer/composer/src/Composer/Exception/NoSslException.php @@ -13,6 +13,8 @@ namespace Composer\Exception; /** + * Specific exception for Composer\Util\HttpDownloader creation. + * * @author Jordi Boggiano */ class NoSslException extends \RuntimeException diff --git a/app/vendor/composer/composer/src/Composer/Factory.php b/app/vendor/composer/composer/src/Composer/Factory.php index acb483294..d67ccf29a 100644 --- a/app/vendor/composer/composer/src/Composer/Factory.php +++ b/app/vendor/composer/composer/src/Composer/Factory.php @@ -17,13 +17,15 @@ use Composer\IO\IOInterface; use Composer\Package\Archiver; use Composer\Package\Version\VersionGuesser; +use Composer\Package\RootPackageInterface; use Composer\Repository\RepositoryManager; use Composer\Repository\RepositoryFactory; use Composer\Repository\WritableRepositoryInterface; use Composer\Util\Filesystem; use Composer\Util\Platform; use Composer\Util\ProcessExecutor; -use Composer\Util\RemoteFilesystem; +use Composer\Util\HttpDownloader; +use Composer\Util\Loop; use Composer\Util\Silencer; use Composer\Plugin\PluginEvents; use Composer\EventDispatcher\Event; @@ -35,6 +37,8 @@ use Composer\Autoload\AutoloadGenerator; use Composer\Package\Version\VersionParser; use Composer\Downloader\TransportException; +use Composer\Json\JsonValidationException; +use Composer\Repository\InstalledRepositoryInterface; use Seld\JsonLint\JsonParser; /** @@ -67,18 +71,29 @@ protected static function getHomeDir() } $userDir = self::getUserDir(); - if (is_dir($userDir . '/.composer')) { - return $userDir . '/.composer'; - } + $dirs = array(); if (self::useXdg()) { // XDG Base Directory Specifications - $xdgConfig = getenv('XDG_CONFIG_HOME') ?: $userDir . '/.config'; + $xdgConfig = getenv('XDG_CONFIG_HOME'); + if (!$xdgConfig) { + $xdgConfig = $userDir . '/.config'; + } - return $xdgConfig . '/composer'; + $dirs[] = $xdgConfig . '/composer'; } - return $userDir . '/.composer'; + $dirs[] = $userDir . '/.composer'; + + // select first dir which exists of: $XDG_CONFIG_HOME/composer or ~/.composer + foreach ($dirs as $dir) { + if (Silencer::call('is_dir', $dir)) { + return $dir; + } + } + + // if none exists, we default to first defined one (XDG one if system uses it, or ~/.composer otherwise) + return $dirs[0]; } /** @@ -108,6 +123,15 @@ protected static function getCacheDir($home) } $userDir = self::getUserDir(); + if (PHP_OS === 'Darwin') { + // Migrate existing cache dir in old location if present + if (is_dir($home . '/cache') && !is_dir($userDir . '/Library/Caches/composer')) { + Silencer::call('rename', $home . '/cache', $userDir . '/Library/Caches/composer'); + } + + return $userDir . '/Library/Caches/composer'; + } + if ($home === $userDir . '/.composer' && is_dir($home . '/cache')) { return $home . '/cache'; } @@ -222,6 +246,13 @@ public static function getComposerFile() return trim(getenv('COMPOSER')) ?: './composer.json'; } + public static function getLockFile($composerFile) + { + return "json" === pathinfo($composerFile, PATHINFO_EXTENSION) + ? substr($composerFile, 0, -4).'lock' + : $composerFile . '.lock'; + } + public static function createAdditionalStyles() { return array( @@ -243,14 +274,6 @@ public static function createOutput() return new ConsoleOutput(ConsoleOutput::VERBOSITY_NORMAL, null, $formatter); } - /** - * @deprecated Use Composer\Repository\RepositoryFactory::defaultRepos instead - */ - public static function createDefaultRepositories(IOInterface $io = null, Config $config = null, RepositoryManager $rm = null) - { - return RepositoryFactory::defaultRepos($io, $config, $rm); - } - /** * Creates a Composer instance * @@ -283,11 +306,17 @@ public function createComposer(IOInterface $io, $localConfig = null, $disablePlu } else { $message = 'Composer could not find the config file: '.$localConfig; } - $instructions = 'To initialize a project, please create a composer.json file as described in the https://getcomposer.org/ "Getting Started" section'; + $instructions = $fullLoad ? 'To initialize a project, please create a composer.json file. See https://getcomposer.org/basic-usage' : ''; throw new \InvalidArgumentException($message.PHP_EOL.$instructions); } - $file->validateSchema(JsonFile::LAX_SCHEMA); + try { + $file->validateSchema(JsonFile::LAX_SCHEMA); + } catch (JsonValidationException $e) { + $errors = ' - ' . implode(PHP_EOL . ' - ', $e->getErrors()); + $message = $e->getMessage() . ':' . PHP_EOL . $errors; + throw new JsonValidationException($message); + } $jsonParser = new JsonParser; try { $jsonParser->parse(file_get_contents($localConfig), JsonParser::DETECT_KEY_CONFLICTS); @@ -303,7 +332,7 @@ public function createComposer(IOInterface $io, $localConfig = null, $disablePlu $config = static::createConfig($io, $cwd); $config->merge($localConfig); if (isset($composerFile)) { - $io->writeError('Loading config file ' . $composerFile, true, IOInterface::DEBUG); + $io->writeError('Loading config file ' . $composerFile .' ('.realpath($composerFile).')', true, IOInterface::DEBUG); $config->setConfigSource(new JsonConfigSource(new JsonFile(realpath($composerFile), null, $io))); $localAuthFile = new JsonFile(dirname(realpath($composerFile)) . '/auth.json', null, $io); @@ -323,21 +352,26 @@ public function createComposer(IOInterface $io, $localConfig = null, $disablePlu if ($fullLoad) { // load auth configs into the IO instance $io->loadConfiguration($config); + + // load existing Composer\InstalledVersions instance if available + if (!class_exists('Composer\InstalledVersions', false) && file_exists($installedVersionsPath = $config->get('vendor-dir').'/composer/InstalledVersions.php')) { + include $installedVersionsPath; + } } - $rfs = self::createRemoteFilesystem($io, $config); + $httpDownloader = self::createHttpDownloader($io, $config); + $process = new ProcessExecutor($io); + $loop = new Loop($httpDownloader, $process); + $composer->setLoop($loop); // initialize event dispatcher - $dispatcher = new EventDispatcher($composer, $io); + $dispatcher = new EventDispatcher($composer, $io, $process); $composer->setEventDispatcher($dispatcher); // initialize repository manager - $rm = RepositoryFactory::manager($io, $config, $dispatcher, $rfs); + $rm = RepositoryFactory::manager($io, $config, $httpDownloader, $dispatcher, $process); $composer->setRepositoryManager($rm); - // load local repository - $this->addLocalRepository($io, $rm, $vendorDir); - // force-set the version of the global package if not defined as // guessing it adds no value and only takes time if (!$fullLoad && !isset($localConfig['version'])) { @@ -346,18 +380,21 @@ public function createComposer(IOInterface $io, $localConfig = null, $disablePlu // load package $parser = new VersionParser; - $guesser = new VersionGuesser($config, new ProcessExecutor($io), $parser); - $loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, $guesser, $io); + $guesser = new VersionGuesser($config, $process, $parser); + $loader = $this->loadRootPackage($rm, $config, $parser, $guesser, $io); $package = $loader->load($localConfig, 'Composer\Package\RootPackage', $cwd); $composer->setPackage($package); + // load local repository + $this->addLocalRepository($io, $rm, $vendorDir, $package, $process); + // initialize installation manager - $im = $this->createInstallationManager(); + $im = $this->createInstallationManager($loop, $io, $dispatcher); $composer->setInstallationManager($im); if ($fullLoad) { // initialize download manager - $dm = $this->createDownloadManager($io, $config, $dispatcher, $rfs); + $dm = $this->createDownloadManager($io, $config, $httpDownloader, $process, $dispatcher); $composer->setDownloadManager($dm); // initialize autoload generator @@ -365,12 +402,12 @@ public function createComposer(IOInterface $io, $localConfig = null, $disablePlu $composer->setAutoloadGenerator($generator); // initialize archive manager - $am = $this->createArchiveManager($config, $dm); + $am = $this->createArchiveManager($config, $dm, $loop); $composer->setArchiveManager($am); } // add installers to the manager (must happen after download manager is created since they read it out of $composer) - $this->createDefaultInstallers($im, $composer, $io); + $this->createDefaultInstallers($im, $composer, $io, $process); if ($fullLoad) { $globalComposer = null; @@ -386,11 +423,9 @@ public function createComposer(IOInterface $io, $localConfig = null, $disablePlu // init locker if possible if ($fullLoad && isset($composerFile)) { - $lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION) - ? substr($composerFile, 0, -4).'lock' - : $composerFile . '.lock'; + $lockFile = self::getLockFile($composerFile); - $locker = new Package\Locker($io, new JsonFile($lockFile, null, $io), $rm, $im, file_get_contents($composerFile)); + $locker = new Package\Locker($io, new JsonFile($lockFile, null, $io), $im, file_get_contents($composerFile), $process); $composer->setLocker($locker); } @@ -400,18 +435,16 @@ public function createComposer(IOInterface $io, $localConfig = null, $disablePlu // once everything is initialized we can // purge packages from local repos if they have been deleted on the filesystem - if ($rm->getLocalRepository()) { - $this->purgePackages($rm->getLocalRepository(), $im); - } + $this->purgePackages($rm->getLocalRepository(), $im); } return $composer; } /** - * @param IOInterface $io IO instance - * @param bool $disablePlugins Whether plugins should not be loaded - * @return Composer + * @param IOInterface $io IO instance + * @param bool $disablePlugins Whether plugins should not be loaded + * @return Composer|null */ public static function createGlobal(IOInterface $io, $disablePlugins = false) { @@ -424,9 +457,14 @@ public static function createGlobal(IOInterface $io, $disablePlugins = false) * @param Repository\RepositoryManager $rm * @param string $vendorDir */ - protected function addLocalRepository(IOInterface $io, RepositoryManager $rm, $vendorDir) + protected function addLocalRepository(IOInterface $io, RepositoryManager $rm, $vendorDir, RootPackageInterface $rootPackage, ProcessExecutor $process = null) { - $rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json', null, $io))); + $fs = null; + if ($process) { + $fs = new Filesystem($process); + } + + $rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json', null, $io), true, $rootPackage, $fs)); } /** @@ -451,14 +489,17 @@ protected function createGlobalComposer(IOInterface $io, Config $config, $disabl * @param EventDispatcher $eventDispatcher * @return Downloader\DownloadManager */ - public function createDownloadManager(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, RemoteFilesystem $rfs = null) + public function createDownloadManager(IOInterface $io, Config $config, HttpDownloader $httpDownloader, ProcessExecutor $process, EventDispatcher $eventDispatcher = null) { $cache = null; if ($config->get('cache-files-ttl') > 0) { $cache = new Cache($io, $config->get('cache-files-dir'), 'a-z0-9_./'); + $cache->setReadOnly($config->get('cache-read-only')); } - $dm = new Downloader\DownloadManager($io); + $fs = new Filesystem($process); + + $dm = new Downloader\DownloadManager($io, false, $fs); switch ($preferred = $config->get('preferred-install')) { case 'dist': $dm->setPreferDist(true); @@ -476,22 +517,19 @@ public function createDownloadManager(IOInterface $io, Config $config, EventDisp $dm->setPreferences($preferred); } - $executor = new ProcessExecutor($io); - $fs = new Filesystem($executor); - - $dm->setDownloader('git', new Downloader\GitDownloader($io, $config, $executor, $fs)); - $dm->setDownloader('svn', new Downloader\SvnDownloader($io, $config, $executor, $fs)); - $dm->setDownloader('fossil', new Downloader\FossilDownloader($io, $config, $executor, $fs)); - $dm->setDownloader('hg', new Downloader\HgDownloader($io, $config, $executor, $fs)); - $dm->setDownloader('perforce', new Downloader\PerforceDownloader($io, $config)); - $dm->setDownloader('zip', new Downloader\ZipDownloader($io, $config, $eventDispatcher, $cache, $executor, $rfs)); - $dm->setDownloader('rar', new Downloader\RarDownloader($io, $config, $eventDispatcher, $cache, $executor, $rfs)); - $dm->setDownloader('tar', new Downloader\TarDownloader($io, $config, $eventDispatcher, $cache, $rfs)); - $dm->setDownloader('gzip', new Downloader\GzipDownloader($io, $config, $eventDispatcher, $cache, $executor, $rfs)); - $dm->setDownloader('xz', new Downloader\XzDownloader($io, $config, $eventDispatcher, $cache, $executor, $rfs)); - $dm->setDownloader('phar', new Downloader\PharDownloader($io, $config, $eventDispatcher, $cache, $rfs)); - $dm->setDownloader('file', new Downloader\FileDownloader($io, $config, $eventDispatcher, $cache, $rfs)); - $dm->setDownloader('path', new Downloader\PathDownloader($io, $config, $eventDispatcher, $cache, $rfs)); + $dm->setDownloader('git', new Downloader\GitDownloader($io, $config, $process, $fs)); + $dm->setDownloader('svn', new Downloader\SvnDownloader($io, $config, $process, $fs)); + $dm->setDownloader('fossil', new Downloader\FossilDownloader($io, $config, $process, $fs)); + $dm->setDownloader('hg', new Downloader\HgDownloader($io, $config, $process, $fs)); + $dm->setDownloader('perforce', new Downloader\PerforceDownloader($io, $config, $process, $fs)); + $dm->setDownloader('zip', new Downloader\ZipDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process)); + $dm->setDownloader('rar', new Downloader\RarDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process)); + $dm->setDownloader('tar', new Downloader\TarDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process)); + $dm->setDownloader('gzip', new Downloader\GzipDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process)); + $dm->setDownloader('xz', new Downloader\XzDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process)); + $dm->setDownloader('phar', new Downloader\PharDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process)); + $dm->setDownloader('file', new Downloader\FileDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process)); + $dm->setDownloader('path', new Downloader\PathDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process)); return $dm; } @@ -501,15 +539,9 @@ public function createDownloadManager(IOInterface $io, Config $config, EventDisp * @param Downloader\DownloadManager $dm Manager use to download sources * @return Archiver\ArchiveManager */ - public function createArchiveManager(Config $config, Downloader\DownloadManager $dm = null) + public function createArchiveManager(Config $config, Downloader\DownloadManager $dm, Loop $loop) { - if (null === $dm) { - $io = new IO\NullIO(); - $io->loadConfiguration($config); - $dm = $this->createDownloadManager($io, $config); - } - - $am = new Archiver\ArchiveManager($dm); + $am = new Archiver\ArchiveManager($dm, $loop); $am->addArchiver(new Archiver\ZipArchiver); $am->addArchiver(new Archiver\PharArchiver); @@ -531,9 +563,9 @@ protected function createPluginManager(IOInterface $io, Composer $composer, Comp /** * @return Installer\InstallationManager */ - protected function createInstallationManager() + public function createInstallationManager(Loop $loop, IOInterface $io, EventDispatcher $eventDispatcher = null) { - return new Installer\InstallationManager(); + return new Installer\InstallationManager($loop, $io, $eventDispatcher); } /** @@ -541,19 +573,21 @@ protected function createInstallationManager() * @param Composer $composer * @param IO\IOInterface $io */ - protected function createDefaultInstallers(Installer\InstallationManager $im, Composer $composer, IOInterface $io) + protected function createDefaultInstallers(Installer\InstallationManager $im, Composer $composer, IOInterface $io, ProcessExecutor $process = null) { - $im->addInstaller(new Installer\LibraryInstaller($io, $composer, null)); - $im->addInstaller(new Installer\PearInstaller($io, $composer, 'pear-library')); - $im->addInstaller(new Installer\PluginInstaller($io, $composer)); + $fs = new Filesystem($process); + $binaryInstaller = new Installer\BinaryInstaller($io, rtrim($composer->getConfig()->get('bin-dir'), '/'), $composer->getConfig()->get('bin-compat'), $fs); + + $im->addInstaller(new Installer\LibraryInstaller($io, $composer, null, $fs, $binaryInstaller)); + $im->addInstaller(new Installer\PluginInstaller($io, $composer, $fs, $binaryInstaller)); $im->addInstaller(new Installer\MetapackageInstaller($io)); } /** - * @param WritableRepositoryInterface $repo repository to purge packages from + * @param InstalledRepositoryInterface $repo repository to purge packages from * @param Installer\InstallationManager $im manager to check whether packages are still installed */ - protected function purgePackages(WritableRepositoryInterface $repo, Installer\InstallationManager $im) + protected function purgePackages(InstalledRepositoryInterface $repo, Installer\InstallationManager $im) { foreach ($repo->getPackages() as $package) { if (!$im->isPackageInstalled($repo, $package)) { @@ -562,6 +596,11 @@ protected function purgePackages(WritableRepositoryInterface $repo, Installer\In } } + protected function loadRootPackage(RepositoryManager $rm, Config $config, VersionParser $parser, VersionGuesser $guesser, IOInterface $io) + { + return new Package\Loader\RootPackageLoader($rm, $config, $parser, $guesser, $io); + } + /** * @param IOInterface $io IO instance * @param mixed $config either a configuration array or a filename to read from, if null it will read from @@ -577,12 +616,14 @@ public static function create(IOInterface $io, $config = null, $disablePlugins = } /** - * @param IOInterface $io IO instance - * @param Config $config Config instance - * @param array $options Array of options passed directly to RemoteFilesystem constructor - * @return RemoteFilesystem + * If you are calling this in a plugin, you probably should instead use $composer->getLoop()->getHttpDownloader() + * + * @param IOInterface $io IO instance + * @param Config $config Config instance + * @param array $options Array of options passed directly to HttpDownloader constructor + * @return HttpDownloader */ - public static function createRemoteFilesystem(IOInterface $io, Config $config = null, $options = array()) + public static function createHttpDownloader(IOInterface $io, Config $config, $options = array()) { static $warned = false; $disableTls = false; @@ -590,7 +631,7 @@ public static function createRemoteFilesystem(IOInterface $io, Config $config = if (isset($_SERVER['argv']) && in_array('disable-tls', $_SERVER['argv']) && (in_array('conf', $_SERVER['argv']) || in_array('config', $_SERVER['argv']))) { $warned = true; $disableTls = !extension_loaded('openssl'); - } elseif ($config && $config->get('disable-tls') === true) { + } elseif ($config->get('disable-tls') === true) { if (!$warned) { $io->writeError('You are running Composer with SSL/TLS protection disabled.'); } @@ -600,18 +641,18 @@ public static function createRemoteFilesystem(IOInterface $io, Config $config = throw new Exception\NoSslException('The openssl extension is required for SSL/TLS protection but is not available. ' . 'If you can not enable the openssl extension, you can disable this error, at your own risk, by setting the \'disable-tls\' option to true.'); } - $remoteFilesystemOptions = array(); + $httpDownloaderOptions = array(); if ($disableTls === false) { - if ($config && $config->get('cafile')) { - $remoteFilesystemOptions['ssl']['cafile'] = $config->get('cafile'); + if ($config->get('cafile')) { + $httpDownloaderOptions['ssl']['cafile'] = $config->get('cafile'); } - if ($config && $config->get('capath')) { - $remoteFilesystemOptions['ssl']['capath'] = $config->get('capath'); + if ($config->get('capath')) { + $httpDownloaderOptions['ssl']['capath'] = $config->get('capath'); } - $remoteFilesystemOptions = array_replace_recursive($remoteFilesystemOptions, $options); + $httpDownloaderOptions = array_replace_recursive($httpDownloaderOptions, $options); } try { - $remoteFilesystem = new RemoteFilesystem($io, $config, $remoteFilesystemOptions, $disableTls); + $httpDownloader = new HttpDownloader($io, $config, $httpDownloaderOptions, $disableTls); } catch (TransportException $e) { if (false !== strpos($e->getMessage(), 'cafile')) { $io->write('Unable to locate a valid CA certificate file. You must set a valid \'cafile\' option.'); @@ -624,7 +665,7 @@ public static function createRemoteFilesystem(IOInterface $io, Config $config = throw $e; } - return $remoteFilesystem; + return $httpDownloader; } /** @@ -633,11 +674,15 @@ public static function createRemoteFilesystem(IOInterface $io, Config $config = private static function useXdg() { foreach (array_keys($_SERVER) as $key) { - if (substr($key, 0, 4) === 'XDG_') { + if (strpos($key, 'XDG_') === 0) { return true; } } + if (Silencer::call('is_dir', '/etc/xdg')) { + return true; + } + return false; } diff --git a/app/vendor/composer/composer/src/Composer/IO/BaseIO.php b/app/vendor/composer/composer/src/Composer/IO/BaseIO.php index 013597450..380902fa7 100644 --- a/app/vendor/composer/composer/src/Composer/IO/BaseIO.php +++ b/app/vendor/composer/composer/src/Composer/IO/BaseIO.php @@ -14,11 +14,11 @@ use Composer\Config; use Composer\Util\ProcessExecutor; -use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; -abstract class BaseIO implements IOInterface, LoggerInterface +abstract class BaseIO implements IOInterface { + /** @var array */ protected $authentications = array(); /** @@ -138,7 +138,9 @@ public function loadConfiguration(Config $config) } foreach ($gitlabToken as $domain => $token) { - $this->checkAndSetAuthentication($domain, $token, 'private-token'); + $username = is_array($token) && array_key_exists("username", $token) ? $token["username"] : $token; + $password = is_array($token) && array_key_exists("token", $token) ? $token["token"] : 'private-token'; + $this->checkAndSetAuthentication($domain, $username, $password); } // reload http basic credentials from config if available @@ -155,126 +157,78 @@ public function loadConfiguration(Config $config) } /** - * System is unusable. - * - * @param string $message - * @param array $context - * @return null + * {@inheritDoc} */ public function emergency($message, array $context = array()) { - return $this->log(LogLevel::EMERGENCY, $message, $context); + $this->log(LogLevel::EMERGENCY, $message, $context); } /** - * Action must be taken immediately. - * - * Example: Entire website down, database unavailable, etc. This should - * trigger the SMS alerts and wake you up. - * - * @param string $message - * @param array $context - * @return null + * {@inheritDoc} */ public function alert($message, array $context = array()) { - return $this->log(LogLevel::ALERT, $message, $context); + $this->log(LogLevel::ALERT, $message, $context); } /** - * Critical conditions. - * - * Example: Application component unavailable, unexpected exception. - * - * @param string $message - * @param array $context - * @return null + * {@inheritDoc} */ public function critical($message, array $context = array()) { - return $this->log(LogLevel::CRITICAL, $message, $context); + $this->log(LogLevel::CRITICAL, $message, $context); } /** - * Runtime errors that do not require immediate action but should typically - * be logged and monitored. - * - * @param string $message - * @param array $context - * @return null + * {@inheritDoc} */ public function error($message, array $context = array()) { - return $this->log(LogLevel::ERROR, $message, $context); + $this->log(LogLevel::ERROR, $message, $context); } /** - * Exceptional occurrences that are not errors. - * - * Example: Use of deprecated APIs, poor use of an API, undesirable things - * that are not necessarily wrong. - * - * @param string $message - * @param array $context - * @return null + * {@inheritDoc} */ public function warning($message, array $context = array()) { - return $this->log(LogLevel::WARNING, $message, $context); + $this->log(LogLevel::WARNING, $message, $context); } /** - * Normal but significant events. - * - * @param string $message - * @param array $context - * @return null + * {@inheritDoc} */ public function notice($message, array $context = array()) { - return $this->log(LogLevel::NOTICE, $message, $context); + $this->log(LogLevel::NOTICE, $message, $context); } /** - * Interesting events. - * - * Example: User logs in, SQL logs. - * - * @param string $message - * @param array $context - * @return null + * {@inheritDoc} */ public function info($message, array $context = array()) { - return $this->log(LogLevel::INFO, $message, $context); + $this->log(LogLevel::INFO, $message, $context); } /** - * Detailed debug information. - * - * @param string $message - * @param array $context - * @return null + * {@inheritDoc} */ public function debug($message, array $context = array()) { - return $this->log(LogLevel::DEBUG, $message, $context); + $this->log(LogLevel::DEBUG, $message, $context); } /** - * Logs with an arbitrary level. - * - * @param mixed $level - * @param string $message - * @param array $context - * @return null + * {@inheritDoc} */ public function log($level, $message, array $context = array()) { if (in_array($level, array(LogLevel::EMERGENCY, LogLevel::ALERT, LogLevel::CRITICAL, LogLevel::ERROR))) { - $this->writeError(''.$message.'', true, self::NORMAL); + $this->writeError(''.$message.''); } elseif ($level === LogLevel::WARNING) { - $this->writeError(''.$message.'', true, self::NORMAL); + $this->writeError(''.$message.''); } elseif ($level === LogLevel::NOTICE) { $this->writeError(''.$message.'', true, self::VERBOSE); } elseif ($level === LogLevel::INFO) { diff --git a/app/vendor/composer/composer/src/Composer/IO/BufferIO.php b/app/vendor/composer/composer/src/Composer/IO/BufferIO.php index db4671341..e86500e2d 100644 --- a/app/vendor/composer/composer/src/Composer/IO/BufferIO.php +++ b/app/vendor/composer/composer/src/Composer/IO/BufferIO.php @@ -24,6 +24,11 @@ */ class BufferIO extends ConsoleIO { + /** @var StringInput */ + protected $input; + /** @var StreamOutput */ + protected $output; + /** * @param string $input * @param int $verbosity @@ -73,7 +78,7 @@ public function setUserInputs(array $inputs) private function createStream(array $inputs) { - $stream = fopen('php://memory', 'r+', false); + $stream = fopen('php://memory', 'r+'); foreach ($inputs as $input) { fwrite($stream, $input.PHP_EOL); diff --git a/app/vendor/composer/composer/src/Composer/IO/ConsoleIO.php b/app/vendor/composer/composer/src/Composer/IO/ConsoleIO.php index 925a528be..d6aa538c1 100644 --- a/app/vendor/composer/composer/src/Composer/IO/ConsoleIO.php +++ b/app/vendor/composer/composer/src/Composer/IO/ConsoleIO.php @@ -14,6 +14,7 @@ use Composer\Question\StrictConfirmationQuestion; use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -35,9 +36,9 @@ class ConsoleIO extends BaseIO /** @var HelperSet */ protected $helperSet; /** @var string */ - protected $lastMessage; + protected $lastMessage = ''; /** @var string */ - protected $lastMessageErr; + protected $lastMessageErr = ''; /** @var float */ private $startTime; @@ -158,13 +159,6 @@ private function doWrite($messages, $newline, $stderr, $verbosity, $raw = false) return; } - // hack to keep our usage BC with symfony<2.8 versions - // this removes the quiet output but there is no way around it - // see https://github.com/composer/composer/pull/4913 - if (OutputInterface::VERBOSITY_QUIET === 0) { - $sfVerbosity = OutputInterface::OUTPUT_NORMAL; - } - if ($raw) { if ($sfVerbosity === OutputInterface::OUTPUT_NORMAL) { $sfVerbosity = OutputInterface::OUTPUT_RAW; @@ -253,6 +247,15 @@ private function doOverwrite($messages, $newline, $size, $stderr, $verbosity) } } + /** + * @param int $max + * @return ProgressBar + */ + public function getProgressBar($max = 0) + { + return new ProgressBar($this->getErrorOutput(), $max); + } + /** * {@inheritDoc} */ diff --git a/app/vendor/composer/composer/src/Composer/IO/IOInterface.php b/app/vendor/composer/composer/src/Composer/IO/IOInterface.php index 5766ba479..fd2a2d503 100644 --- a/app/vendor/composer/composer/src/Composer/IO/IOInterface.php +++ b/app/vendor/composer/composer/src/Composer/IO/IOInterface.php @@ -13,13 +13,14 @@ namespace Composer\IO; use Composer\Config; +use Psr\Log\LoggerInterface; /** * The Input/Output helper interface. * * @author François Pluchino */ -interface IOInterface +interface IOInterface extends LoggerInterface { const QUIET = 1; const NORMAL = 2; @@ -80,6 +81,24 @@ public function write($messages, $newline = true, $verbosity = self::NORMAL); */ public function writeError($messages, $newline = true, $verbosity = self::NORMAL); + /** + * Writes a message to the output, without formatting it. + * + * @param string|array $messages The message as an array of lines or a single string + * @param bool $newline Whether to add a newline or not + * @param int $verbosity Verbosity level from the VERBOSITY_* constants + */ + public function writeRaw($messages, $newline = true, $verbosity = self::NORMAL); + + /** + * Writes a message to the error output, without formatting it. + * + * @param string|array $messages The message as an array of lines or a single string + * @param bool $newline Whether to add a newline or not + * @param int $verbosity Verbosity level from the VERBOSITY_* constants + */ + public function writeErrorRaw($messages, $newline = true, $verbosity = self::NORMAL); + /** * Overwrites a previous message to the output. * @@ -107,7 +126,7 @@ public function overwriteError($messages, $newline = true, $size = null, $verbos * @param string $default The default answer if none is given by the user * * @throws \RuntimeException If there is no data to read in the input stream - * @return string The user answer + * @return string|null The user answer */ public function ask($question, $default = null); @@ -145,7 +164,7 @@ public function askAndValidate($question, $validator, $attempts = null, $default * * @param string $question The question to ask * - * @return string The answer + * @return string|null The answer */ public function askAndHideAnswer($question); @@ -160,7 +179,7 @@ public function askAndHideAnswer($question); * @param bool $multiselect Select more than one value separated by comma * * @throws \InvalidArgumentException - * @return int|string|array The selected value or values (the key of the choices array) + * @return int|string|array|bool The selected value or values (the key of the choices array) */ public function select($question, $choices, $default, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false); diff --git a/app/vendor/composer/composer/src/Composer/IO/NullIO.php b/app/vendor/composer/composer/src/Composer/IO/NullIO.php index cc6d5eba1..9dc9d3a0f 100644 --- a/app/vendor/composer/composer/src/Composer/IO/NullIO.php +++ b/app/vendor/composer/composer/src/Composer/IO/NullIO.php @@ -106,7 +106,7 @@ public function askConfirmation($question, $default = true) /** * {@inheritDoc} */ - public function askAndValidate($question, $validator, $attempts = false, $default = null) + public function askAndValidate($question, $validator, $attempts = null, $default = null) { return $default; } diff --git a/app/vendor/composer/composer/src/Composer/InstalledVersions.php b/app/vendor/composer/composer/src/Composer/InstalledVersions.php new file mode 100644 index 000000000..7c5502ca4 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/InstalledVersions.php @@ -0,0 +1,337 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + */ +class InstalledVersions +{ + private static $installed; + private static $canGetVendors; + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']); + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints($constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + + if (self::$canGetVendors) { + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; + if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { + self::$installed = $installed[count($installed) - 1]; + } + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = require __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + $installed[] = self::$installed; + + return $installed; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Installer.php b/app/vendor/composer/composer/src/Composer/Installer.php index 21e211285..d04005132 100644 --- a/app/vendor/composer/composer/src/Composer/Installer.php +++ b/app/vendor/composer/composer/src/Composer/Installer.php @@ -13,43 +13,50 @@ namespace Composer; use Composer\Autoload\AutoloadGenerator; +use Composer\Console\GithubActionError; use Composer\DependencyResolver\DefaultPolicy; +use Composer\DependencyResolver\LocalRepoTransaction; +use Composer\DependencyResolver\LockTransaction; use Composer\DependencyResolver\Operation\UpdateOperation; use Composer\DependencyResolver\Operation\InstallOperation; use Composer\DependencyResolver\Operation\UninstallOperation; -use Composer\DependencyResolver\Operation\MarkAliasUninstalledOperation; -use Composer\DependencyResolver\Operation\OperationInterface; -use Composer\DependencyResolver\PolicyInterface; use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; -use Composer\DependencyResolver\Rule; use Composer\DependencyResolver\Solver; use Composer\DependencyResolver\SolverProblemsException; +use Composer\DependencyResolver\PolicyInterface; use Composer\Downloader\DownloadManager; use Composer\EventDispatcher\EventDispatcher; use Composer\Installer\InstallationManager; use Composer\Installer\InstallerEvents; -use Composer\Installer\NoopInstaller; use Composer\Installer\SuggestedPackagesReporter; use Composer\IO\IOInterface; use Composer\Package\AliasPackage; +use Composer\Package\RootAliasPackage; use Composer\Package\BasePackage; use Composer\Package\CompletePackage; use Composer\Package\CompletePackageInterface; use Composer\Package\Link; use Composer\Package\Loader\ArrayLoader; use Composer\Package\Dumper\ArrayDumper; +use Composer\Package\Version\VersionParser; +use Composer\Package\Package; +use Composer\Repository\ArrayRepository; +use Composer\Repository\RepositorySet; +use Composer\Repository\CompositeRepository; use Composer\Semver\Constraint\Constraint; use Composer\Package\Locker; -use Composer\Package\PackageInterface; use Composer\Package\RootPackageInterface; -use Composer\Repository\CompositeRepository; use Composer\Repository\InstalledArrayRepository; +use Composer\Repository\InstalledRepositoryInterface; +use Composer\Repository\InstalledRepository; +use Composer\Repository\RootPackageRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryInterface; use Composer\Repository\RepositoryManager; -use Composer\Repository\WritableRepositoryInterface; +use Composer\Repository\LockArrayRepository; use Composer\Script\ScriptEvents; +use Composer\Util\Platform; /** * @author Jordi Boggiano @@ -74,6 +81,12 @@ class Installer */ protected $package; + // TODO can we get rid of the below and just use the package itself? + /** + * @var RootPackageInterface + */ + protected $fixedRootPackage; + /** * @var DownloadManager */ @@ -109,27 +122,29 @@ class Installer protected $optimizeAutoloader = false; protected $classMapAuthoritative = false; protected $apcuAutoloader = false; + protected $apcuAutoloaderPrefix; protected $devMode = false; protected $dryRun = false; protected $verbose = false; protected $update = false; + protected $install = true; protected $dumpAutoloader = true; protected $runScripts = true; protected $ignorePlatformReqs = false; protected $preferStable = false; protected $preferLowest = false; - protected $skipSuggest = false; protected $writeLock; protected $executeOperations = true; + /** @var bool */ + protected $updateMirrors = false; /** * Array of package names/globs flagged for update * * @var array|null */ - protected $updateWhitelist = null; // TODO 2.0 rename to updateAllowList - protected $whitelistDependencies = false; // TODO 2.0 rename to allowListTransitiveDependencies - protected $whitelistAllDependencies = false; // TODO 2.0 rename to allowListAllDependencies + protected $updateAllowList = null; + protected $updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED; /** * @var SuggestedPackagesReporter @@ -137,9 +152,9 @@ class Installer protected $suggestedPackagesReporter; /** - * @var RepositoryInterface + * @var ?RepositoryInterface */ - protected $additionalInstalledRepository; + protected $additionalFixedRepository; /** * Constructor @@ -165,6 +180,7 @@ public function __construct(IOInterface $io, Config $config, RootPackageInterfac $this->installationManager = $installationManager; $this->eventDispatcher = $eventDispatcher; $this->autoloadGenerator = $autoloadGenerator; + $this->suggestedPackagesReporter = new SuggestedPackagesReporter($this->io); $this->writeLock = $config->get('lock'); } @@ -184,8 +200,15 @@ public function run() gc_collect_cycles(); gc_disable(); + if ($this->updateAllowList && $this->updateMirrors) { + throw new \RuntimeException("The installer options updateMirrors and updateAllowList are mutually exclusive."); + } + + $isFreshInstall = $this->repositoryManager->getLocalRepository()->isFresh(); + // Force update if there is no lock file present if (!$this->update && !$this->locker->isLocked()) { + $this->io->writeError('No composer.lock file present. Updating dependencies to latest instead of installing from lock file. See https://getcomposer.org/install for more information.'); $this->update = true; } @@ -195,15 +218,18 @@ public function run() $this->executeOperations = false; $this->writeLock = false; $this->dumpAutoloader = false; - $this->installationManager->addInstaller(new NoopInstaller); $this->mockLocalRepositories($this->repositoryManager); } + if ($this->update && !$this->install) { + $this->dumpAutoloader = false; + } + if ($this->runScripts) { - $_SERVER['COMPOSER_DEV_MODE'] = $this->devMode ? '1' : '0'; - putenv('COMPOSER_DEV_MODE='.$_SERVER['COMPOSER_DEV_MODE']); + Platform::putEnv('COMPOSER_DEV_MODE', $this->devMode ? '1' : '0'); // dispatch pre event + // should we treat this more strictly as running an update and then running an install, triggering events multiple times? $eventName = $this->update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD; $this->eventDispatcher->dispatchScript($eventName, $this->devMode); } @@ -211,46 +237,43 @@ public function run() $this->downloadManager->setPreferSource($this->preferSource); $this->downloadManager->setPreferDist($this->preferDist); - // create installed repo, this contains all local packages + platform packages (php & extensions) $localRepo = $this->repositoryManager->getLocalRepository(); - if ($this->update) { - $platformOverrides = $this->config->get('platform') ?: array(); - } else { - $platformOverrides = $this->locker->getPlatformOverrides(); - } - $platformRepo = new PlatformRepository(array(), $platformOverrides); - $installedRepo = $this->createInstalledRepo($localRepo, $platformRepo); - - $aliases = $this->getRootAliases(); - $this->aliasPlatformPackages($platformRepo, $aliases); - - if (!$this->suggestedPackagesReporter) { - $this->suggestedPackagesReporter = new SuggestedPackagesReporter($this->io); - } try { - list($res, $devPackages) = $this->doInstall($localRepo, $installedRepo, $platformRepo, $aliases); + if ($this->update) { + $res = $this->doUpdate($localRepo, $this->install); + } else { + $res = $this->doInstall($localRepo); + } if ($res !== 0) { return $res; } } catch (\Exception $e) { - if ($this->executeOperations && $this->config->get('notify-on-install')) { + if ($this->executeOperations && $this->install && $this->config->get('notify-on-install')) { $this->installationManager->notifyInstalls($this->io); } throw $e; } - if ($this->executeOperations && $this->config->get('notify-on-install')) { + if ($this->executeOperations && $this->install && $this->config->get('notify-on-install')) { $this->installationManager->notifyInstalls($this->io); } - // output suggestions if we're in dev mode - if ($this->devMode && !$this->skipSuggest) { - $this->suggestedPackagesReporter->output($installedRepo); + if ($this->update) { + $installedRepo = new InstalledRepository(array( + $this->locker->getLockedRepository($this->devMode), + $this->createPlatformRepo(false), + new RootPackageRepository(clone $this->package), + )); + if ($isFreshInstall) { + $this->suggestedPackagesReporter->addSuggestionsFromPackage($this->package); + } + $this->suggestedPackagesReporter->outputMinimalistic($installedRepo); } - # Find abandoned packages and warn user - foreach ($localRepo->getPackages() as $package) { + // Find abandoned packages and warn user + $lockedRepository = $this->locker->getLockedRepository(true); + foreach ($lockedRepository->getPackages() as $package) { if (!$package instanceof CompletePackage || !$package->isAbandoned()) { continue; } @@ -268,30 +291,6 @@ public function run() ); } - // write lock - if ($this->update && $this->writeLock) { - $localRepo->reload(); - - $platformReqs = $this->extractPlatformRequirements($this->package->getRequires()); - $platformDevReqs = $this->extractPlatformRequirements($this->package->getDevRequires()); - - $updatedLock = $this->locker->setLockData( - array_diff($localRepo->getCanonicalPackages(), $devPackages), - $devPackages, - $platformReqs, - $platformDevReqs, - $aliases, - $this->package->getMinimumStability(), - $this->package->getStabilityFlags(), - $this->preferStable || $this->package->getPreferStable(), - $this->preferLowest, - $this->config->get('platform') ?: array() - ); - if ($updatedLock) { - $this->io->writeError('Writing lock file'); - } - } - if ($this->dumpAutoloader) { // write autoloader if ($this->optimizeAutoloader) { @@ -300,14 +299,14 @@ public function run() $this->io->writeError('Generating autoload files'); } - $this->autoloadGenerator->setDevMode($this->devMode); $this->autoloadGenerator->setClassMapAuthoritative($this->classMapAuthoritative); - $this->autoloadGenerator->setApcu($this->apcuAutoloader); + $this->autoloadGenerator->setApcu($this->apcuAutoloader, $this->apcuAutoloaderPrefix); $this->autoloadGenerator->setRunScripts($this->runScripts); + $this->autoloadGenerator->setIgnorePlatformRequirements($this->ignorePlatformReqs); $this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader); } - if ($this->executeOperations) { + if ($this->install && $this->executeOperations) { // force binaries re-generation in case they are missing foreach ($localRepo->getPackages() as $package) { $this->installationManager->ensureBinariesPresence($package); @@ -346,305 +345,188 @@ public function run() return 0; } - /** - * @param RepositoryInterface $localRepo - * @param RepositoryInterface $installedRepo - * @param PlatformRepository $platformRepo - * @param array $aliases - * @return array [int, PackageInterfaces[]|null] with the exit code and an array of dev packages on update, or null on install - */ - protected function doInstall($localRepo, $installedRepo, $platformRepo, $aliases) + protected function doUpdate(InstalledRepositoryInterface $localRepo, $doInstall) { - // init vars + $platformRepo = $this->createPlatformRepo(true); + $aliases = $this->getRootAliases(true); + $lockedRepository = null; - $repositories = null; - // initialize locked repo if we are installing from lock or in a partial update - // and a lock file is present as we need to force install non-allowed lock file - // packages in that case - if (!$this->update || (!empty($this->updateWhitelist) && $this->locker->isLocked())) { - try { - $lockedRepository = $this->locker->getLockedRepository($this->devMode); - } catch (\RuntimeException $e) { - // if there are dev requires, then we really can not install - if ($this->package->getDevRequires()) { - throw $e; - } - // no require-dev in composer.json and the lock file was created with no dev info, so skip them - $lockedRepository = $this->locker->getLockedRepository(); + try { + if ($this->locker->isLocked()) { + $lockedRepository = $this->locker->getLockedRepository(true); + } + } catch (\Seld\JsonLint\ParsingException $e) { + if ($this->updateAllowList || $this->updateMirrors) { + // in case we are doing a partial update or updating mirrors, the lock file is needed so we error + throw $e; } + // otherwise, ignoring parse errors as the lock file will be regenerated from scratch when + // doing a full update } - $this->allowListUpdateDependencies( - $lockedRepository ?: $localRepo, - $this->package->getRequires(), - $this->package->getDevRequires() - ); + if (($this->updateAllowList || $this->updateMirrors) && !$lockedRepository) { + $this->io->writeError('Cannot update ' . ($this->updateMirrors ? 'lock file information' : 'only a partial set of packages') . ' without a lock file present. Run `composer update` to generate a lock file.', true, IOInterface::QUIET); + + return 1; + } $this->io->writeError('Loading composer repositories with package information'); - // creating repository pool - $policy = $this->createPolicy(); - $pool = $this->createPool($this->update ? null : $lockedRepository); - $pool->addRepository($installedRepo, $aliases); - if ($this->update) { - $repositories = $this->repositoryManager->getRepositories(); - foreach ($repositories as $repository) { - $pool->addRepository($repository, $aliases); - } + // creating repository set + $policy = $this->createPolicy(true); + $repositorySet = $this->createRepositorySet(true, $platformRepo, $aliases); + $repositories = $this->repositoryManager->getRepositories(); + foreach ($repositories as $repository) { + $repositorySet->addRepository($repository); } - // Add the locked repository after the others in case we are doing a - // partial update so missing packages can be found there still. - // For installs from lock it's the only one added so it is first if ($lockedRepository) { - $pool->addRepository($lockedRepository, $aliases); + $repositorySet->addRepository($lockedRepository); } - // creating requirements request - $request = $this->createRequest($this->package, $platformRepo); - - if ($this->update) { - // remove unstable packages from the localRepo if they don't match the current stability settings - $removedUnstablePackages = array(); - foreach ($localRepo->getPackages() as $package) { - if ( - !$pool->isPackageAcceptable($package->getNames(), $package->getStability()) - && $this->installationManager->isPackageInstalled($localRepo, $package) - ) { - $removedUnstablePackages[$package->getName()] = true; - $request->remove($package->getName(), new Constraint('=', $package->getVersion())); - } - } - - $this->io->writeError('Updating dependencies'.($this->devMode ? ' (including require-dev)' : '').''); - - $request->updateAll(); - - $links = array_merge($this->package->getRequires(), $this->package->getDevRequires()); - - foreach ($links as $link) { - $request->install($link->getTarget(), $link->getConstraint()); - } - - // if the updateWhitelist is enabled, packages not in it are also fixed - // to the version specified in the lock, or their currently installed version - if ($this->updateWhitelist) { - $currentPackages = $this->getCurrentPackages($installedRepo); - - // collect packages to fixate from root requirements as well as installed packages - $candidates = array(); - foreach ($links as $link) { - $candidates[$link->getTarget()] = true; - $rootRequires[$link->getTarget()] = $link; - } - foreach ($currentPackages as $package) { - $candidates[$package->getName()] = true; - } + $request = $this->createRequest($this->fixedRootPackage, $platformRepo, $lockedRepository); + $this->requirePackagesForUpdate($request, $lockedRepository, true); - // fix them to the version in lock (or currently installed) if they are not updateable - foreach ($candidates as $candidate => $dummy) { - foreach ($currentPackages as $curPackage) { - if ($curPackage->getName() === $candidate) { - if (!$this->isUpdateable($curPackage) && !isset($removedUnstablePackages[$curPackage->getName()])) { - $constraint = new Constraint('=', $curPackage->getVersion()); - $description = $this->locker->isLocked() ? '(locked at' : '(installed at'; - $requiredAt = isset($rootRequires[$candidate]) ? ', required as ' . $rootRequires[$candidate]->getPrettyConstraint() : ''; - $constraint->setPrettyString($description . ' ' . $curPackage->getPrettyVersion() . $requiredAt . ')'); - $request->install($curPackage->getName(), $constraint); - } - break; - } - } - } - } - } else { - $this->io->writeError('Installing dependencies'.($this->devMode ? ' (including require-dev)' : '').' from lock file'); - - if (!$this->locker->isFresh()) { - $this->io->writeError('Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. It is recommended that you run `composer update` or `composer update `.', true, IOInterface::QUIET); - } - - foreach ($lockedRepository->getPackages() as $package) { - $version = $package->getVersion(); - if (isset($aliases[$package->getName()][$version])) { - $version = $aliases[$package->getName()][$version]['alias_normalized']; - } - $constraint = new Constraint('=', $version); - $constraint->setPrettyString($package->getPrettyVersion()); - $request->install($package->getName(), $constraint); - } - - foreach ($this->locker->getPlatformRequirements($this->devMode) as $link) { - $request->install($link->getTarget(), $link->getConstraint()); - } + // pass the allow list into the request, so the pool builder can apply it + if ($this->updateAllowList) { + $request->setUpdateAllowList($this->updateAllowList, $this->updateAllowTransitiveDependencies); } - // force dev packages to have the latest links if we update or install from a (potentially new) lock - $this->processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, 'force-links'); + $pool = $repositorySet->createPool($request, $this->io, $this->eventDispatcher); + + $this->io->writeError('Updating dependencies'); // solve dependencies - $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $this->devMode, $policy, $pool, $installedRepo, $request); - $solver = new Solver($policy, $pool, $installedRepo, $this->io); + $solver = new Solver($policy, $pool, $this->io); try { - $operations = $solver->solve($request, $this->ignorePlatformReqs); + $lockTransaction = $solver->solve($request, $this->ignorePlatformReqs); $ruleSetSize = $solver->getRuleSetSize(); $solver = null; } catch (SolverProblemsException $e) { - $this->io->writeError('Your requirements could not be resolved to an installable set of packages.', true, IOInterface::QUIET); - $this->io->writeError($e->getMessage()); - if ($this->update && !$this->devMode) { + $err = 'Your requirements could not be resolved to an installable set of packages.'; + $prettyProblem = $e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose()); + + $this->io->writeError(''. $err .'', true, IOInterface::QUIET); + $this->io->writeError($prettyProblem); + if (!$this->devMode) { $this->io->writeError('Running update with --no-dev does not mean require-dev is ignored, it just means the packages will not be installed. If dev requirements are blocking the update you have to resolve those problems.', true, IOInterface::QUIET); } - return array(max(1, $e->getCode()), array()); - } - - // force dev packages to be updated if we update or install from a (potentially new) lock - $operations = $this->processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, 'force-updates', $operations); + $ghe = new GithubActionError($this->io); + $ghe->emit($err."\n".$prettyProblem); - $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $this->devMode, $policy, $pool, $installedRepo, $request, $operations); + return max(1, $e->getCode()); + } $this->io->writeError("Analyzed ".count($pool)." packages to resolve dependencies", true, IOInterface::VERBOSE); $this->io->writeError("Analyzed ".$ruleSetSize." rules to resolve dependencies", true, IOInterface::VERBOSE); - // execute operations - if (!$operations) { - $this->io->writeError('Nothing to install or update'); + if (!$lockTransaction->getOperations()) { + $this->io->writeError('Nothing to modify in lock file'); } - $operations = $this->movePluginsToFront($operations); - $operations = $this->moveUninstallsToFront($operations); - - // extract dev packages and mark them to be skipped if it's a --no-dev install or update - // we also force them to be uninstalled if they are present in the local repo - if ($this->update) { - $devPackages = $this->extractDevPackages($operations, $localRepo, $platformRepo, $aliases); - if (!$this->devMode) { - $operations = $this->filterDevPackageOperations($devPackages, $operations, $localRepo); - } - } else { - $devPackages = null; + $exitCode = $this->extractDevPackages($lockTransaction, $platformRepo, $aliases, $policy, $lockedRepository); + if ($exitCode !== 0) { + return $exitCode; } - if ($operations) { - $installs = $updates = $uninstalls = array(); - foreach ($operations as $operation) { + // write lock + $platformReqs = $this->extractPlatformRequirements($this->package->getRequires()); + $platformDevReqs = $this->extractPlatformRequirements($this->package->getDevRequires()); + + $installsUpdates = $uninstalls = array(); + if ($lockTransaction->getOperations()) { + $installNames = $updateNames = $uninstallNames = array(); + foreach ($lockTransaction->getOperations() as $operation) { if ($operation instanceof InstallOperation) { - $installs[] = $operation->getPackage()->getPrettyName().':'.$operation->getPackage()->getFullPrettyVersion(); + $installsUpdates[] = $operation; + $installNames[] = $operation->getPackage()->getPrettyName().':'.$operation->getPackage()->getFullPrettyVersion(); } elseif ($operation instanceof UpdateOperation) { - $updates[] = $operation->getTargetPackage()->getPrettyName().':'.$operation->getTargetPackage()->getFullPrettyVersion(); + // when mirrors/metadata from a package gets updated we do not want to list it as an + // update in the output as it is only an internal lock file metadata update + if ($this->updateMirrors + && $operation->getInitialPackage()->getName() == $operation->getTargetPackage()->getName() + && $operation->getInitialPackage()->getVersion() == $operation->getTargetPackage()->getVersion() + ) { + continue; + } + + $installsUpdates[] = $operation; + $updateNames[] = $operation->getTargetPackage()->getPrettyName().':'.$operation->getTargetPackage()->getFullPrettyVersion(); } elseif ($operation instanceof UninstallOperation) { - $uninstalls[] = $operation->getPackage()->getPrettyName(); + $uninstalls[] = $operation; + $uninstallNames[] = $operation->getPackage()->getPrettyName(); } } $this->io->writeError(sprintf( - "Package operations: %d install%s, %d update%s, %d removal%s", - count($installs), - 1 === count($installs) ? '' : 's', - count($updates), - 1 === count($updates) ? '' : 's', + "Lock file operations: %d install%s, %d update%s, %d removal%s", + count($installNames), + 1 === count($installNames) ? '' : 's', + count($updateNames), + 1 === count($updateNames) ? '' : 's', count($uninstalls), 1 === count($uninstalls) ? '' : 's' )); - if ($installs) { - $this->io->writeError("Installs: ".implode(', ', $installs), true, IOInterface::VERBOSE); + if ($installNames) { + $this->io->writeError("Installs: ".implode(', ', $installNames), true, IOInterface::VERBOSE); } - if ($updates) { - $this->io->writeError("Updates: ".implode(', ', $updates), true, IOInterface::VERBOSE); + if ($updateNames) { + $this->io->writeError("Updates: ".implode(', ', $updateNames), true, IOInterface::VERBOSE); } if ($uninstalls) { - $this->io->writeError("Removals: ".implode(', ', $uninstalls), true, IOInterface::VERBOSE); + $this->io->writeError("Removals: ".implode(', ', $uninstallNames), true, IOInterface::VERBOSE); } } - foreach ($operations as $operation) { - // collect suggestions - $jobType = $operation->getJobType(); - if ('install' === $jobType) { - $this->suggestedPackagesReporter->addSuggestionsFromPackage($operation->getPackage()); - } - - // updating, force dev packages' references if they're in root package refs - if ($this->update) { - $package = null; - if ('update' === $jobType) { - $package = $operation->getTargetPackage(); - } elseif ('install' === $jobType) { - $package = $operation->getPackage(); - } - if ($package && $package->isDev()) { - $references = $this->package->getReferences(); - if (isset($references[$package->getName()])) { - $this->updateInstallReferences($package, $references[$package->getName()]); - } - } - if ('update' === $jobType) { - $targetPackage = $operation->getTargetPackage(); - if ($targetPackage->isDev()) { - $initialPackage = $operation->getInitialPackage(); - if ($targetPackage->getVersion() === $initialPackage->getVersion() - && (!$targetPackage->getSourceReference() || $targetPackage->getSourceReference() === $initialPackage->getSourceReference()) - && (!$targetPackage->getDistReference() || $targetPackage->getDistReference() === $initialPackage->getDistReference()) - ) { - $this->io->writeError(' - Skipping update of ' . $targetPackage->getPrettyName() . ' to the same reference-locked version', true, IOInterface::DEBUG); - $this->io->writeError('', true, IOInterface::DEBUG); - - continue; - } - } - } - } - - $event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($jobType); - if (defined($event) && $this->runScripts) { - $this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $policy, $pool, $installedRepo, $request, $operations, $operation); + $sortByName = function ($a, $b) { + if ($a instanceof UpdateOperation) { + $a = $a->getTargetPackage()->getName(); + } else { + $a = $a->getPackage()->getName(); } - - // output non-alias ops when not executing operations (i.e. dry run), output alias ops in debug verbosity - if (!$this->executeOperations && false === strpos($operation->getJobType(), 'Alias')) { - $this->io->writeError(' - ' . $operation); - } elseif ($this->io->isDebug() && false !== strpos($operation->getJobType(), 'Alias')) { - $this->io->writeError(' - ' . $operation); + if ($b instanceof UpdateOperation) { + $b = $b->getTargetPackage()->getName(); + } else { + $b = $b->getPackage()->getName(); } - $this->installationManager->execute($localRepo, $operation); - - // output reasons why the operation was ran, only for install/update operations - if ($this->verbose && $this->io->isVeryVerbose() && in_array($jobType, array('install', 'update'))) { - $reason = $operation->getReason(); - if ($reason instanceof Rule) { - switch ($reason->getReason()) { - case Rule::RULE_JOB_INSTALL: - $this->io->writeError(' REASON: Required by the root package: '.$reason->getPrettyString($pool)); - $this->io->writeError(''); - break; - case Rule::RULE_PACKAGE_REQUIRES: - $this->io->writeError(' REASON: '.$reason->getPrettyString($pool)); - $this->io->writeError(''); - break; - } - } - } + return strcmp($a, $b); + }; + usort($uninstalls, $sortByName); + usort($installsUpdates, $sortByName); - if ($this->executeOperations || $this->writeLock) { - $localRepo->write(); + foreach (array_merge($uninstalls, $installsUpdates) as $operation) { + // collect suggestions + if ($operation instanceof InstallOperation) { + $this->suggestedPackagesReporter->addSuggestionsFromPackage($operation->getPackage()); } - $event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($jobType); - if (defined($event) && $this->runScripts) { - $this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $policy, $pool, $installedRepo, $request, $operations, $operation); + // output op, but alias op only in debug verbosity + if (false === strpos($operation->getOperationType(), 'Alias') || $this->io->isDebug()) { + $this->io->writeError(' - ' . $operation->show(true)); } } - if ($this->executeOperations) { - // force source/dist urls to be updated for all packages - $this->processPackageUrls($pool, $policy, $localRepo, $repositories); - $localRepo->write(); + $updatedLock = $this->locker->setLockData( + $lockTransaction->getNewLockPackages(false, $this->updateMirrors), + $lockTransaction->getNewLockPackages(true, $this->updateMirrors), + $platformReqs, + $platformDevReqs, + $lockTransaction->getAliases($aliases), + $this->package->getMinimumStability(), + $this->package->getStabilityFlags(), + $this->preferStable || $this->package->getPreferStable(), + $this->preferLowest, + $this->config->get('platform') ?: array(), + $this->writeLock && $this->executeOperations + ); + if ($updatedLock && $this->writeLock && $this->executeOperations) { + $this->io->writeError('Writing lock file'); } // see https://github.com/composer/composer/issues/2764 - if ($operations) { + if ($this->executeOperations && count($lockTransaction->getOperations()) > 0) { $vendorDir = $this->config->get('vendor-dir'); if (is_dir($vendorDir)) { // suppress errors as this fails sometimes on OSX for no apparent reason @@ -653,231 +535,205 @@ protected function doInstall($localRepo, $installedRepo, $platformRepo, $aliases } } - return array(0, $devPackages); + if ($doInstall) { + // TODO ensure lock is used from locker as-is, since it may not have been written to disk in case of executeOperations == false + return $this->doInstall($localRepo, true); + } + + return 0; } /** - * Extracts the dev packages out of the localRepo - * - * This works by faking the operations so we can see what the dev packages - * would be at the end of the operation execution. This lets us then remove - * the dev packages from the list of operations accordingly if we are in a - * --no-dev install or update. - * - * @return array + * Run the solver a second time on top of the existing update result with only the current result set in the pool + * and see what packages would get removed if we only had the non-dev packages in the solver request */ - private function extractDevPackages(array $operations, RepositoryInterface $localRepo, PlatformRepository $platformRepo, array $aliases) + protected function extractDevPackages(LockTransaction $lockTransaction, PlatformRepository $platformRepo, array $aliases, PolicyInterface $policy, LockArrayRepository $lockedRepository = null) { if (!$this->package->getDevRequires()) { - return array(); + return 0; } - // fake-apply all operations to this clone of the local repo so we see the complete set of package we would end up with - $tempLocalRepo = clone $localRepo; - foreach ($operations as $operation) { - switch ($operation->getJobType()) { - case 'install': - case 'markAliasInstalled': - if (!$tempLocalRepo->hasPackage($operation->getPackage())) { - $tempLocalRepo->addPackage(clone $operation->getPackage()); - } - break; + $resultRepo = new ArrayRepository(array()); + $loader = new ArrayLoader(null, true); + $dumper = new ArrayDumper(); + foreach ($lockTransaction->getNewLockPackages(false) as $pkg) { + $resultRepo->addPackage($loader->load($dumper->dump($pkg))); + } - case 'uninstall': - case 'markAliasUninstalled': - $tempLocalRepo->removePackage($operation->getPackage()); - break; + $repositorySet = $this->createRepositorySet(true, $platformRepo, $aliases); + $repositorySet->addRepository($resultRepo); - case 'update': - $tempLocalRepo->removePackage($operation->getInitialPackage()); - if (!$tempLocalRepo->hasPackage($operation->getTargetPackage())) { - $tempLocalRepo->addPackage(clone $operation->getTargetPackage()); - } - break; + $request = $this->createRequest($this->fixedRootPackage, $platformRepo); + $this->requirePackagesForUpdate($request, $lockedRepository, false); - default: - throw new \LogicException('Unknown type: '.$operation->getJobType()); - } - } + $pool = $repositorySet->createPoolWithAllPackages(); - // we have to reload the local repo to handle aliases properly - // but as it is not persisted on disk we use a loader/dumper - // to reload it in memory - $localRepo = new InstalledArrayRepository(array()); - $loader = new ArrayLoader(null, true); - $dumper = new ArrayDumper(); - foreach ($tempLocalRepo->getCanonicalPackages() as $pkg) { - $localRepo->addPackage($loader->load($dumper->dump($pkg))); - } - unset($tempLocalRepo, $loader, $dumper); - - $policy = $this->createPolicy(); - $pool = $this->createPool(); - $installedRepo = $this->createInstalledRepo($localRepo, $platformRepo); - $pool->addRepository($installedRepo, $aliases); - - // creating requirements request without dev requirements - $request = $this->createRequest($this->package, $platformRepo); - $request->updateAll(); - foreach ($this->package->getRequires() as $link) { - $request->install($link->getTarget(), $link->getConstraint()); - } + $solver = new Solver($policy, $pool, $this->io); + try { + $nonDevLockTransaction = $solver->solve($request, $this->ignorePlatformReqs); + $solver = null; + } catch (SolverProblemsException $e) { + $err = 'Unable to find a compatible set of packages based on your non-dev requirements alone.'; + $prettyProblem = $e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose(), true); - // solve deps to see which get removed - $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $request); - $solver = new Solver($policy, $pool, $installedRepo, $this->io); - $ops = $solver->solve($request, $this->ignorePlatformReqs); - $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $request, $ops); + $this->io->writeError(''. $err .'', true, IOInterface::QUIET); + $this->io->writeError('Your requirements can be resolved successfully when require-dev packages are present.'); + $this->io->writeError('You may need to move packages from require-dev or some of their dependencies to require.'); + $this->io->writeError($prettyProblem); - $devPackages = array(); - foreach ($ops as $op) { - if ($op->getJobType() === 'uninstall') { - $devPackages[] = $op->getPackage(); - } + $ghe = new GithubActionError($this->io); + $ghe->emit($err."\n".$prettyProblem); + + return max(1, $e->getCode()); } - return $devPackages; + $lockTransaction->setNonDevPackages($nonDevLockTransaction); + + return 0; } /** - * @return OperationInterface[] filtered operations, dev packages are uninstalled and all operations on them ignored + * @param InstalledRepositoryInterface $localRepo + * @param bool $alreadySolved Whether the function is called as part of an update command or independently + * @return int exit code */ - private function filterDevPackageOperations(array $devPackages, array $operations, RepositoryInterface $localRepo) + protected function doInstall(InstalledRepositoryInterface $localRepo, $alreadySolved = false) { - $finalOps = array(); - $packagesToSkip = array(); - foreach ($devPackages as $pkg) { - $packagesToSkip[$pkg->getName()] = true; - if ($installedDevPkg = $localRepo->findPackage($pkg->getName(), '*')) { - if ($installedDevPkg instanceof AliasPackage) { - $finalOps[] = new MarkAliasUninstalledOperation($installedDevPkg, 'non-dev install removing it'); - $installedDevPkg = $installedDevPkg->getAliasOf(); - } - $finalOps[] = new UninstallOperation($installedDevPkg, 'non-dev install removing it'); + $this->io->writeError('Installing dependencies from lock file'.($this->devMode ? ' (including require-dev)' : '').''); + + $lockedRepository = $this->locker->getLockedRepository($this->devMode); + + // verify that the lock file works with the current platform repository + // we can skip this part if we're doing this as the second step after an update + if (!$alreadySolved) { + $this->io->writeError('Verifying lock file contents can be installed on current platform.'); + + $platformRepo = $this->createPlatformRepo(false); + // creating repository set + $policy = $this->createPolicy(false); + // use aliases from lock file only, so empty root aliases here + $repositorySet = $this->createRepositorySet(false, $platformRepo, array(), $lockedRepository); + $repositorySet->addRepository($lockedRepository); + + // creating requirements request + $request = $this->createRequest($this->fixedRootPackage, $platformRepo, $lockedRepository); + + if (!$this->locker->isFresh()) { + $this->io->writeError('Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. It is recommended that you run `composer update` or `composer update `.', true, IOInterface::QUIET); } - } - // skip operations applied on dev packages - foreach ($operations as $op) { - $package = $op->getJobType() === 'update' ? $op->getTargetPackage() : $op->getPackage(); - if (isset($packagesToSkip[$package->getName()])) { - continue; + foreach ($lockedRepository->getPackages() as $package) { + $request->fixLockedPackage($package); } - $finalOps[] = $op; - } + foreach ($this->locker->getPlatformRequirements($this->devMode) as $link) { + $request->requireName($link->getTarget(), $link->getConstraint()); + } - return $finalOps; - } + $pool = $repositorySet->createPool($request, $this->io, $this->eventDispatcher); - /** - * Workaround: if your packages depend on plugins, we must be sure - * that those are installed / updated first; else it would lead to packages - * being installed multiple times in different folders, when running Composer - * twice. - * - * While this does not fix the root-causes of https://github.com/composer/composer/issues/1147, - * it at least fixes the symptoms and makes usage of composer possible (again) - * in such scenarios. - * - * @param OperationInterface[] $operations - * @return OperationInterface[] reordered operation list - */ - private function movePluginsToFront(array $operations) - { - $pluginsNoDeps = array(); - $pluginsWithDeps = array(); - $pluginRequires = array(); - - foreach (array_reverse($operations, true) as $idx => $op) { - if ($op instanceof InstallOperation) { - $package = $op->getPackage(); - } elseif ($op instanceof UpdateOperation) { - $package = $op->getTargetPackage(); - } else { - continue; - } + // solve dependencies + $solver = new Solver($policy, $pool, $this->io); + try { + $lockTransaction = $solver->solve($request, $this->ignorePlatformReqs); + $solver = null; + + // installing the locked packages on this platform resulted in lock modifying operations, there wasn't a conflict, but the lock file as-is seems to not work on this system + if (0 !== count($lockTransaction->getOperations())) { + $this->io->writeError('Your lock file cannot be installed on this system without changes. Please run composer update.', true, IOInterface::QUIET); - // is this package a plugin? - $isPlugin = $package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer'; - - // is this a plugin or a dependency of a plugin? - if ($isPlugin || count(array_intersect($package->getNames(), $pluginRequires))) { - // get the package's requires, but filter out any platform requirements or 'composer-plugin-api' - $requires = array_filter(array_keys($package->getRequires()), function ($req) { - return $req !== 'composer-plugin-api' && !preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req); - }); - - // is this a plugin with no meaningful dependencies? - if ($isPlugin && !count($requires)) { - // plugins with no dependencies go to the very front - array_unshift($pluginsNoDeps, $op); - } else { - // capture the requirements for this package so those packages will be moved up as well - $pluginRequires = array_merge($pluginRequires, $requires); - // move the operation to the front - array_unshift($pluginsWithDeps, $op); + return 1; } + } catch (SolverProblemsException $e) { + $err = 'Your lock file does not contain a compatible set of packages. Please run composer update.'; + $prettyProblem = $e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose()); - unset($operations[$idx]); + $this->io->writeError(''. $err .'', true, IOInterface::QUIET); + $this->io->writeError($prettyProblem); + + $ghe = new GithubActionError($this->io); + $ghe->emit($err."\n".$prettyProblem); + + return max(1, $e->getCode()); } } - return array_merge($pluginsNoDeps, $pluginsWithDeps, $operations); - } + // TODO in how far do we need to do anything here to ensure dev packages being updated to latest in lock without version change are treated correctly? + $localRepoTransaction = new LocalRepoTransaction($lockedRepository, $localRepo); + $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_OPERATIONS_EXEC, $this->devMode, $this->executeOperations, $localRepoTransaction); - /** - * Removals of packages should be executed before installations in - * case two packages resolve to the same path (due to custom installers) - * - * @param OperationInterface[] $operations - * @return OperationInterface[] reordered operation list - */ - private function moveUninstallsToFront(array $operations) - { - $uninstOps = array(); - foreach ($operations as $idx => $op) { - if ($op instanceof UninstallOperation) { - $uninstOps[] = $op; - unset($operations[$idx]); + if (!$localRepoTransaction->getOperations()) { + $this->io->writeError('Nothing to install, update or remove'); + } + + if ($localRepoTransaction->getOperations()) { + $installs = $updates = $uninstalls = array(); + foreach ($localRepoTransaction->getOperations() as $operation) { + if ($operation instanceof InstallOperation) { + $installs[] = $operation->getPackage()->getPrettyName().':'.$operation->getPackage()->getFullPrettyVersion(); + } elseif ($operation instanceof UpdateOperation) { + $updates[] = $operation->getTargetPackage()->getPrettyName().':'.$operation->getTargetPackage()->getFullPrettyVersion(); + } elseif ($operation instanceof UninstallOperation) { + $uninstalls[] = $operation->getPackage()->getPrettyName(); + } + } + + $this->io->writeError(sprintf( + "Package operations: %d install%s, %d update%s, %d removal%s", + count($installs), + 1 === count($installs) ? '' : 's', + count($updates), + 1 === count($updates) ? '' : 's', + count($uninstalls), + 1 === count($uninstalls) ? '' : 's' + )); + if ($installs) { + $this->io->writeError("Installs: ".implode(', ', $installs), true, IOInterface::VERBOSE); + } + if ($updates) { + $this->io->writeError("Updates: ".implode(', ', $updates), true, IOInterface::VERBOSE); + } + if ($uninstalls) { + $this->io->writeError("Removals: ".implode(', ', $uninstalls), true, IOInterface::VERBOSE); + } + } + + if ($this->executeOperations) { + $localRepo->setDevPackageNames($this->locker->getDevPackageNames()); + $this->installationManager->execute($localRepo, $localRepoTransaction->getOperations(), $this->devMode, $this->runScripts); + } else { + foreach ($localRepoTransaction->getOperations() as $operation) { + // output op, but alias op only in debug verbosity + if (false === strpos($operation->getOperationType(), 'Alias') || $this->io->isDebug()) { + $this->io->writeError(' - ' . $operation->show(false)); + } } } - return array_merge($uninstOps, $operations); + return 0; } - /** - * @return RepositoryInterface - */ - private function createInstalledRepo(RepositoryInterface $localRepo, PlatformRepository $platformRepo) + protected function createPlatformRepo($forUpdate) { - // clone root package to have one in the installed repo that does not require anything - // we don't want it to be uninstallable, but its requirements should not conflict - // with the lock file for example - $installedRootPackage = clone $this->package; - $installedRootPackage->setRequires(array()); - $installedRootPackage->setDevRequires(array()); - - $repos = array( - $localRepo, - new InstalledArrayRepository(array($installedRootPackage)), - $platformRepo, - ); - $installedRepo = new CompositeRepository($repos); - if ($this->additionalInstalledRepository) { - $installedRepo->addRepository($this->additionalInstalledRepository); + if ($forUpdate) { + $platformOverrides = $this->config->get('platform') ?: array(); + } else { + $platformOverrides = $this->locker->getPlatformOverrides(); } - return $installedRepo; + return new PlatformRepository(array(), $platformOverrides); } /** + * @param bool $forUpdate + * @param PlatformRepository $platformRepo + * @param array $rootAliases * @param RepositoryInterface|null $lockedRepository - * @return Pool + * @return RepositorySet */ - private function createPool(RepositoryInterface $lockedRepository = null) + private function createRepositorySet($forUpdate, PlatformRepository $platformRepo, array $rootAliases = array(), $lockedRepository = null) { - if ($this->update) { + if ($forUpdate) { $minimumStability = $this->package->getMinimumStability(); $stabilityFlags = $this->package->getStabilityFlags(); @@ -894,30 +750,58 @@ private function createPool(RepositoryInterface $lockedRepository = null) } } - $rootConstraints = array(); + $rootRequires = array(); foreach ($requires as $req => $constraint) { // skip platform requirements from the root package to avoid filtering out existing platform packages - if ($this->ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) { + if ((true === $this->ignorePlatformReqs || (is_array($this->ignorePlatformReqs) && in_array($req, $this->ignorePlatformReqs, true))) && PlatformRepository::isPlatformPackage($req)) { continue; } if ($constraint instanceof Link) { - $rootConstraints[$req] = $constraint->getConstraint(); + $rootRequires[$req] = $constraint->getConstraint(); } else { - $rootConstraints[$req] = $constraint; + $rootRequires[$req] = $constraint; } } - return new Pool($minimumStability, $stabilityFlags, $rootConstraints); + $this->fixedRootPackage = clone $this->package; + $this->fixedRootPackage->setRequires(array()); + $this->fixedRootPackage->setDevRequires(array()); + + $stabilityFlags[$this->package->getName()] = BasePackage::$stabilities[VersionParser::parseStability($this->package->getVersion())]; + + $repositorySet = new RepositorySet($minimumStability, $stabilityFlags, $rootAliases, $this->package->getReferences(), $rootRequires); + $repositorySet->addRepository(new RootPackageRepository($this->fixedRootPackage)); + $repositorySet->addRepository($platformRepo); + if ($this->additionalFixedRepository) { + // allow using installed repos if needed to avoid warnings about installed repositories being used in the RepositorySet + // see https://github.com/composer/composer/pull/9574 + $additionalFixedRepositories = $this->additionalFixedRepository; + if ($additionalFixedRepositories instanceof CompositeRepository) { + $additionalFixedRepositories = $additionalFixedRepositories->getRepositories(); + } else { + $additionalFixedRepositories = array($additionalFixedRepositories); + } + foreach ($additionalFixedRepositories as $additionalFixedRepository) { + if ($additionalFixedRepository instanceof InstalledRepository || $additionalFixedRepository instanceof InstalledRepositoryInterface) { + $repositorySet->allowInstalledRepositories(); + break; + } + } + + $repositorySet->addRepository($this->additionalFixedRepository); + } + + return $repositorySet; } /** * @return DefaultPolicy */ - private function createPolicy() + private function createPolicy($forUpdate) { $preferStable = null; $preferLowest = null; - if (!$this->update) { + if (!$forUpdate) { $preferStable = $this->locker->getPreferStable(); $preferLowest = $this->locker->getPreferLowest(); } @@ -934,374 +818,90 @@ private function createPolicy() } /** - * @param RootPackageInterface $rootPackage - * @param PlatformRepository $platformRepo * @return Request */ - private function createRequest(RootPackageInterface $rootPackage, PlatformRepository $platformRepo) + private function createRequest(RootPackageInterface $rootPackage, PlatformRepository $platformRepo, LockArrayRepository $lockedRepository = null) { - $request = new Request(); + $request = new Request($lockedRepository); - $constraint = new Constraint('=', $rootPackage->getVersion()); - $constraint->setPrettyString($rootPackage->getPrettyVersion()); - $request->install($rootPackage->getName(), $constraint); + $request->fixPackage($rootPackage); + if ($rootPackage instanceof RootAliasPackage) { + $request->fixPackage($rootPackage->getAliasOf()); + } $fixedPackages = $platformRepo->getPackages(); - if ($this->additionalInstalledRepository) { - $additionalFixedPackages = $this->additionalInstalledRepository->getPackages(); - $fixedPackages = array_merge($fixedPackages, $additionalFixedPackages); + if ($this->additionalFixedRepository) { + $fixedPackages = array_merge($fixedPackages, $this->additionalFixedRepository->getPackages()); } // fix the version of all platform packages + additionally installed packages // to prevent the solver trying to remove or update those + // TODO why not replaces? $provided = $rootPackage->getProvides(); foreach ($fixedPackages as $package) { - $constraint = new Constraint('=', $package->getVersion()); - $constraint->setPrettyString($package->getPrettyVersion()); - // skip platform packages that are provided by the root package if ($package->getRepository() !== $platformRepo || !isset($provided[$package->getName()]) - || !$provided[$package->getName()]->getConstraint()->matches($constraint) + || !$provided[$package->getName()]->getConstraint()->matches(new Constraint('=', $package->getVersion())) ) { - $request->fix($package->getName(), $constraint); + $request->fixPackage($package); } } return $request; } - /** - * @param WritableRepositoryInterface $localRepo - * @param Pool $pool - * @param PolicyInterface $policy - * @param array $repositories - * @param RepositoryInterface $installedRepo - * @param RepositoryInterface $lockedRepository - * @param string $task - * @param array|null $operations - * @return array - */ - private function processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, $task, array $operations = null) + private function requirePackagesForUpdate(Request $request, LockArrayRepository $lockedRepository = null, $includeDevRequires = true) { - if ($task === 'force-updates' && null === $operations) { - throw new \InvalidArgumentException('Missing operations argument'); - } - if ($task === 'force-links') { - $operations = array(); - } - - if ($this->update && $this->updateWhitelist) { - $currentPackages = $this->getCurrentPackages($installedRepo); - } - - foreach ($localRepo->getCanonicalPackages() as $package) { - // skip non-dev packages - if (!$package->isDev()) { - continue; + // if we're updating mirrors we want to keep exactly the same versions installed which are in the lock file, but we want current remote metadata + if ($this->updateMirrors) { + $excludedPackages = array(); + if (!$includeDevRequires) { + $excludedPackages = array_flip($this->locker->getDevPackageNames()); } - // skip packages that will be updated/uninstalled - foreach ($operations as $operation) { - if (('update' === $operation->getJobType() && $operation->getInitialPackage()->equals($package)) - || ('uninstall' === $operation->getJobType() && $operation->getPackage()->equals($package)) - ) { - continue 2; + foreach ($lockedRepository->getPackages() as $lockedPackage) { + // exclude alias packages here as for root aliases, both alias and aliased are + // present in the lock repo and we only want to require the aliased version + if (!$lockedPackage instanceof AliasPackage && !isset($excludedPackages[$lockedPackage->getName()])) { + $request->requireName($lockedPackage->getName(), new Constraint('==', $lockedPackage->getVersion())); } } - - if ($this->update) { - // skip package if the allow list is enabled and it is not in it - if ($this->updateWhitelist && !$this->isUpdateable($package)) { - // check if non-updateable packages are out of date compared to the lock file to ensure we don't corrupt it - foreach ($currentPackages as $curPackage) { - if ($curPackage->isDev() && $curPackage->getName() === $package->getName() && $curPackage->getVersion() === $package->getVersion()) { - if ($task === 'force-links') { - $package->setRequires($curPackage->getRequires()); - $package->setConflicts($curPackage->getConflicts()); - $package->setProvides($curPackage->getProvides()); - $package->setReplaces($curPackage->getReplaces()); - } elseif ($task === 'force-updates') { - if (($curPackage->getSourceReference() && $curPackage->getSourceReference() !== $package->getSourceReference()) - || ($curPackage->getDistReference() && $curPackage->getDistReference() !== $package->getDistReference()) - ) { - $operations[] = new UpdateOperation($package, $curPackage); - } - } - - break; - } - } - - continue; - } - - // find similar packages (name/version) in all repositories - $matches = $pool->whatProvides($package->getName(), new Constraint('=', $package->getVersion())); - foreach ($matches as $index => $match) { - // skip local packages - if (!in_array($match->getRepository(), $repositories, true)) { - unset($matches[$index]); - continue; - } - - // skip providers/replacers - if ($match->getName() !== $package->getName()) { - unset($matches[$index]); - continue; - } - - $matches[$index] = $match->getId(); - } - - // select preferred package according to policy rules - if ($matches && $matches = $policy->selectPreferredPackages($pool, array(), $matches)) { - $newPackage = $pool->literalToPackage($matches[0]); - - if ($task === 'force-links' && $newPackage) { - $package->setRequires($newPackage->getRequires()); - $package->setConflicts($newPackage->getConflicts()); - $package->setProvides($newPackage->getProvides()); - $package->setReplaces($newPackage->getReplaces()); - } - - if ( - $task === 'force-updates' - && $newPackage - && ( - ($newPackage->getSourceReference() && $newPackage->getSourceReference() !== $package->getSourceReference()) - || ($newPackage->getDistReference() && $newPackage->getDistReference() !== $package->getDistReference()) - ) - ) { - $operations[] = new UpdateOperation($package, $newPackage); - - continue; - } - } - - if ($task === 'force-updates') { - // force installed package to update to referenced version in root package if it does not match the installed version - $references = $this->package->getReferences(); - - if (isset($references[$package->getName()]) && $references[$package->getName()] !== $package->getSourceReference()) { - // changing the source ref to update to will be handled in the operations loop - $operations[] = new UpdateOperation($package, clone $package); - } - } - } else { - // force update to locked version if it does not match the installed version - foreach ($lockedRepository->findPackages($package->getName()) as $lockedPackage) { - if ($lockedPackage->isDev() && $lockedPackage->getVersion() === $package->getVersion()) { - if ($task === 'force-links') { - $package->setRequires($lockedPackage->getRequires()); - $package->setConflicts($lockedPackage->getConflicts()); - $package->setProvides($lockedPackage->getProvides()); - $package->setReplaces($lockedPackage->getReplaces()); - } elseif ($task === 'force-updates') { - if (($lockedPackage->getSourceReference() && $lockedPackage->getSourceReference() !== $package->getSourceReference()) - || ($lockedPackage->getDistReference() && $lockedPackage->getDistReference() !== $package->getDistReference()) - ) { - $operations[] = new UpdateOperation($package, $lockedPackage); - } - } - - break; - } - } + } else { + $links = $this->package->getRequires(); + if ($includeDevRequires) { + $links = array_merge($links, $this->package->getDevRequires()); } - } - - return $operations; - } - - /** - * Loads the most "current" list of packages that are installed meaning from lock ideally or from installed repo as fallback - * @param RepositoryInterface $installedRepo - * @return array - */ - private function getCurrentPackages($installedRepo) - { - if ($this->locker->isLocked()) { - try { - return $this->locker->getLockedRepository(true)->getPackages(); - } catch (\RuntimeException $e) { - // fetch only non-dev packages from lock if doing a dev update fails due to a previously incomplete lock file - return $this->locker->getLockedRepository()->getPackages(); + foreach ($links as $link) { + $request->requireName($link->getTarget(), $link->getConstraint()); } } - - return $installedRepo->getPackages(); } /** + * @param bool $forUpdate * @return array */ - private function getRootAliases() + private function getRootAliases($forUpdate) { - if ($this->update) { + if ($forUpdate) { $aliases = $this->package->getAliases(); } else { $aliases = $this->locker->getAliases(); } - $normalizedAliases = array(); - - foreach ($aliases as $alias) { - $normalizedAliases[$alias['package']][$alias['version']] = array( - 'alias' => $alias['alias'], - 'alias_normalized' => $alias['alias_normalized'], - ); - } - - return $normalizedAliases; - } - - /** - * @param Pool $pool - * @param PolicyInterface $policy - * @param WritableRepositoryInterface $localRepo - * @param array $repositories - */ - private function processPackageUrls($pool, $policy, $localRepo, $repositories) - { - if (!$this->update) { - return; - } - - $rootRefs = $this->package->getReferences(); - - foreach ($localRepo->getCanonicalPackages() as $package) { - // find similar packages (name/version) in all repositories - $matches = $pool->whatProvides($package->getName(), new Constraint('=', $package->getVersion())); - foreach ($matches as $index => $match) { - // skip local packages - if (!in_array($match->getRepository(), $repositories, true)) { - unset($matches[$index]); - continue; - } - - // skip providers/replacers - if ($match->getName() !== $package->getName()) { - unset($matches[$index]); - continue; - } - - $matches[$index] = $match->getId(); - } - - // select preferred package according to policy rules - if ($matches && $matches = $policy->selectPreferredPackages($pool, array(), $matches)) { - $newPackage = $pool->literalToPackage($matches[0]); - - // update the dist and source URLs - $sourceUrl = $package->getSourceUrl(); - $newSourceUrl = $newPackage->getSourceUrl(); - $newReference = $newPackage->getSourceReference(); - - if ($package->isDev() && isset($rootRefs[$package->getName()]) && $package->getSourceReference() === $rootRefs[$package->getName()]) { - $newReference = $rootRefs[$package->getName()]; - } - - $this->updatePackageUrl($package, $newSourceUrl, $newPackage->getSourceType(), $newReference, $newPackage->getDistUrl(), $newPackage->getDistType(), $newPackage->getDistSha1Checksum()); - - if ($package instanceof CompletePackage && $newPackage instanceof CompletePackage) { - $package->setAbandoned($newPackage->getReplacementPackage() ?: $newPackage->isAbandoned()); - } - - $package->setDistMirrors($newPackage->getDistMirrors()); - $package->setSourceMirrors($newPackage->getSourceMirrors()); - $package->setTransportOptions($newPackage->getTransportOptions()); - } - } - } - - private function updatePackageUrl(PackageInterface $package, $sourceUrl, $sourceType, $sourceReference, $distUrl, $distType, $distShaSum) - { - $oldSourceRef = $package->getSourceReference(); - - if ($package->getSourceUrl() !== $sourceUrl) { - $package->setSourceType($sourceType); - $package->setSourceUrl($sourceUrl); - $package->setSourceReference($sourceReference); - } - - // only update dist url for github/bitbucket/gitlab dists as they use a combination of dist url + dist reference to install - // but for other urls this is ambiguous and could result in bad outcomes - if (preg_match('{^https?://(?:(?:www\.)?bitbucket\.org|(api\.)?github\.com|(?:www\.)?gitlab\.com)/}i', $distUrl)) { - $package->setDistUrl($distUrl); - $package->setDistType($distType); - $package->setDistSha1Checksum($distShaSum); - $this->updateInstallReferences($package, $sourceReference); - } - - if ($this->updateWhitelist && !$this->isUpdateable($package)) { - $this->updateInstallReferences($package, $oldSourceRef); - } - } - - private function updateInstallReferences(PackageInterface $package, $reference) - { - if (!$reference) { - return; - } - - $package->setSourceReference($reference); - - if (preg_match('{^https?://(?:(?:www\.)?bitbucket\.org|(api\.)?github\.com|(?:www\.)?gitlab\.com)/}i', $package->getDistUrl())) { - $package->setDistReference($reference); - $package->setDistUrl(preg_replace('{(?<=/|sha=)[a-f0-9]{40}(?=/|$)}i', $reference, $package->getDistUrl())); - } elseif ($package->getDistReference()) { // update the dist reference if there was one, but if none was provided ignore it - $package->setDistReference($reference); - } - } - - /** - * @param PlatformRepository $platformRepo - * @param array $aliases - */ - private function aliasPlatformPackages(PlatformRepository $platformRepo, $aliases) - { - foreach ($aliases as $package => $versions) { - foreach ($versions as $version => $alias) { - $packages = $platformRepo->findPackages($package, $version); - foreach ($packages as $package) { - $aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']); - $aliasPackage->setRootPackageAlias(true); - $platformRepo->addPackage($aliasPackage); - } - } - } - } - - /** - * @param PackageInterface $package - * @return bool - */ - private function isUpdateable(PackageInterface $package) - { - if (!$this->updateWhitelist) { - throw new \LogicException('isUpdateable should only be called when an allow list is present'); - } - - foreach ($this->updateWhitelist as $pattern => $void) { - $patternRegexp = BasePackage::packageNameToRegexp($pattern); - if (preg_match($patternRegexp, $package->getName())) { - return true; - } - } - - return false; + return $aliases; } /** * @param array $links * @return array */ - private function extractPlatformRequirements($links) + private function extractPlatformRequirements(array $links) { $platformReqs = array(); foreach ($links as $link) { - if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { + if (PlatformRepository::isPlatformPackage($link->getTarget())) { $platformReqs[$link->getTarget()] = $link->getPrettyConstraint(); } } @@ -1309,112 +909,6 @@ private function extractPlatformRequirements($links) return $platformReqs; } - /** - * Adds all dependencies of the update allow list to the allow list, too. - * - * Packages which are listed as requirements in the root package will be - * skipped including their dependencies, unless they are listed in the - * update allow list themselves or $whitelistAllDependencies is true. - * - * @param RepositoryInterface $localOrLockRepo Use the locked repo if available, otherwise installed repo will do - * As we want the most accurate package list to work with, and installed - * repo might be empty but locked repo will always be current. - * @param array $rootRequires An array of links to packages in require of the root package - * @param array $rootDevRequires An array of links to packages in require-dev of the root package - */ - private function allowListUpdateDependencies($localOrLockRepo, array $rootRequires, array $rootDevRequires) - { - if (!$this->updateWhitelist) { - return; - } - - $rootRequires = array_merge($rootRequires, $rootDevRequires); - - $skipPackages = array(); - if (!$this->whitelistAllDependencies) { - foreach ($rootRequires as $require) { - $skipPackages[$require->getTarget()] = true; - } - } - - $pool = new Pool('dev'); - $pool->addRepository($localOrLockRepo); - - $seen = array(); - - $rootRequiredPackageNames = array_keys($rootRequires); - - foreach ($this->updateWhitelist as $packageName => $void) { - $packageQueue = new \SplQueue; - $nameMatchesRequiredPackage = false; - - $depPackages = $pool->whatProvides($packageName); - $matchesByPattern = array(); - // check if the name is a glob pattern that did not match directly - if (empty($depPackages)) { - // add any installed package matching the allow listed name/pattern - $allowListPatternSearchRegexp = BasePackage::packageNameToRegexp($packageName, '^%s$'); - foreach ($localOrLockRepo->search($allowListPatternSearchRegexp) as $installedPackage) { - $matchesByPattern[] = $pool->whatProvides($installedPackage['name']); - } - - // add root requirements which match the allow listed name/pattern - $allowListPatternRegexp = BasePackage::packageNameToRegexp($packageName); - foreach ($rootRequiredPackageNames as $rootRequiredPackageName) { - if (preg_match($allowListPatternRegexp, $rootRequiredPackageName)) { - $nameMatchesRequiredPackage = true; - break; - } - } - } - - if (!empty($matchesByPattern)) { - $depPackages = array_merge($depPackages, call_user_func_array('array_merge', $matchesByPattern)); - } - - if (count($depPackages) == 0 && !$nameMatchesRequiredPackage && !in_array($packageName, array('nothing', 'lock', 'mirrors'))) { - $this->io->writeError('Package "' . $packageName . '" listed for update is not installed. Ignoring.'); - } - - foreach ($depPackages as $depPackage) { - $packageQueue->enqueue($depPackage); - } - - while (!$packageQueue->isEmpty()) { - $package = $packageQueue->dequeue(); - if (isset($seen[$package->getId()])) { - continue; - } - - $seen[$package->getId()] = true; - $this->updateWhitelist[$package->getName()] = true; - - if (!$this->whitelistDependencies && !$this->whitelistAllDependencies) { - continue; - } - - $requires = $package->getRequires(); - - foreach ($requires as $require) { - $requirePackages = $pool->whatProvides($require->getTarget()); - - foreach ($requirePackages as $requirePackage) { - if (isset($this->updateWhitelist[$requirePackage->getName()])) { - continue; - } - - if (isset($skipPackages[$requirePackage->getName()]) && !preg_match(BasePackage::packageNameToRegexp($packageName), $requirePackage->getName())) { - $this->io->writeError('Dependency "' . $requirePackage->getName() . '" is also a root requirement, but is not explicitly allowed. Ignoring.'); - continue; - } - - $packageQueue->enqueue($requirePackage); - } - } - } - } - } - /** * Replace local repositories with InstalledArrayRepository instances * @@ -1431,7 +925,8 @@ private function mockLocalRepositories(RepositoryManager $rm) foreach ($packages as $key => $package) { if ($package instanceof AliasPackage) { $alias = (string) $package->getAliasOf(); - $packages[$key] = new AliasPackage($packages[$alias], $package->getVersion(), $package->getPrettyVersion()); + $className = get_class($package); + $packages[$key] = new $className($packages[$alias], $package->getVersion(), $package->getPrettyVersion()); } } $rm->setLocalRepository( @@ -1462,12 +957,12 @@ public static function create(IOInterface $io, Composer $composer) } /** - * @param RepositoryInterface $additionalInstalledRepository + * @param RepositoryInterface $additionalFixedRepository * @return $this */ - public function setAdditionalInstalledRepository(RepositoryInterface $additionalInstalledRepository) + public function setAdditionalFixedRepository(RepositoryInterface $additionalFixedRepository) { - $this->additionalInstalledRepository = $additionalInstalledRepository; + $this->additionalFixedRepository = $additionalFixedRepository; return $this; } @@ -1527,7 +1022,7 @@ public function setPreferDist($preferDist = true) * @param bool $optimizeAutoloader * @return Installer */ - public function setOptimizeAutoloader($optimizeAutoloader = false) + public function setOptimizeAutoloader($optimizeAutoloader) { $this->optimizeAutoloader = (bool) $optimizeAutoloader; if (!$this->optimizeAutoloader) { @@ -1546,7 +1041,7 @@ public function setOptimizeAutoloader($optimizeAutoloader = false) * @param bool $classMapAuthoritative * @return Installer */ - public function setClassMapAuthoritative($classMapAuthoritative = false) + public function setClassMapAuthoritative($classMapAuthoritative) { $this->classMapAuthoritative = (bool) $classMapAuthoritative; if ($this->classMapAuthoritative) { @@ -1560,12 +1055,14 @@ public function setClassMapAuthoritative($classMapAuthoritative = false) /** * Whether or not generated autoloader considers APCu caching. * - * @param bool $apcuAutoloader + * @param bool $apcuAutoloader + * @param string|null $apcuAutoloaderPrefix * @return Installer */ - public function setApcuAutoloader($apcuAutoloader = false) + public function setApcuAutoloader($apcuAutoloader, $apcuAutoloaderPrefix = null) { - $this->apcuAutoloader = (bool) $apcuAutoloader; + $this->apcuAutoloader = $apcuAutoloader; + $this->apcuAutoloaderPrefix = $apcuAutoloaderPrefix; return $this; } @@ -1576,13 +1073,26 @@ public function setApcuAutoloader($apcuAutoloader = false) * @param bool $update * @return Installer */ - public function setUpdate($update = true) + public function setUpdate($update) { $this->update = (bool) $update; return $this; } + /** + * Allows disabling the install step after an update + * + * @param bool $install + * @return Installer + */ + public function setInstall($install) + { + $this->install = (bool) $install; + + return $this; + } + /** * enables dev packages * @@ -1618,6 +1128,7 @@ public function setDumpAutoloader($dumpAutoloader = true) * * @param bool $runScripts * @return Installer + * @deprecated Use setRunScripts(false) on the EventDispatcher instance being injected instead */ public function setRunScripts($runScripts = true) { @@ -1665,28 +1176,35 @@ public function isVerbose() /** * set ignore Platform Package requirements * - * @param bool $ignorePlatformReqs + * If this is set to true, all platform requirements are ignored + * If this is set to false, no platform requirements are ignored + * If this is set to string[], those packages will be ignored + * + * @param bool|array $ignorePlatformReqs * @return Installer */ - public function setIgnorePlatformRequirements($ignorePlatformReqs = false) + public function setIgnorePlatformRequirements($ignorePlatformReqs) { - $this->ignorePlatformReqs = (bool) $ignorePlatformReqs; + if (is_array($ignorePlatformReqs)) { + $this->ignorePlatformReqs = array_filter($ignorePlatformReqs, function ($req) { + return PlatformRepository::isPlatformPackage($req); + }); + } else { + $this->ignorePlatformReqs = (bool) $ignorePlatformReqs; + } return $this; } /** - * restrict the update operation to a few packages, all other packages - * that are already installed will be kept at their current version + * Update the lock file to the exact same versions and references but use current remote metadata like URLs and mirror info * - * @deprecated use setUpdateAllowList instead - * - * @param array $packages + * @param bool $updateMirrors * @return Installer */ - public function setUpdateWhitelist(array $packages) + public function setUpdateMirrors($updateMirrors) { - $this->updateWhitelist = array_flip(array_map('strtolower', $packages)); + $this->updateMirrors = $updateMirrors; return $this; } @@ -1700,84 +1218,31 @@ public function setUpdateWhitelist(array $packages) */ public function setUpdateAllowList(array $packages) { - // call original method for BC - return $this->setUpdateWhitelist($packages); - } - - /** - * @deprecated use setAllowListTransitiveDependencies instead - */ - public function setWhitelistDependencies($updateDependencies = true) - { - return $this->setWhitelistTransitiveDependencies($updateDependencies); - } - - /** - * Should dependencies of allowed packages (but not direct dependencies) be updated? - * - * This will NOT allow list any dependencies that are also directly defined - * in the root package. - * - * @deprecated use setAllowListTransitiveDependencies instead - * - * @param bool $updateTransitiveDependencies - * @return Installer - */ - public function setWhitelistTransitiveDependencies($updateTransitiveDependencies = true) - { - $this->whitelistDependencies = (bool) $updateTransitiveDependencies; + $this->updateAllowList = array_flip(array_map('strtolower', $packages)); return $this; } /** - * Should dependencies of allowed packages (but not direct dependencies) be updated? + * Should dependencies of packages marked for update be updated? * - * This will NOT allow list any dependencies that are also directly defined - * in the root package. + * Depending on the chosen constant this will either only update the directly named packages, all transitive + * dependencies which are not root requirement or all transitive dependencies including root requirements * - * @param bool $updateTransitiveDependencies + * @param int $updateAllowTransitiveDependencies One of the UPDATE_ constants on the Request class * @return Installer */ - public function setAllowListTransitiveDependencies($updateTransitiveDependencies = true) + public function setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) { - // call original method for BC - return $this->setWhitelistTransitiveDependencies($updateTransitiveDependencies); - } + if (!in_array($updateAllowTransitiveDependencies, array(Request::UPDATE_ONLY_LISTED, Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE, Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS), true)) { + throw new \RuntimeException("Invalid value for updateAllowTransitiveDependencies supplied"); + } - /** - * Should all dependencies of allowed packages be updated recursively? - * - * This will allow list any dependencies of the allow listed packages, including - * those defined in the root package. - * - * @deprecated use setAllowListAllDependencies instead - * - * @param bool $updateAllDependencies - * @return Installer - */ - public function setWhitelistAllDependencies($updateAllDependencies = true) - { - $this->whitelistAllDependencies = (bool) $updateAllDependencies; + $this->updateAllowTransitiveDependencies = $updateAllowTransitiveDependencies; return $this; } - /** - * Should all dependencies of allowed packages be updated recursively? - * - * This will allow list any dependencies of the allow listed packages, including - * those defined in the root package. - * - * @param bool $updateAllDependencies - * @return Installer - */ - public function setAllowListAllDependencies($updateAllDependencies = true) - { - // call original method for BC - return $this->setWhitelistAllDependencies($updateAllDependencies); - } - /** * Should packages be preferred in a stable version when updating? * @@ -1834,19 +1299,6 @@ public function setExecuteOperations($executeOperations = true) return $this; } - /** - * Should suggestions be skipped? - * - * @param bool $skipSuggest - * @return Installer - */ - public function setSkipSuggest($skipSuggest = true) - { - $this->skipSuggest = (bool) $skipSuggest; - - return $this; - } - /** * Disables plugins. * diff --git a/app/vendor/composer/composer/src/Composer/Installer/BinaryInstaller.php b/app/vendor/composer/composer/src/Composer/Installer/BinaryInstaller.php index 0a7b97149..c692e72b3 100644 --- a/app/vendor/composer/composer/src/Composer/Installer/BinaryInstaller.php +++ b/app/vendor/composer/composer/src/Composer/Installer/BinaryInstaller.php @@ -28,9 +28,13 @@ */ class BinaryInstaller { + /** @var string */ protected $binDir; + /** @var string */ protected $binCompat; + /** @var IOInterface */ protected $io; + /** @var Filesystem */ protected $filesystem; /** @@ -53,6 +57,9 @@ public function installBinaries(PackageInterface $package, $installPath, $warnOn if (!$binaries) { return; } + + Platform::workaroundFilesystemIssues(); + foreach ($binaries as $bin) { $binPath = $installPath.'/'.$bin; if (!file_exists($binPath)) { @@ -82,15 +89,17 @@ public function installBinaries(PackageInterface $package, $installPath, $warnOn } if ($this->binCompat === "auto") { - if (Platform::isWindows()) { + if (Platform::isWindows() || Platform::isWindowsSubsystemForLinux()) { $this->installFullBinaries($binPath, $link, $bin, $package); } else { $this->installSymlinkBinaries($binPath, $link); } } elseif ($this->binCompat === "full") { $this->installFullBinaries($binPath, $link, $bin, $package); + } elseif ($this->binCompat === "symlink") { + $this->installSymlinkBinaries($binPath, $link); } - Silencer::call('chmod', $link, 0777 & ~umask()); + Silencer::call('chmod', $binPath, 0777 & ~umask()); } } @@ -144,7 +153,6 @@ protected function installFullBinaries($binPath, $link, $bin, PackageInterface $ // add unixy support for cygwin and similar environments if ('.bat' !== substr($binPath, -4)) { $this->installUnixyProxyBinaries($binPath, $link); - @chmod($link, 0777 & ~umask()); $link .= '.bat'; if (file_exists($link)) { $this->io->writeError(' Skipped installation of bin '.$bin.'.bat proxy for package '.$package->getName().': a .bat proxy was already installed'); @@ -152,6 +160,7 @@ protected function installFullBinaries($binPath, $link, $bin, PackageInterface $ } if (!file_exists($link)) { file_put_contents($link, $this->generateWindowsProxyCode($binPath, $link)); + Silencer::call('chmod', $link, 0777 & ~umask()); } } @@ -165,6 +174,7 @@ protected function installSymlinkBinaries($binPath, $link) protected function installUnixyProxyBinaries($binPath, $link) { file_put_contents($link, $this->generateUnixyProxyCode($binPath, $link)); + Silencer::call('chmod', $link, 0777 & ~umask()); } protected function initializeBinDir() @@ -191,6 +201,48 @@ protected function generateUnixyProxyCode($bin, $link) $binDir = ProcessExecutor::escape(dirname($binPath)); $binFile = basename($binPath); + $binContents = file_get_contents($bin); + // For php files, we generate a PHP proxy instead of a shell one, + // which allows calling the proxy with a custom php process + if (preg_match('{^(?:#!(?:/usr)?/bin/env php|#!(?:/usr)?/bin/php| var_export(\$binPath, true), + '__DIR__' => var_export(dirname(\$binPath), true), + )); + + eval(\$contents); + exit(0); +} +include \$binPath; + +PROXY; + } + } + $proxyCode = << */ private $installers = array(); + /** @var array */ private $cache = array(); + /** @var array> */ private $notifiablePackages = array(); + /** @var Loop */ + private $loop; + /** @var IOInterface */ + private $io; + /** @var ?EventDispatcher */ + private $eventDispatcher; + /** @var bool */ + private $outputProgress; + + public function __construct(Loop $loop, IOInterface $io, EventDispatcher $eventDispatcher = null) + { + $this->loop = $loop; + $this->io = $io; + $this->eventDispatcher = $eventDispatcher; + } public function reset() { @@ -151,36 +172,327 @@ public function ensureBinariesPresence(PackageInterface $package) /** * Executes solver operation. * - * @param RepositoryInterface $repo repository in which to check - * @param OperationInterface $operation operation instance + * @param InstalledRepositoryInterface $repo repository in which to add/remove/update packages + * @param OperationInterface[] $operations operations to execute + * @param bool $devMode whether the install is being run in dev mode + * @param bool $runScripts whether to dispatch script events */ - public function execute(RepositoryInterface $repo, OperationInterface $operation) + public function execute(InstalledRepositoryInterface $repo, array $operations, $devMode = true, $runScripts = true) { - $method = $operation->getJobType(); - $this->$method($repo, $operation); + /** @var PromiseInterface[] */ + $cleanupPromises = array(); + + $loop = $this->loop; + $io = $this->io; + $runCleanup = function () use (&$cleanupPromises, $loop) { + $promises = array(); + + $loop->abortJobs(); + + foreach ($cleanupPromises as $cleanup) { + $promises[] = new \React\Promise\Promise(function ($resolve, $reject) use ($cleanup) { + $promise = $cleanup(); + if (!$promise instanceof PromiseInterface) { + $resolve(); + } else { + $promise->then(function () use ($resolve) { + $resolve(); + }); + } + }); + } + + if (!empty($promises)) { + $loop->wait($promises); + } + }; + + $handleInterruptsUnix = function_exists('pcntl_async_signals') && function_exists('pcntl_signal'); + $handleInterruptsWindows = function_exists('sapi_windows_set_ctrl_handler') && PHP_SAPI === 'cli'; + $prevHandler = null; + $windowsHandler = null; + if ($handleInterruptsUnix) { + pcntl_async_signals(true); + $prevHandler = pcntl_signal_get_handler(SIGINT); + pcntl_signal(SIGINT, function ($sig) use ($runCleanup, $prevHandler, $io) { + $io->writeError('Received SIGINT, aborting', true, IOInterface::DEBUG); + $runCleanup(); + + if (!in_array($prevHandler, array(SIG_DFL, SIG_IGN), true)) { + call_user_func($prevHandler, $sig); + } + + exit(130); + }); + } + if ($handleInterruptsWindows) { + $windowsHandler = function ($event) use ($runCleanup, $io) { + if ($event !== PHP_WINDOWS_EVENT_CTRL_C) { + return; + } + $io->writeError('Received CTRL+C, aborting', true, IOInterface::DEBUG); + $runCleanup(); + + exit(130); + }; + sapi_windows_set_ctrl_handler($windowsHandler); + } + + try { + // execute operations in batches to make sure download-modifying-plugins are installed + // before the other packages get downloaded + $batches = array(); + $batch = array(); + foreach ($operations as $index => $operation) { + if ($operation instanceof UpdateOperation || $operation instanceof InstallOperation) { + $package = $operation instanceof UpdateOperation ? $operation->getTargetPackage() : $operation->getPackage(); + if ($package->getType() === 'composer-plugin' && ($extra = $package->getExtra()) && isset($extra['plugin-modifies-downloads']) && $extra['plugin-modifies-downloads'] === true) { + if ($batch) { + $batches[] = $batch; + } + $batches[] = array($index => $operation); + $batch = array(); + + continue; + } + } + $batch[$index] = $operation; + } + + if ($batch) { + $batches[] = $batch; + } + + foreach ($batches as $batch) { + $this->downloadAndExecuteBatch($repo, $batch, $cleanupPromises, $devMode, $runScripts, $operations); + } + } catch (\Exception $e) { + $runCleanup(); + + if ($handleInterruptsUnix) { + pcntl_signal(SIGINT, $prevHandler); + } + if ($handleInterruptsWindows) { + sapi_windows_set_ctrl_handler($windowsHandler, false); + } + + throw $e; + } + + if ($handleInterruptsUnix) { + pcntl_signal(SIGINT, $prevHandler); + } + if ($handleInterruptsWindows) { + sapi_windows_set_ctrl_handler($windowsHandler, false); + } + + // do a last write so that we write the repository even if nothing changed + // as that can trigger an update of some files like InstalledVersions.php if + // running a new composer version + $repo->write($devMode, $this); + } + + /** + * @param array $operations List of operations to execute in this batch + * @param array $allOperations Complete list of operations to be executed in the install job, used for event listeners + */ + private function downloadAndExecuteBatch(InstalledRepositoryInterface $repo, array $operations, array &$cleanupPromises, $devMode, $runScripts, array $allOperations) + { + $promises = array(); + + foreach ($operations as $index => $operation) { + $opType = $operation->getOperationType(); + + // ignoring alias ops as they don't need to execute anything at this stage + if (!in_array($opType, array('update', 'install', 'uninstall'))) { + continue; + } + + if ($opType === 'update') { + $package = $operation->getTargetPackage(); + $initialPackage = $operation->getInitialPackage(); + } else { + $package = $operation->getPackage(); + $initialPackage = null; + } + $installer = $this->getInstaller($package->getType()); + + $cleanupPromises[$index] = function () use ($opType, $installer, $package, $initialPackage) { + // avoid calling cleanup if the download was not even initialized for a package + // as without installation source configured nothing will work + if (!$package->getInstallationSource()) { + return; + } + + return $installer->cleanup($opType, $package, $initialPackage); + }; + + if ($opType !== 'uninstall') { + $promise = $installer->download($package, $initialPackage); + if ($promise) { + $promises[] = $promise; + } + } + } + + // execute all downloads first + if (count($promises)) { + $this->waitOnPromises($promises); + } + + // execute operations in batches to make sure every plugin is installed in the + // right order and activated before the packages depending on it are installed + $batches = array(); + $batch = array(); + foreach ($operations as $index => $operation) { + if (in_array($operation->getOperationType(), array('update', 'install'), true)) { + $package = $operation->getOperationType() === 'update' ? $operation->getTargetPackage() : $operation->getPackage(); + if ($package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer') { + if ($batch) { + $batches[] = $batch; + } + $batches[] = array($index => $operation); + $batch = array(); + + continue; + } + } + $batch[$index] = $operation; + } + + if ($batch) { + $batches[] = $batch; + } + + foreach ($batches as $batch) { + $this->executeBatch($repo, $batch, $cleanupPromises, $devMode, $runScripts, $allOperations); + } + } + + /** + * @param array $operations List of operations to execute in this batch + * @param array $allOperations Complete list of operations to be executed in the install job, used for event listeners + */ + private function executeBatch(InstalledRepositoryInterface $repo, array $operations, array $cleanupPromises, $devMode, $runScripts, array $allOperations) + { + $promises = array(); + $postExecCallbacks = array(); + + foreach ($operations as $index => $operation) { + $opType = $operation->getOperationType(); + + // ignoring alias ops as they don't need to execute anything + if (!in_array($opType, array('update', 'install', 'uninstall'))) { + // output alias ops in debug verbosity as they have no output otherwise + if ($this->io->isDebug()) { + $this->io->writeError(' - ' . $operation->show(false)); + } + $this->$opType($repo, $operation); + + continue; + } + + if ($opType === 'update') { + $package = $operation->getTargetPackage(); + $initialPackage = $operation->getInitialPackage(); + } else { + $package = $operation->getPackage(); + $initialPackage = null; + } + $installer = $this->getInstaller($package->getType()); + + $event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($opType); + if (defined($event) && $runScripts && $this->eventDispatcher) { + $this->eventDispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $allOperations, $operation); + } + + $dispatcher = $this->eventDispatcher; + $installManager = $this; + $io = $this->io; + + $promise = $installer->prepare($opType, $package, $initialPackage); + if (!$promise instanceof PromiseInterface) { + $promise = \React\Promise\resolve(); + } + + $promise = $promise->then(function () use ($opType, $installManager, $repo, $operation) { + return $installManager->$opType($repo, $operation); + })->then($cleanupPromises[$index]) + ->then(function () use ($installManager, $devMode, $repo) { + $repo->write($devMode, $installManager); + }, function ($e) use ($opType, $package, $io) { + $io->writeError(' ' . ucfirst($opType) .' of '.$package->getPrettyName().' failed'); + + throw $e; + }); + + $postExecCallbacks[] = function () use ($opType, $runScripts, $dispatcher, $devMode, $repo, $allOperations, $operation) { + $event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($opType); + if (defined($event) && $runScripts && $dispatcher) { + $dispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $allOperations, $operation); + } + }; + + $promises[] = $promise; + } + + // execute all prepare => installs/updates/removes => cleanup steps + if (count($promises)) { + $this->waitOnPromises($promises); + } + + Platform::workaroundFilesystemIssues(); + + foreach ($postExecCallbacks as $cb) { + $cb(); + } + } + + private function waitOnPromises(array $promises) + { + $progress = null; + if ( + $this->outputProgress + && $this->io instanceof ConsoleIO + && !getenv('CI') + && !$this->io->isDebug() + && count($promises) > 1 + ) { + $progress = $this->io->getProgressBar(); + } + $this->loop->wait($promises, $progress); + if ($progress) { + $progress->clear(); + // ProgressBar in non-decorated output does not output a final line-break and clear() does nothing + if (!$this->io->isDecorated()) { + $this->io->writeError(''); + } + } } /** * Executes install operation. * - * @param RepositoryInterface $repo repository in which to check - * @param InstallOperation $operation operation instance + * @param InstalledRepositoryInterface $repo repository in which to check + * @param InstallOperation $operation operation instance */ - public function install(RepositoryInterface $repo, InstallOperation $operation) + public function install(InstalledRepositoryInterface $repo, InstallOperation $operation) { $package = $operation->getPackage(); $installer = $this->getInstaller($package->getType()); - $installer->install($repo, $package); + $promise = $installer->install($repo, $package); $this->markForNotification($package); + + return $promise; } /** * Executes update operation. * - * @param RepositoryInterface $repo repository in which to check - * @param UpdateOperation $operation operation instance + * @param InstalledRepositoryInterface $repo repository in which to check + * @param UpdateOperation $operation operation instance */ - public function update(RepositoryInterface $repo, UpdateOperation $operation) + public function update(InstalledRepositoryInterface $repo, UpdateOperation $operation) { $initial = $operation->getInitialPackage(); $target = $operation->getTargetPackage(); @@ -190,34 +502,44 @@ public function update(RepositoryInterface $repo, UpdateOperation $operation) if ($initialType === $targetType) { $installer = $this->getInstaller($initialType); - $installer->update($repo, $initial, $target); + $promise = $installer->update($repo, $initial, $target); $this->markForNotification($target); } else { - $this->getInstaller($initialType)->uninstall($repo, $initial); - $this->getInstaller($targetType)->install($repo, $target); + $promise = $this->getInstaller($initialType)->uninstall($repo, $initial); + if (!$promise instanceof PromiseInterface) { + $promise = \React\Promise\resolve(); + } + + $installer = $this->getInstaller($targetType); + $promise = $promise->then(function () use ($installer, $repo, $target) { + return $installer->install($repo, $target); + }); } + + return $promise; } /** * Uninstalls package. * - * @param RepositoryInterface $repo repository in which to check - * @param UninstallOperation $operation operation instance + * @param InstalledRepositoryInterface $repo repository in which to check + * @param UninstallOperation $operation operation instance */ - public function uninstall(RepositoryInterface $repo, UninstallOperation $operation) + public function uninstall(InstalledRepositoryInterface $repo, UninstallOperation $operation) { $package = $operation->getPackage(); $installer = $this->getInstaller($package->getType()); - $installer->uninstall($repo, $package); + + return $installer->uninstall($repo, $package); } /** * Executes markAliasInstalled operation. * - * @param RepositoryInterface $repo repository in which to check - * @param MarkAliasInstalledOperation $operation operation instance + * @param InstalledRepositoryInterface $repo repository in which to check + * @param MarkAliasInstalledOperation $operation operation instance */ - public function markAliasInstalled(RepositoryInterface $repo, MarkAliasInstalledOperation $operation) + public function markAliasInstalled(InstalledRepositoryInterface $repo, MarkAliasInstalledOperation $operation) { $package = $operation->getPackage(); @@ -229,10 +551,10 @@ public function markAliasInstalled(RepositoryInterface $repo, MarkAliasInstalled /** * Executes markAlias operation. * - * @param RepositoryInterface $repo repository in which to check + * @param InstalledRepositoryInterface $repo repository in which to check * @param MarkAliasUninstalledOperation $operation operation instance */ - public function markAliasUninstalled(RepositoryInterface $repo, MarkAliasUninstalledOperation $operation) + public function markAliasUninstalled(InstalledRepositoryInterface $repo, MarkAliasUninstalledOperation $operation) { $package = $operation->getPackage(); @@ -252,66 +574,65 @@ public function getInstallPath(PackageInterface $package) return $installer->getInstallPath($package); } - public function notifyInstalls(IOInterface $io) + public function setOutputProgress($outputProgress) { - foreach ($this->notifiablePackages as $repoUrl => $packages) { - $repositoryName = parse_url($repoUrl, PHP_URL_HOST); - if ($io->hasAuthentication($repositoryName)) { - $auth = $io->getAuthentication($repositoryName); - $authStr = base64_encode($auth['username'] . ':' . $auth['password']); - $authHeader = 'Authorization: Basic '.$authStr; - } + $this->outputProgress = $outputProgress; + } - // non-batch API, deprecated - if (strpos($repoUrl, '%package%')) { - foreach ($packages as $package) { - $url = str_replace('%package%', $package->getPrettyName(), $repoUrl); + public function notifyInstalls(IOInterface $io) + { + $promises = array(); - $params = array( - 'version' => $package->getPrettyVersion(), - 'version_normalized' => $package->getVersion(), - ); - $opts = array('http' => - array( - 'method' => 'POST', - 'header' => array('Content-type: application/x-www-form-urlencoded'), - 'content' => http_build_query($params, '', '&'), - 'timeout' => 3, - ), - ); - if (isset($authHeader)) { - $opts['http']['header'][] = $authHeader; + try { + foreach ($this->notifiablePackages as $repoUrl => $packages) { + // non-batch API, deprecated + if (strpos($repoUrl, '%package%')) { + foreach ($packages as $package) { + $url = str_replace('%package%', $package->getPrettyName(), $repoUrl); + + $params = array( + 'version' => $package->getPrettyVersion(), + 'version_normalized' => $package->getVersion(), + ); + $opts = array( + 'retry-auth-failure' => false, + 'http' => array( + 'method' => 'POST', + 'header' => array('Content-type: application/x-www-form-urlencoded'), + 'content' => http_build_query($params, '', '&'), + 'timeout' => 3, + ), + ); + + $promises[] = $this->loop->getHttpDownloader()->add($url, $opts); } - $context = StreamContextFactory::getContext($url, $opts); - @file_get_contents($url, false, $context); + continue; } - continue; - } + $postData = array('downloads' => array()); + foreach ($packages as $package) { + $postData['downloads'][] = array( + 'name' => $package->getPrettyName(), + 'version' => $package->getVersion(), + ); + } - $postData = array('downloads' => array()); - foreach ($packages as $package) { - $postData['downloads'][] = array( - 'name' => $package->getPrettyName(), - 'version' => $package->getVersion(), + $opts = array( + 'retry-auth-failure' => false, + 'http' => array( + 'method' => 'POST', + 'header' => array('Content-Type: application/json'), + 'content' => json_encode($postData), + 'timeout' => 6, + ), ); - } - $opts = array('http' => - array( - 'method' => 'POST', - 'header' => array('Content-Type: application/json'), - 'content' => json_encode($postData), - 'timeout' => 6, - ), - ); - if (isset($authHeader)) { - $opts['http']['header'][] = $authHeader; + $promises[] = $this->loop->getHttpDownloader()->add($repoUrl, $opts); } - $context = StreamContextFactory::getContext($repoUrl, $opts); - @file_get_contents($repoUrl, false, $context); + $this->loop->wait($promises); + } catch (\Exception $e) { } $this->reset(); diff --git a/app/vendor/composer/composer/src/Composer/Installer/InstallerEvent.php b/app/vendor/composer/composer/src/Composer/Installer/InstallerEvent.php index 87153bd51..ee81754d9 100644 --- a/app/vendor/composer/composer/src/Composer/Installer/InstallerEvent.php +++ b/app/vendor/composer/composer/src/Composer/Installer/InstallerEvent.php @@ -13,19 +13,10 @@ namespace Composer\Installer; use Composer\Composer; -use Composer\DependencyResolver\PolicyInterface; -use Composer\DependencyResolver\Operation\OperationInterface; -use Composer\DependencyResolver\Pool; -use Composer\DependencyResolver\Request; +use Composer\DependencyResolver\Transaction; use Composer\EventDispatcher\Event; use Composer\IO\IOInterface; -use Composer\Repository\CompositeRepository; -/** - * An event for all installer. - * - * @author François Pluchino - */ class InstallerEvent extends Event { /** @@ -44,55 +35,34 @@ class InstallerEvent extends Event private $devMode; /** - * @var PolicyInterface - */ - private $policy; - - /** - * @var Pool - */ - private $pool; - - /** - * @var CompositeRepository - */ - private $installedRepo; - - /** - * @var Request + * @var bool */ - private $request; + private $executeOperations; /** - * @var OperationInterface[] + * @var Transaction */ - private $operations; + private $transaction; /** * Constructor. * - * @param string $eventName - * @param Composer $composer - * @param IOInterface $io - * @param bool $devMode - * @param PolicyInterface $policy - * @param Pool $pool - * @param CompositeRepository $installedRepo - * @param Request $request - * @param OperationInterface[] $operations - */ - public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array()) + * @param string $eventName + * @param Composer $composer + * @param IOInterface $io + * @param bool $devMode + * @param bool $executeOperations + * @param Transaction $transaction + */ + public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, $executeOperations, Transaction $transaction) { parent::__construct($eventName); $this->composer = $composer; $this->io = $io; $this->devMode = $devMode; - $this->policy = $policy; - $this->pool = $pool; - $this->installedRepo = $installedRepo; - $this->request = $request; - $this->operations = $operations; + $this->executeOperations = $executeOperations; + $this->transaction = $transaction; } /** @@ -120,42 +90,18 @@ public function isDevMode() } /** - * @return PolicyInterface - */ - public function getPolicy() - { - return $this->policy; - } - - /** - * @return Pool - */ - public function getPool() - { - return $this->pool; - } - - /** - * @return CompositeRepository - */ - public function getInstalledRepo() - { - return $this->installedRepo; - } - - /** - * @return Request + * @return bool */ - public function getRequest() + public function isExecutingOperations() { - return $this->request; + return $this->executeOperations; } /** - * @return OperationInterface[] + * @return Transaction|null */ - public function getOperations() + public function getTransaction() { - return $this->operations; + return $this->transaction; } } diff --git a/app/vendor/composer/composer/src/Composer/Installer/InstallerEvents.php b/app/vendor/composer/composer/src/Composer/Installer/InstallerEvents.php index e05c92587..f75b8bdd4 100644 --- a/app/vendor/composer/composer/src/Composer/Installer/InstallerEvents.php +++ b/app/vendor/composer/composer/src/Composer/Installer/InstallerEvents.php @@ -12,32 +12,15 @@ namespace Composer\Installer; -/** - * The Installer Events. - * - * @author François Pluchino - */ class InstallerEvents { /** - * The PRE_DEPENDENCIES_SOLVING event occurs as a installer begins - * resolve operations. - * - * The event listener method receives a - * Composer\Installer\InstallerEvent instance. - * - * @var string - */ - const PRE_DEPENDENCIES_SOLVING = 'pre-dependencies-solving'; - - /** - * The POST_DEPENDENCIES_SOLVING event occurs as a installer after - * resolve operations. + * The PRE_OPERATIONS_EXEC event occurs before the lock file gets + * installed and operations are executed. * - * The event listener method receives a - * Composer\Installer\InstallerEvent instance. + * The event listener method receives an Composer\Installer\InstallerEvent instance. * * @var string */ - const POST_DEPENDENCIES_SOLVING = 'post-dependencies-solving'; + const PRE_OPERATIONS_EXEC = 'pre-operations-exec'; } diff --git a/app/vendor/composer/composer/src/Composer/Installer/InstallerInterface.php b/app/vendor/composer/composer/src/Composer/Installer/InstallerInterface.php index 410fce29b..f664a3ab4 100644 --- a/app/vendor/composer/composer/src/Composer/Installer/InstallerInterface.php +++ b/app/vendor/composer/composer/src/Composer/Installer/InstallerInterface.php @@ -15,6 +15,7 @@ use Composer\Package\PackageInterface; use Composer\Repository\InstalledRepositoryInterface; use InvalidArgumentException; +use React\Promise\PromiseInterface; /** * Interface for the package installation manager. @@ -42,33 +43,73 @@ public function supports($packageType); */ public function isInstalled(InstalledRepositoryInterface $repo, PackageInterface $package); + /** + * Downloads the files needed to later install the given package. + * + * @param PackageInterface $package package instance + * @param PackageInterface $prevPackage previous package instance in case of an update + * @return PromiseInterface|null + */ + public function download(PackageInterface $package, PackageInterface $prevPackage = null); + + /** + * Do anything that needs to be done between all downloads have been completed and the actual operation is executed + * + * All packages get first downloaded, then all together prepared, then all together installed/updated/uninstalled. Therefore + * for error recovery it is important to avoid failing during install/update/uninstall as much as possible, and risky things or + * user prompts should happen in the prepare step rather. In case of failure, cleanup() will be called so that changes can + * be undone as much as possible. + * + * @param string $type one of install/update/uninstall + * @param PackageInterface $package package instance + * @param PackageInterface $prevPackage previous package instance in case of an update + * @return PromiseInterface|null + */ + public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null); + /** * Installs specific package. * - * @param InstalledRepositoryInterface $repo repository in which to check - * @param PackageInterface $package package instance + * @param InstalledRepositoryInterface $repo repository in which to check + * @param PackageInterface $package package instance + * @return PromiseInterface|null */ public function install(InstalledRepositoryInterface $repo, PackageInterface $package); /** * Updates specific package. * - * @param InstalledRepositoryInterface $repo repository in which to check - * @param PackageInterface $initial already installed package version - * @param PackageInterface $target updated version - * - * @throws InvalidArgumentException if $initial package is not installed + * @param InstalledRepositoryInterface $repo repository in which to check + * @param PackageInterface $initial already installed package version + * @param PackageInterface $target updated version + * @throws InvalidArgumentException if $initial package is not installed + * @return PromiseInterface|null */ public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target); /** * Uninstalls specific package. * - * @param InstalledRepositoryInterface $repo repository in which to check - * @param PackageInterface $package package instance + * @param InstalledRepositoryInterface $repo repository in which to check + * @param PackageInterface $package package instance + * @return PromiseInterface|null */ public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package); + /** + * Do anything to cleanup changes applied in the prepare or install/update/uninstall steps + * + * Note that cleanup will be called for all packages regardless if they failed an operation or not, to give + * all installers a change to cleanup things they did previously, so you need to keep track of changes + * applied in the installer/downloader themselves. + * + * @param string $type one of install/update/uninstall + * @param PackageInterface $package package instance + * @param PackageInterface $prevPackage previous package instance in case of an update + * @return PromiseInterface|null + */ + public function cleanup($type, PackageInterface $package, PackageInterface $prevPackage = null); + /** * Returns the installation path of a package * diff --git a/app/vendor/composer/composer/src/Composer/Installer/LibraryInstaller.php b/app/vendor/composer/composer/src/Composer/Installer/LibraryInstaller.php index 34fbbbee4..239e5d129 100644 --- a/app/vendor/composer/composer/src/Composer/Installer/LibraryInstaller.php +++ b/app/vendor/composer/composer/src/Composer/Installer/LibraryInstaller.php @@ -19,6 +19,8 @@ use Composer\Util\Filesystem; use Composer\Util\Silencer; use Composer\Util\Platform; +use React\Promise\PromiseInterface; +use Composer\Downloader\DownloadManager; /** * Package installation manager. @@ -28,14 +30,19 @@ */ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface { + /** @var Composer */ protected $composer; + /** @var string */ protected $vendorDir; - protected $binDir; + /** @var DownloadManager */ protected $downloadManager; + /** @var IOInterface */ protected $io; + /** @var string */ protected $type; + /** @var Filesystem */ protected $filesystem; - protected $binCompat; + /** @var BinaryInstaller */ protected $binaryInstaller; /** @@ -43,7 +50,7 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface * * @param IOInterface $io * @param Composer $composer - * @param string $type + * @param string|null $type * @param Filesystem $filesystem * @param BinaryInstaller $binaryInstaller */ @@ -78,13 +85,46 @@ public function isInstalled(InstalledRepositoryInterface $repo, PackageInterface $installPath = $this->getInstallPath($package); - if (is_readable($installPath)) { + if (Filesystem::isReadable($installPath)) { return true; } return (Platform::isWindows() && $this->filesystem->isJunction($installPath)) || is_link($installPath); } + /** + * {@inheritDoc} + */ + public function download(PackageInterface $package, PackageInterface $prevPackage = null) + { + $this->initializeVendorDir(); + $downloadPath = $this->getInstallPath($package); + + return $this->downloadManager->download($package, $downloadPath, $prevPackage); + } + + /** + * {@inheritDoc} + */ + public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null) + { + $this->initializeVendorDir(); + $downloadPath = $this->getInstallPath($package); + + return $this->downloadManager->prepare($type, $package, $downloadPath, $prevPackage); + } + + /** + * {@inheritDoc} + */ + public function cleanup($type, PackageInterface $package, PackageInterface $prevPackage = null) + { + $this->initializeVendorDir(); + $downloadPath = $this->getInstallPath($package); + + return $this->downloadManager->cleanup($type, $package, $downloadPath, $prevPackage); + } + /** * {@inheritDoc} */ @@ -94,15 +134,24 @@ public function install(InstalledRepositoryInterface $repo, PackageInterface $pa $downloadPath = $this->getInstallPath($package); // remove the binaries if it appears the package files are missing - if (!is_readable($downloadPath) && $repo->hasPackage($package)) { + if (!Filesystem::isReadable($downloadPath) && $repo->hasPackage($package)) { $this->binaryInstaller->removeBinaries($package); } - $this->installCode($package); - $this->binaryInstaller->installBinaries($package, $this->getInstallPath($package)); - if (!$repo->hasPackage($package)) { - $repo->addPackage(clone $package); + $promise = $this->installCode($package); + if (!$promise instanceof PromiseInterface) { + $promise = \React\Promise\resolve(); } + + $binaryInstaller = $this->binaryInstaller; + $installPath = $this->getInstallPath($package); + + return $promise->then(function () use ($binaryInstaller, $installPath, $package, $repo) { + $binaryInstaller->installBinaries($package, $installPath); + if (!$repo->hasPackage($package)) { + $repo->addPackage(clone $package); + } + }); } /** @@ -117,12 +166,21 @@ public function update(InstalledRepositoryInterface $repo, PackageInterface $ini $this->initializeVendorDir(); $this->binaryInstaller->removeBinaries($initial); - $this->updateCode($initial, $target); - $this->binaryInstaller->installBinaries($target, $this->getInstallPath($target)); - $repo->removePackage($initial); - if (!$repo->hasPackage($target)) { - $repo->addPackage(clone $target); + $promise = $this->updateCode($initial, $target); + if (!$promise instanceof PromiseInterface) { + $promise = \React\Promise\resolve(); } + + $binaryInstaller = $this->binaryInstaller; + $installPath = $this->getInstallPath($target); + + return $promise->then(function () use ($binaryInstaller, $installPath, $target, $initial, $repo) { + $binaryInstaller->installBinaries($target, $installPath); + $repo->removePackage($initial); + if (!$repo->hasPackage($target)) { + $repo->addPackage(clone $target); + } + }); } /** @@ -134,17 +192,26 @@ public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $ throw new \InvalidArgumentException('Package is not installed: '.$package); } - $this->removeCode($package); - $this->binaryInstaller->removeBinaries($package); - $repo->removePackage($package); + $promise = $this->removeCode($package); + if (!$promise instanceof PromiseInterface) { + $promise = \React\Promise\resolve(); + } + $binaryInstaller = $this->binaryInstaller; $downloadPath = $this->getPackageBasePath($package); - if (strpos($package->getName(), '/')) { - $packageVendorDir = dirname($downloadPath); - if (is_dir($packageVendorDir) && $this->filesystem->isDirEmpty($packageVendorDir)) { - Silencer::call('rmdir', $packageVendorDir); + $filesystem = $this->filesystem; + + return $promise->then(function () use ($binaryInstaller, $filesystem, $downloadPath, $package, $repo) { + $binaryInstaller->removeBinaries($package); + $repo->removePackage($package); + + if (strpos($package->getName(), '/')) { + $packageVendorDir = dirname($downloadPath); + if (is_dir($packageVendorDir) && $filesystem->isDirEmpty($packageVendorDir)) { + Silencer::call('rmdir', $packageVendorDir); + } } - } + }); } /** @@ -194,7 +261,8 @@ protected function getPackageBasePath(PackageInterface $package) protected function installCode(PackageInterface $package) { $downloadPath = $this->getInstallPath($package); - $this->downloadManager->download($package, $downloadPath); + + return $this->downloadManager->install($package, $downloadPath); } protected function updateCode(PackageInterface $initial, PackageInterface $target) @@ -204,24 +272,37 @@ protected function updateCode(PackageInterface $initial, PackageInterface $targe if ($targetDownloadPath !== $initialDownloadPath) { // if the target and initial dirs intersect, we force a remove + install // to avoid the rename wiping the target dir as part of the initial dir cleanup - if (substr($initialDownloadPath, 0, strlen($targetDownloadPath)) === $targetDownloadPath - || substr($targetDownloadPath, 0, strlen($initialDownloadPath)) === $initialDownloadPath + if (strpos($initialDownloadPath, $targetDownloadPath) === 0 + || strpos($targetDownloadPath, $initialDownloadPath) === 0 ) { - $this->removeCode($initial); - $this->installCode($target); + $promise = $this->removeCode($initial); + if (!$promise instanceof PromiseInterface) { + $promise = \React\Promise\resolve(); + } + + $self = $this; + + return $promise->then(function () use ($self, $target) { + $reflMethod = new \ReflectionMethod($self, 'installCode'); + $reflMethod->setAccessible(true); - return; + // equivalent of $this->installCode($target) with php 5.3 support + // TODO remove this once 5.3 support is dropped + return $reflMethod->invoke($self, $target); + }); } $this->filesystem->rename($initialDownloadPath, $targetDownloadPath); } - $this->downloadManager->update($initial, $target, $targetDownloadPath); + + return $this->downloadManager->update($initial, $target, $targetDownloadPath); } protected function removeCode(PackageInterface $package) { $downloadPath = $this->getPackageBasePath($package); - $this->downloadManager->remove($package, $downloadPath); + + return $this->downloadManager->remove($package, $downloadPath); } protected function initializeVendorDir() diff --git a/app/vendor/composer/composer/src/Composer/Installer/MetapackageInstaller.php b/app/vendor/composer/composer/src/Composer/Installer/MetapackageInstaller.php index e1f31c1bf..936f72536 100644 --- a/app/vendor/composer/composer/src/Composer/Installer/MetapackageInstaller.php +++ b/app/vendor/composer/composer/src/Composer/Installer/MetapackageInstaller.php @@ -14,8 +14,10 @@ use Composer\Repository\InstalledRepositoryInterface; use Composer\Package\PackageInterface; -use Composer\Package\Version\VersionParser; use Composer\IO\IOInterface; +use Composer\DependencyResolver\Operation\UpdateOperation; +use Composer\DependencyResolver\Operation\InstallOperation; +use Composer\DependencyResolver\Operation\UninstallOperation; /** * Metapackage installation manager. @@ -24,6 +26,7 @@ */ class MetapackageInstaller implements InstallerInterface { + /** @var IOInterface */ private $io; public function __construct(IOInterface $io) @@ -47,14 +50,43 @@ public function isInstalled(InstalledRepositoryInterface $repo, PackageInterface return $repo->hasPackage($package); } + /** + * {@inheritDoc} + */ + public function download(PackageInterface $package, PackageInterface $prevPackage = null) + { + // noop + return \React\Promise\resolve(); + } + + /** + * {@inheritDoc} + */ + public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null) + { + // noop + return \React\Promise\resolve(); + } + + /** + * {@inheritDoc} + */ + public function cleanup($type, PackageInterface $package, PackageInterface $prevPackage = null) + { + // noop + return \React\Promise\resolve(); + } + /** * {@inheritDoc} */ public function install(InstalledRepositoryInterface $repo, PackageInterface $package) { - $this->io->writeError(" - Installing " . $package->getName() . " (" . $package->getFullPrettyVersion() . ")"); + $this->io->writeError(" - " . InstallOperation::format($package)); $repo->addPackage(clone $package); + + return \React\Promise\resolve(); } /** @@ -66,14 +98,12 @@ public function update(InstalledRepositoryInterface $repo, PackageInterface $ini throw new \InvalidArgumentException('Package is not installed: '.$initial); } - $name = $target->getName(); - $from = $initial->getFullPrettyVersion(); - $to = $target->getFullPrettyVersion(); - $actionName = VersionParser::isUpgrade($initial->getVersion(), $target->getVersion()) ? 'Updating' : 'Downgrading'; - $this->io->writeError(" - " . $actionName . " " . $name . " (" . $from . " => " . $to . ")"); + $this->io->writeError(" - " . UpdateOperation::format($initial, $target)); $repo->removePackage($initial); $repo->addPackage(clone $target); + + return \React\Promise\resolve(); } /** @@ -85,9 +115,11 @@ public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $ throw new \InvalidArgumentException('Package is not installed: '.$package); } - $this->io->writeError(" - Removing " . $package->getName() . " (" . $package->getFullPrettyVersion() . ")"); + $this->io->writeError(" - " . UninstallOperation::format($package)); $repo->removePackage($package); + + return \React\Promise\resolve(); } /** diff --git a/app/vendor/composer/composer/src/Composer/Installer/NoopInstaller.php b/app/vendor/composer/composer/src/Composer/Installer/NoopInstaller.php index 72cf17d22..1342db4ac 100644 --- a/app/vendor/composer/composer/src/Composer/Installer/NoopInstaller.php +++ b/app/vendor/composer/composer/src/Composer/Installer/NoopInstaller.php @@ -40,6 +40,30 @@ public function isInstalled(InstalledRepositoryInterface $repo, PackageInterface return $repo->hasPackage($package); } + /** + * {@inheritDoc} + */ + public function download(PackageInterface $package, PackageInterface $prevPackage = null) + { + return \React\Promise\resolve(); + } + + /** + * {@inheritDoc} + */ + public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null) + { + return \React\Promise\resolve(); + } + + /** + * {@inheritDoc} + */ + public function cleanup($type, PackageInterface $package, PackageInterface $prevPackage = null) + { + return \React\Promise\resolve(); + } + /** * {@inheritDoc} */ @@ -48,6 +72,8 @@ public function install(InstalledRepositoryInterface $repo, PackageInterface $pa if (!$repo->hasPackage($package)) { $repo->addPackage(clone $package); } + + return \React\Promise\resolve(); } /** @@ -63,6 +89,8 @@ public function update(InstalledRepositoryInterface $repo, PackageInterface $ini if (!$repo->hasPackage($target)) { $repo->addPackage(clone $target); } + + return \React\Promise\resolve(); } /** @@ -74,6 +102,8 @@ public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $ throw new \InvalidArgumentException('Package is not installed: '.$package); } $repo->removePackage($package); + + return \React\Promise\resolve(); } /** diff --git a/app/vendor/composer/composer/src/Composer/Installer/PackageEvent.php b/app/vendor/composer/composer/src/Composer/Installer/PackageEvent.php index f5cf0ed6e..ac9145943 100644 --- a/app/vendor/composer/composer/src/Composer/Installer/PackageEvent.php +++ b/app/vendor/composer/composer/src/Composer/Installer/PackageEvent.php @@ -15,20 +15,43 @@ use Composer\Composer; use Composer\IO\IOInterface; use Composer\DependencyResolver\Operation\OperationInterface; -use Composer\DependencyResolver\PolicyInterface; -use Composer\DependencyResolver\Pool; -use Composer\DependencyResolver\Request; -use Composer\Repository\CompositeRepository; +use Composer\Repository\RepositoryInterface; +use Composer\EventDispatcher\Event; /** * The Package Event. * * @author Jordi Boggiano */ -class PackageEvent extends InstallerEvent +class PackageEvent extends Event { /** - * @var OperationInterface The package instance + * @var Composer + */ + private $composer; + + /** + * @var IOInterface + */ + private $io; + + /** + * @var bool + */ + private $devMode; + + /** + * @var RepositoryInterface + */ + private $localRepo; + + /** + * @var OperationInterface[] + */ + private $operations; + + /** + * @var OperationInterface The operation instance which is being executed */ private $operation; @@ -39,20 +62,62 @@ class PackageEvent extends InstallerEvent * @param Composer $composer * @param IOInterface $io * @param bool $devMode - * @param PolicyInterface $policy - * @param Pool $pool - * @param CompositeRepository $installedRepo - * @param Request $request + * @param RepositoryInterface $localRepo * @param OperationInterface[] $operations * @param OperationInterface $operation */ - public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation) + public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, RepositoryInterface $localRepo, array $operations, OperationInterface $operation) { - parent::__construct($eventName, $composer, $io, $devMode, $policy, $pool, $installedRepo, $request, $operations); + parent::__construct($eventName); + $this->composer = $composer; + $this->io = $io; + $this->devMode = $devMode; + $this->localRepo = $localRepo; + $this->operations = $operations; $this->operation = $operation; } + /** + * @return Composer + */ + public function getComposer() + { + return $this->composer; + } + + /** + * @return IOInterface + */ + public function getIO() + { + return $this->io; + } + + /** + * @return bool + */ + public function isDevMode() + { + return $this->devMode; + } + + /** + * @return RepositoryInterface + */ + public function getLocalRepo() + { + return $this->localRepo; + } + + /** + * @return OperationInterface[] + */ + public function getOperations() + { + return $this->operations; + } + /** * Returns the package instance. * diff --git a/app/vendor/composer/composer/src/Composer/Installer/PearBinaryInstaller.php b/app/vendor/composer/composer/src/Composer/Installer/PearBinaryInstaller.php deleted file mode 100644 index f0d6783bb..000000000 --- a/app/vendor/composer/composer/src/Composer/Installer/PearBinaryInstaller.php +++ /dev/null @@ -1,144 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Installer; - -use Composer\IO\IOInterface; -use Composer\Package\PackageInterface; -use Composer\Util\Filesystem; -use Composer\Util\ProcessExecutor; - -/** - * Utility to handle installation of package "bin"/binaries for PEAR packages - * - * @author Jordi Boggiano - */ -class PearBinaryInstaller extends BinaryInstaller -{ - private $installer; - private $vendorDir; - - /** - * @param IOInterface $io - * @param string $binDir - * @param string $vendorDir - * @param string $binCompat - * @param Filesystem $filesystem - * @param PearInstaller $installer - */ - public function __construct(IOInterface $io, $binDir, $vendorDir, $binCompat, Filesystem $filesystem, PearInstaller $installer) - { - parent::__construct($io, $binDir, $binCompat, $filesystem); - $this->installer = $installer; - $this->vendorDir = $vendorDir; - } - - protected function getBinaries(PackageInterface $package) - { - $binariesPath = $this->installer->getInstallPath($package) . '/bin/'; - $binaries = array(); - if (file_exists($binariesPath)) { - foreach (new \FilesystemIterator($binariesPath, \FilesystemIterator::KEY_AS_FILENAME | \FilesystemIterator::CURRENT_AS_FILEINFO) as $fileName => $value) { - if (!$value->isDir()) { - $binaries[] = 'bin/'.$fileName; - } - } - } - - return $binaries; - } - - protected function initializeBinDir() - { - parent::initializeBinDir(); - file_put_contents($this->binDir.'/composer-php', $this->generateUnixyPhpProxyCode()); - @chmod($this->binDir.'/composer-php', 0777 & ~umask()); - file_put_contents($this->binDir.'/composer-php.bat', $this->generateWindowsPhpProxyCode()); - @chmod($this->binDir.'/composer-php.bat', 0777 & ~umask()); - } - - protected function generateWindowsProxyCode($bin, $link) - { - $binPath = $this->filesystem->findShortestPath($link, $bin); - if ('.bat' === substr($bin, -4)) { - $caller = 'call'; - } else { - $handle = fopen($bin, 'r'); - $line = fgets($handle); - fclose($handle); - if (preg_match('{^#!/(?:usr/bin/env )?(?:[^/]+/)*(.+)$}m', $line, $match)) { - $caller = trim($match[1]); - } else { - $caller = 'php'; - } - - if ($caller === 'php') { - return "@echo off\r\n". - "pushd .\r\n". - "cd %~dp0\r\n". - "set PHP_PROXY=%CD%\\composer-php.bat\r\n". - "cd ".ProcessExecutor::escape(dirname($binPath))."\r\n". - "set BIN_TARGET=%CD%\\".basename($binPath)."\r\n". - "popd\r\n". - "%PHP_PROXY% \"%BIN_TARGET%\" %*\r\n"; - } - } - - return "@echo off\r\n". - "pushd .\r\n". - "cd %~dp0\r\n". - "cd ".ProcessExecutor::escape(dirname($binPath))."\r\n". - "set BIN_TARGET=%CD%\\".basename($binPath)."\r\n". - "popd\r\n". - $caller." \"%BIN_TARGET%\" %*\r\n"; - } - - private function generateWindowsPhpProxyCode() - { - $binToVendor = $this->filesystem->findShortestPath($this->binDir, $this->vendorDir, true); - - return - "@echo off\r\n" . - "setlocal enabledelayedexpansion\r\n" . - "set BIN_DIR=%~dp0\r\n" . - "set VENDOR_DIR=%BIN_DIR%\\".$binToVendor."\r\n" . - "set DIRS=.\r\n" . - "FOR /D %%V IN (%VENDOR_DIR%\\*) DO (\r\n" . - " FOR /D %%P IN (%%V\\*) DO (\r\n" . - " set DIRS=!DIRS!;%%~fP\r\n" . - " )\r\n" . - ")\r\n" . - "php.exe -d include_path=!DIRS! %*\r\n"; - } - - private function generateUnixyPhpProxyCode() - { - $binToVendor = $this->filesystem->findShortestPath($this->binDir, $this->vendorDir, true); - - return - "#!/usr/bin/env sh\n". - "SRC_DIR=`pwd`\n". - "BIN_DIR=`dirname $0`\n". - "VENDOR_DIR=\$BIN_DIR/".escapeshellarg($binToVendor)."\n". - "DIRS=\"\"\n". - "for vendor in \$VENDOR_DIR/*; do\n". - " if [ -d \"\$vendor\" ]; then\n". - " for package in \$vendor/*; do\n". - " if [ -d \"\$package\" ]; then\n". - " DIRS=\"\${DIRS}:\${package}\"\n". - " fi\n". - " done\n". - " fi\n". - "done\n". - "php -d include_path=\".\$DIRS\" $@\n"; - } -} diff --git a/app/vendor/composer/composer/src/Composer/Installer/PearInstaller.php b/app/vendor/composer/composer/src/Composer/Installer/PearInstaller.php deleted file mode 100644 index b4aa465ed..000000000 --- a/app/vendor/composer/composer/src/Composer/Installer/PearInstaller.php +++ /dev/null @@ -1,84 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Installer; - -use Composer\IO\IOInterface; -use Composer\Composer; -use Composer\Downloader\PearPackageExtractor; -use Composer\Repository\InstalledRepositoryInterface; -use Composer\Package\PackageInterface; -use Composer\Util\Platform; -use Composer\Util\Filesystem; - -/** - * Package installation manager. - * - * @author Jordi Boggiano - * @author Konstantin Kudryashov - */ -class PearInstaller extends LibraryInstaller -{ - /** - * Initializes library installer. - * - * @param IOInterface $io io instance - * @param Composer $composer - * @param string $type package type that this installer handles - */ - public function __construct(IOInterface $io, Composer $composer, $type = 'pear-library') - { - $filesystem = new Filesystem(); - $binaryInstaller = new PearBinaryInstaller($io, rtrim($composer->getConfig()->get('bin-dir'), '/'), rtrim($composer->getConfig()->get('vendor-dir'), '/'), $composer->getConfig()->get('bin-compat'), $filesystem, $this); - - parent::__construct($io, $composer, $type, $filesystem, $binaryInstaller); - } - - /** - * {@inheritDoc} - */ - public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) - { - $this->uninstall($repo, $initial); - $this->install($repo, $target); - } - - protected function installCode(PackageInterface $package) - { - parent::installCode($package); - - $isWindows = Platform::isWindows(); - $php_bin = $this->binDir . ($isWindows ? '/composer-php.bat' : '/composer-php'); - - if (!$isWindows) { - $php_bin = '/usr/bin/env ' . $php_bin; - } - - $installPath = $this->getInstallPath($package); - $vars = array( - 'os' => $isWindows ? 'windows' : 'linux', - 'php_bin' => $php_bin, - 'pear_php' => $installPath, - 'php_dir' => $installPath, - 'bin_dir' => $installPath . '/bin', - 'data_dir' => $installPath . '/data', - 'version' => $package->getPrettyVersion(), - ); - - $packageArchive = $this->getInstallPath($package).'/'.pathinfo($package->getDistUrl(), PATHINFO_BASENAME); - $pearExtractor = new PearPackageExtractor($packageArchive); - $pearExtractor->extractTo($this->getInstallPath($package), array('php' => '/', 'script' => '/bin', 'data' => '/data'), $vars); - - $this->io->writeError(' Cleaning up', true, IOInterface::VERBOSE); - $this->filesystem->unlink($packageArchive); - } -} diff --git a/app/vendor/composer/composer/src/Composer/Installer/PluginInstaller.php b/app/vendor/composer/composer/src/Composer/Installer/PluginInstaller.php index 8cfb50e2f..120253216 100644 --- a/app/vendor/composer/composer/src/Composer/Installer/PluginInstaller.php +++ b/app/vendor/composer/composer/src/Composer/Installer/PluginInstaller.php @@ -16,6 +16,9 @@ use Composer\IO\IOInterface; use Composer\Repository\InstalledRepositoryInterface; use Composer\Package\PackageInterface; +use Composer\Util\Filesystem; +use Composer\Util\Platform; +use React\Promise\PromiseInterface; /** * Installer for plugin packages @@ -25,18 +28,15 @@ */ class PluginInstaller extends LibraryInstaller { - private $installationManager; - /** * Initializes Plugin installer. * * @param IOInterface $io * @param Composer $composer */ - public function __construct(IOInterface $io, Composer $composer) + public function __construct(IOInterface $io, Composer $composer, Filesystem $fs = null, BinaryInstaller $binaryInstaller = null) { - parent::__construct($io, $composer, 'composer-plugin'); - $this->installationManager = $composer->getInstallationManager(); + parent::__construct($io, $composer, 'composer-plugin', $fs, $binaryInstaller); } /** @@ -50,22 +50,37 @@ public function supports($packageType) /** * {@inheritDoc} */ - public function install(InstalledRepositoryInterface $repo, PackageInterface $package) + public function download(PackageInterface $package, PackageInterface $prevPackage = null) { $extra = $package->getExtra(); if (empty($extra['class'])) { throw new \UnexpectedValueException('Error while installing '.$package->getPrettyName().', composer-plugin packages should have a class defined in their extra key to be usable.'); } - parent::install($repo, $package); - try { - $this->composer->getPluginManager()->registerPackage($package, true); - } catch (\Exception $e) { - // Rollback installation - $this->io->writeError('Plugin installation failed ('.$e->getMessage().'), rolling back'); - parent::uninstall($repo, $package); - throw $e; + return parent::download($package, $prevPackage); + } + + /** + * {@inheritDoc} + */ + public function install(InstalledRepositoryInterface $repo, PackageInterface $package) + { + $promise = parent::install($repo, $package); + if (!$promise instanceof PromiseInterface) { + $promise = \React\Promise\resolve(); } + + $pluginManager = $this->composer->getPluginManager(); + $self = $this; + + return $promise->then(function () use ($self, $pluginManager, $package, $repo) { + try { + Platform::workaroundFilesystemIssues(); + $pluginManager->registerPackage($package, true); + } catch (\Exception $e) { + $self->rollbackInstall($e, $repo, $package); + } + }); } /** @@ -73,12 +88,40 @@ public function install(InstalledRepositoryInterface $repo, PackageInterface $pa */ public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) { - $extra = $target->getExtra(); - if (empty($extra['class'])) { - throw new \UnexpectedValueException('Error while installing '.$target->getPrettyName().', composer-plugin packages should have a class defined in their extra key to be usable.'); + $promise = parent::update($repo, $initial, $target); + if (!$promise instanceof PromiseInterface) { + $promise = \React\Promise\resolve(); } - parent::update($repo, $initial, $target); - $this->composer->getPluginManager()->registerPackage($target, true); + $pluginManager = $this->composer->getPluginManager(); + $self = $this; + + return $promise->then(function () use ($self, $pluginManager, $initial, $target, $repo) { + try { + Platform::workaroundFilesystemIssues(); + $pluginManager->deactivatePackage($initial); + $pluginManager->registerPackage($target, true); + } catch (\Exception $e) { + $self->rollbackInstall($e, $repo, $target); + } + }); + } + + public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package) + { + $this->composer->getPluginManager()->uninstallPackage($package); + + return parent::uninstall($repo, $package); + } + + /** + * TODO v3 should make this private once we can drop PHP 5.3 support + * @private + */ + public function rollbackInstall(\Exception $e, InstalledRepositoryInterface $repo, PackageInterface $package) + { + $this->io->writeError('Plugin initialization failed ('.$e->getMessage().'), uninstalling plugin'); + parent::uninstall($repo, $package); + throw $e; } } diff --git a/app/vendor/composer/composer/src/Composer/Installer/ProjectInstaller.php b/app/vendor/composer/composer/src/Composer/Installer/ProjectInstaller.php index c79238b36..d03e97521 100644 --- a/app/vendor/composer/composer/src/Composer/Installer/ProjectInstaller.php +++ b/app/vendor/composer/composer/src/Composer/Installer/ProjectInstaller.php @@ -25,15 +25,18 @@ */ class ProjectInstaller implements InstallerInterface { + /** @var string */ private $installPath; + /** @var DownloadManager */ private $downloadManager; + /** @var Filesystem */ private $filesystem; - public function __construct($installPath, DownloadManager $dm) + public function __construct($installPath, DownloadManager $dm, Filesystem $fs) { $this->installPath = rtrim(strtr($installPath, '\\', '/'), '/').'/'; $this->downloadManager = $dm; - $this->filesystem = new Filesystem; + $this->filesystem = $fs; } /** @@ -58,7 +61,7 @@ public function isInstalled(InstalledRepositoryInterface $repo, PackageInterface /** * {@inheritDoc} */ - public function install(InstalledRepositoryInterface $repo, PackageInterface $package) + public function download(PackageInterface $package, PackageInterface $prevPackage = null) { $installPath = $this->installPath; if (file_exists($installPath) && !$this->filesystem->isDirEmpty($installPath)) { @@ -67,7 +70,32 @@ public function install(InstalledRepositoryInterface $repo, PackageInterface $pa if (!is_dir($installPath)) { mkdir($installPath, 0777, true); } - $this->downloadManager->download($package, $installPath); + + return $this->downloadManager->download($package, $installPath, $prevPackage); + } + + /** + * {@inheritDoc} + */ + public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null) + { + return $this->downloadManager->prepare($type, $package, $this->installPath, $prevPackage); + } + + /** + * {@inheritDoc} + */ + public function cleanup($type, PackageInterface $package, PackageInterface $prevPackage = null) + { + return $this->downloadManager->cleanup($type, $package, $this->installPath, $prevPackage); + } + + /** + * {@inheritDoc} + */ + public function install(InstalledRepositoryInterface $repo, PackageInterface $package) + { + return $this->downloadManager->install($package, $this->installPath); } /** diff --git a/app/vendor/composer/composer/src/Composer/Installer/SuggestedPackagesReporter.php b/app/vendor/composer/composer/src/Composer/Installer/SuggestedPackagesReporter.php index 25788e547..4db5931d3 100644 --- a/app/vendor/composer/composer/src/Composer/Installer/SuggestedPackagesReporter.php +++ b/app/vendor/composer/composer/src/Composer/Installer/SuggestedPackagesReporter.php @@ -14,7 +14,7 @@ use Composer\IO\IOInterface; use Composer\Package\PackageInterface; -use Composer\Repository\RepositoryInterface; +use Composer\Repository\InstalledRepository; use Symfony\Component\Console\Formatter\OutputFormatter; /** @@ -24,8 +24,12 @@ */ class SuggestedPackagesReporter { + const MODE_LIST = 1; + const MODE_BY_PACKAGE = 2; + const MODE_BY_SUGGESTION = 4; + /** - * @var array + * @var array */ protected $suggestedPackages = array(); @@ -91,38 +95,124 @@ public function addSuggestionsFromPackage(PackageInterface $package) /** * Output suggested packages. + * * Do not list the ones already installed if installed repository provided. * - * @param RepositoryInterface $installedRepo Installed packages - * @return SuggestedPackagesReporter + * @param int $mode One of the MODE_* constants from this class + * @param InstalledRepository|null $installedRepo If passed in, suggested packages which are installed already will be skipped + * @param PackageInterface|null $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown + * @return void */ - public function output(RepositoryInterface $installedRepo = null) + public function output($mode, InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null) + { + $suggestedPackages = $this->getFilteredSuggestions($installedRepo, $onlyDependentsOf); + + $suggesters = array(); + $suggested = array(); + foreach ($suggestedPackages as $suggestion) { + $suggesters[$suggestion['source']][$suggestion['target']] = $suggestion['reason']; + $suggested[$suggestion['target']][$suggestion['source']] = $suggestion['reason']; + } + ksort($suggesters); + ksort($suggested); + + // Simple mode + if ($mode & self::MODE_LIST) { + foreach (array_keys($suggested) as $name) { + $this->io->write(sprintf('%s', $name)); + } + + return; + } + + // Grouped by package + if ($mode & self::MODE_BY_PACKAGE) { + foreach ($suggesters as $suggester => $suggestions) { + $this->io->write(sprintf('%s suggests:', $suggester)); + + foreach ($suggestions as $suggestion => $reason) { + $this->io->write(sprintf(' - %s' . ($reason ? ': %s' : ''), $suggestion, $this->escapeOutput($reason))); + } + $this->io->write(''); + } + } + + // Grouped by suggestion + if ($mode & self::MODE_BY_SUGGESTION) { + // Improve readability in full mode + if ($mode & self::MODE_BY_PACKAGE) { + $this->io->write(str_repeat('-', 78)); + } + foreach ($suggested as $suggestion => $suggesters) { + $this->io->write(sprintf('%s is suggested by:', $suggestion)); + + foreach ($suggesters as $suggester => $reason) { + $this->io->write(sprintf(' - %s' . ($reason ? ': %s' : ''), $suggester, $this->escapeOutput($reason))); + } + $this->io->write(''); + } + } + + if ($onlyDependentsOf) { + $allSuggestedPackages = $this->getFilteredSuggestions($installedRepo); + $diff = count($allSuggestedPackages) - count($suggestedPackages); + if ($diff) { + $this->io->write(''.$diff.' additional suggestions by transitive dependencies can be shown with --all'); + } + } + } + + /** + * Output number of new suggested packages and a hint to use suggest command. + * + * @param InstalledRepository|null $installedRepo If passed in, suggested packages which are installed already will be skipped + * @param PackageInterface|null $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown + * @return void + */ + public function outputMinimalistic(InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null) + { + $suggestedPackages = $this->getFilteredSuggestions($installedRepo, $onlyDependentsOf); + if ($suggestedPackages) { + $this->io->writeError(''.count($suggestedPackages).' package suggestions were added by new dependencies, use `composer suggest` to see details.'); + } + } + + /** + * @param InstalledRepository|null $installedRepo If passed in, suggested packages which are installed already will be skipped + * @param PackageInterface|null $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown + * @return array[] + */ + private function getFilteredSuggestions(InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null) { $suggestedPackages = $this->getPackages(); - $installedPackages = array(); - if (null !== $installedRepo && ! empty($suggestedPackages)) { + $installedNames = array(); + if (null !== $installedRepo && !empty($suggestedPackages)) { foreach ($installedRepo->getPackages() as $package) { - $installedPackages = array_merge( - $installedPackages, + $installedNames = array_merge( + $installedNames, $package->getNames() ); } } + $sourceFilter = array(); + if ($onlyDependentsOf) { + $sourceFilter = array_map(function ($link) { + return $link->getTarget(); + }, array_merge($onlyDependentsOf->getRequires(), $onlyDependentsOf->getDevRequires())); + $sourceFilter[] = $onlyDependentsOf->getName(); + } + + $suggestions = array(); foreach ($suggestedPackages as $suggestion) { - if (in_array($suggestion['target'], $installedPackages)) { + if (in_array($suggestion['target'], $installedNames) || ($sourceFilter && !in_array($suggestion['source'], $sourceFilter))) { continue; } - $this->io->writeError(sprintf( - '%s suggests installing %s%s', - $suggestion['source'], - $this->escapeOutput($suggestion['target']), - $this->escapeOutput('' !== $suggestion['reason'] ? ' ('.$suggestion['reason'].')' : '') - )); + $suggestions[] = $suggestion; } - return $this; + return $suggestions; } /** diff --git a/app/vendor/composer/composer/src/Composer/Json/JsonFile.php b/app/vendor/composer/composer/src/Composer/Json/JsonFile.php index 89524df39..1f830452c 100644 --- a/app/vendor/composer/composer/src/Composer/Json/JsonFile.php +++ b/app/vendor/composer/composer/src/Composer/Json/JsonFile.php @@ -15,7 +15,7 @@ use JsonSchema\Validator; use Seld\JsonLint\JsonParser; use Seld\JsonLint\ParsingException; -use Composer\Util\RemoteFilesystem; +use Composer\Util\HttpDownloader; use Composer\IO\IOInterface; use Composer\Downloader\TransportException; @@ -36,26 +36,29 @@ class JsonFile const COMPOSER_SCHEMA_PATH = '/../../../res/composer-schema.json'; + /** @var string */ private $path; - private $rfs; + /** @var ?HttpDownloader */ + private $httpDownloader; + /** @var ?IOInterface */ private $io; /** * Initializes json file reader/parser. * - * @param string $path path to a lockfile - * @param RemoteFilesystem $rfs required for loading http/https json files - * @param IOInterface $io + * @param string $path path to a lockfile + * @param ?HttpDownloader $httpDownloader required for loading http/https json files + * @param ?IOInterface $io * @throws \InvalidArgumentException */ - public function __construct($path, RemoteFilesystem $rfs = null, IOInterface $io = null) + public function __construct($path, HttpDownloader $httpDownloader = null, IOInterface $io = null) { $this->path = $path; - if (null === $rfs && preg_match('{^https?://}i', $path)) { - throw new \InvalidArgumentException('http urls require a RemoteFilesystem instance to be passed'); + if (null === $httpDownloader && preg_match('{^https?://}i', $path)) { + throw new \InvalidArgumentException('http urls require a HttpDownloader instance to be passed'); } - $this->rfs = $rfs; + $this->httpDownloader = $httpDownloader; $this->io = $io; } @@ -80,17 +83,23 @@ public function exists() /** * Reads json file. * + * @throws ParsingException * @throws \RuntimeException * @return mixed */ public function read() { try { - if ($this->rfs) { - $json = $this->rfs->getContents($this->path, $this->path, false); + if ($this->httpDownloader) { + $json = $this->httpDownloader->get($this->path)->getBody(); } else { if ($this->io && $this->io->isDebug()) { - $this->io->writeError('Reading ' . $this->path); + $realpathInfo = ''; + $realpath = realpath($this->path); + if (false !== $realpath && $realpath !== $this->path) { + $realpathInfo = ' (' . $realpath . ')'; + } + $this->io->writeError('Reading ' . $this->path . $realpathInfo); } $json = file_get_contents($this->path); } @@ -112,11 +121,17 @@ public function read() */ public function write(array $hash, $options = 448) { + if ($this->path === 'php://memory') { + file_put_contents($this->path, static::encode($hash, $options)); + + return; + } + $dir = dirname($this->path); if (!is_dir($dir)) { if (file_exists($dir)) { throw new \UnexpectedValueException( - $dir.' exists and is not a directory.' + realpath($dir).' exists and is not a directory.' ); } if (!@mkdir($dir, 0777, true)) { @@ -158,9 +173,10 @@ private function filePutContentsIfModified($path, $content) /** * Validates the schema of the current json file according to composer-schema.json rules * - * @param int $schema a JsonFile::*_SCHEMA constant + * @param int $schema a JsonFile::*_SCHEMA constant * @param string|null $schemaFile a path to the schema file * @throws JsonValidationException + * @throws ParsingException * @return bool true on success */ public function validateSchema($schema = self::STRICT_SCHEMA, $schemaFile = null) @@ -172,7 +188,9 @@ public function validateSchema($schema = self::STRICT_SCHEMA, $schemaFile = null self::validateSyntax($content, $this->path); } + $isComposerSchemaFile = false; if (null === $schemaFile) { + $isComposerSchemaFile = true; $schemaFile = __DIR__ . self::COMPOSER_SCHEMA_PATH; } @@ -186,13 +204,14 @@ public function validateSchema($schema = self::STRICT_SCHEMA, $schemaFile = null if ($schema === self::LAX_SCHEMA) { $schemaData->additionalProperties = true; $schemaData->required = array(); + } elseif ($schema === self::STRICT_SCHEMA && $isComposerSchemaFile) { + $schemaData->additionalProperties = false; + $schemaData->required = array('name', 'description'); } $validator = new Validator(); $validator->check($data, $schemaData); - // TODO add more validation like check version constraints and such, perhaps build that into the arrayloader? - if (!$validator->isValid()) { $errors = array(); foreach ((array) $validator->getErrors() as $error) { @@ -275,15 +294,16 @@ private static function throwEncodeError($code) /** * Parses json string and returns hash. * - * @param string $json json string + * @param ?string $json json string * @param string $file the json file * + * @throws ParsingException * @return mixed */ public static function parseJson($json, $file = null) { if (null === $json) { - return; + return null; } $data = json_decode($json, true); if (null === $data && JSON_ERROR_NONE !== json_last_error()) { diff --git a/app/vendor/composer/composer/src/Composer/Json/JsonFormatter.php b/app/vendor/composer/composer/src/Composer/Json/JsonFormatter.php index 44acaff59..386e62673 100644 --- a/app/vendor/composer/composer/src/Composer/Json/JsonFormatter.php +++ b/app/vendor/composer/composer/src/Composer/Json/JsonFormatter.php @@ -58,7 +58,8 @@ public static function format($json, $unescapeUnicode, $unescapeSlashes) $buffer .= $char; $noescape = '\\' === $char ? !$noescape : true; continue; - } elseif ('' !== $buffer) { + } + if ('' !== $buffer) { if ($unescapeSlashes) { $buffer = str_replace('\\/', '/', $buffer); } diff --git a/app/vendor/composer/composer/src/Composer/Json/JsonManipulator.php b/app/vendor/composer/composer/src/Composer/Json/JsonManipulator.php index e1ee43485..b1c82a687 100644 --- a/app/vendor/composer/composer/src/Composer/Json/JsonManipulator.php +++ b/app/vendor/composer/composer/src/Composer/Json/JsonManipulator.php @@ -19,18 +19,22 @@ */ class JsonManipulator { + /** @var string */ private static $DEFINES = '(?(DEFINE) - (? -? (?= [1-9]|0(?!\d) ) \d+ (\.\d+)? ([eE] [+-]? \d+)? ) + (? -? (?= [1-9]|0(?!\d) ) \d++ (\.\d++)? ([eE] [+-]?+ \d++)? ) (? true | false | null ) - (? " ([^"\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9A-Fa-f]{4} )* " ) - (? \[ (?: (?&json) \s* (?: , (?&json) \s* )* )? \s* \] ) - (? \s* (?&string) \s* : (?&json) \s* ) - (? \{ (?: (?&pair) (?: , (?&pair) )* )? \s* \} ) - (? \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) ) + (? " ([^"\\\\]*+ | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9A-Fa-f]{4} )* " ) + (? \[ (?: (?&json) \s*+ (?: , (?&json) \s*+ )*+ )?+ \s*+ \] ) + (? \s*+ (?&string) \s*+ : (?&json) \s*+ ) + (? \{ (?: (?&pair) (?: , (?&pair) )*+ )?+ \s*+ \} ) + (? \s*+ (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) ) )'; + /** @var string */ private $contents; + /** @var string */ private $newline; + /** @var string */ private $indent; public function __construct($contents) @@ -117,7 +121,7 @@ public function addLink($type, $package, $constraint, $sortPackages = false) private function sortPackages(array &$packages = array()) { $prefix = function ($requirement) { - if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $requirement)) { + if (PlatformRepository::isPlatformPackage($requirement)) { return preg_replace( array( '/^php/', @@ -145,9 +149,9 @@ private function sortPackages(array &$packages = array()) }); } - public function addRepository($name, $config) + public function addRepository($name, $config, $append = true) { - return $this->addSubNode('repositories', $name, $config); + return $this->addSubNode('repositories', $name, $config, $append); } public function removeRepository($name) @@ -167,15 +171,15 @@ public function removeConfigSetting($name) public function addProperty($name, $value) { - if (substr($name, 0, 8) === 'suggest.') { + if (strpos($name, 'suggest.') === 0) { return $this->addSubNode('suggest', substr($name, 8), $value); } - if (substr($name, 0, 6) === 'extra.') { + if (strpos($name, 'extra.') === 0) { return $this->addSubNode('extra', substr($name, 6), $value); } - if (substr($name, 0, 8) === 'scripts.') { + if (strpos($name, 'scripts.') === 0) { return $this->addSubNode('scripts', substr($name, 8), $value); } @@ -184,22 +188,22 @@ public function addProperty($name, $value) public function removeProperty($name) { - if (substr($name, 0, 8) === 'suggest.') { + if (strpos($name, 'suggest.') === 0) { return $this->removeSubNode('suggest', substr($name, 8)); } - if (substr($name, 0, 6) === 'extra.') { + if (strpos($name, 'extra.') === 0) { return $this->removeSubNode('extra', substr($name, 6)); } - if (substr($name, 0, 8) === 'scripts.') { + if (strpos($name, 'scripts.') === 0) { return $this->removeSubNode('scripts', substr($name, 8)); } return $this->removeMainKey($name); } - public function addSubNode($mainNode, $name, $value) + public function addSubNode($mainNode, $name, $value, $append = true) { $decoded = JsonFile::parseJson($this->contents); @@ -258,7 +262,7 @@ public function addSubNode($mainNode, $name, $value) return $matches['start'] . $that->format($value, 1) . $matches['end']; }, $children); } else { - $this->pregMatch('#^{ \s*? (?P\S+.*?)? (?P\s*) }$#sx', $children, $match); + $this->pregMatch('#^{ (?P\s*?) (?P\S+.*?)? (?P\s*) }$#sx', $children, $match); $whitespace = ''; if (!empty($match['trailingspace'])) { @@ -271,11 +275,24 @@ public function addSubNode($mainNode, $name, $value) } // child missing but non empty children - $children = preg_replace( - '#'.$whitespace.'}$#', - addcslashes(',' . $this->newline . $this->indent . $this->indent . JsonFile::encode($name).': '.$this->format($value, 1) . $whitespace . '}', '\\$'), - $children - ); + if ($append) { + $children = preg_replace( + '#'.$whitespace.'}$#', + addcslashes(',' . $this->newline . $this->indent . $this->indent . JsonFile::encode($name).': '.$this->format($value, 1) . $whitespace . '}', '\\$'), + $children + ); + } else { + $whitespace = ''; + if (!empty($match['leadingspace'])) { + $whitespace = $match['leadingspace']; + } + + $children = preg_replace( + '#^{'.$whitespace.'#', + addcslashes('{' . $whitespace . JsonFile::encode($name).': '.$this->format($value, 1) . ',' . $this->newline . $this->indent . $this->indent, '\\$'), + $children + ); + } } else { if ($subName !== null) { $value = array($subName => $value); @@ -356,6 +373,10 @@ public function removeSubNode($mainNode, $name) $childrenClean = $children; } + if (!isset($childrenClean)) { + throw new \InvalidArgumentException("JsonManipulator: \$childrenClean is not defined. Please report at https://github.com/composer/composer/issues/new."); + } + // no child data left, $name was the only key in $this->pregMatch('#^{ \s*? (?P\S+.*?)? (?P\s*) }$#sx', $childrenClean, $match); if (empty($match['content'])) { @@ -381,7 +402,7 @@ public function removeSubNode($mainNode, $name) if ($subName !== null) { $curVal = json_decode($matches['content'], true); unset($curVal[$name][$subName]); - $childrenClean = $that->format($curVal, 0); + $childrenClean = $that->format($curVal); } return $matches['start'] . $childrenClean . $matches['end']; @@ -463,6 +484,21 @@ public function removeMainKey($key) return false; } + public function removeMainKeyIfEmpty($key) + { + $decoded = JsonFile::parseJson($this->contents); + + if (!array_key_exists($key, $decoded)) { + return true; + } + + if (is_array($decoded[$key]) && count($decoded[$key]) === 0) { + return $this->removeMainKey($key); + } + + return true; + } + public function format($data, $depth = 0) { if (is_array($data)) { diff --git a/app/vendor/composer/composer/src/Composer/Json/JsonValidationException.php b/app/vendor/composer/composer/src/Composer/Json/JsonValidationException.php index 2fa5eb489..e4533f672 100644 --- a/app/vendor/composer/composer/src/Composer/Json/JsonValidationException.php +++ b/app/vendor/composer/composer/src/Composer/Json/JsonValidationException.php @@ -19,14 +19,24 @@ */ class JsonValidationException extends Exception { + /** + * @var string[] + */ protected $errors; + /** + * @param string $message + * @param string[] $errors + */ public function __construct($message, $errors = array(), Exception $previous = null) { $this->errors = $errors; - parent::__construct($message, 0, $previous); + parent::__construct((string) $message, 0, $previous); } + /** + * @return string[] + */ public function getErrors() { return $this->errors; diff --git a/app/vendor/composer/composer/src/Composer/Package/AliasPackage.php b/app/vendor/composer/composer/src/Composer/Package/AliasPackage.php index ee93ec497..c993230c6 100644 --- a/app/vendor/composer/composer/src/Composer/Package/AliasPackage.php +++ b/app/vendor/composer/composer/src/Composer/Package/AliasPackage.php @@ -18,15 +18,16 @@ /** * @author Jordi Boggiano */ -class AliasPackage extends BasePackage implements CompletePackageInterface +class AliasPackage extends BasePackage { protected $version; protected $prettyVersion; protected $dev; protected $rootPackageAlias = false; protected $stability; + protected $hasSelfVersionRequires = false; - /** @var PackageInterface */ + /** @var BasePackage */ protected $aliasOf; /** @var Link[] */ protected $requires; @@ -42,11 +43,11 @@ class AliasPackage extends BasePackage implements CompletePackageInterface /** * All descendants' constructors should call this parent constructor * - * @param PackageInterface $aliasOf The package this package is an alias of - * @param string $version The version the alias must report - * @param string $prettyVersion The alias's non-normalized version + * @param BasePackage $aliasOf The package this package is an alias of + * @param string $version The version the alias must report + * @param string $prettyVersion The alias's non-normalized version */ - public function __construct(PackageInterface $aliasOf, $version, $prettyVersion) + public function __construct(BasePackage $aliasOf, $version, $prettyVersion) { parent::__construct($aliasOf->getName()); @@ -56,14 +57,14 @@ public function __construct(PackageInterface $aliasOf, $version, $prettyVersion) $this->stability = VersionParser::parseStability($version); $this->dev = $this->stability === 'dev'; - foreach (array('requires', 'devRequires', 'conflicts', 'provides', 'replaces') as $type) { + foreach (Link::$TYPES as $type) { $links = $aliasOf->{'get' . ucfirst($type)}(); $this->$type = $this->replaceSelfVersionDependencies($links, $type); } } /** - * @return PackageInterface + * @return BasePackage */ public function getAliasOf() { @@ -112,6 +113,7 @@ public function getRequires() /** * {@inheritDoc} + * @return array */ public function getConflicts() { @@ -120,6 +122,7 @@ public function getConflicts() /** * {@inheritDoc} + * @return array */ public function getProvides() { @@ -128,6 +131,7 @@ public function getProvides() /** * {@inheritDoc} + * @return array */ public function getReplaces() { @@ -166,26 +170,37 @@ public function isRootPackageAlias() } /** - * @param Link[] $links - * @param string $linkType + * @param Link[] $links + * @param Link::TYPE_* $linkType * * @return Link[] */ protected function replaceSelfVersionDependencies(array $links, $linkType) { - if (in_array($linkType, array('conflicts', 'provides', 'replaces'), true)) { + // for self.version requirements, we use the original package's branch name instead, to avoid leaking the magic dev-master-alias to users + $prettyVersion = $this->prettyVersion; + if ($prettyVersion === VersionParser::DEFAULT_BRANCH_ALIAS) { + $prettyVersion = $this->aliasOf->getPrettyVersion(); + } + + if (\in_array($linkType, array(Link::TYPE_CONFLICT, Link::TYPE_PROVIDE, Link::TYPE_REPLACE), true)) { $newLinks = array(); foreach ($links as $link) { // link is self.version, but must be replacing also the replaced version if ('self.version' === $link->getPrettyConstraint()) { - $newLinks[] = new Link($link->getSource(), $link->getTarget(), new Constraint('=', $this->version), $linkType, $this->prettyVersion); + $newLinks[] = new Link($link->getSource(), $link->getTarget(), $constraint = new Constraint('=', $this->version), $linkType, $prettyVersion); + $constraint->setPrettyString($prettyVersion); } } $links = array_merge($links, $newLinks); } else { foreach ($links as $index => $link) { if ('self.version' === $link->getPrettyConstraint()) { - $links[$index] = new Link($link->getSource(), $link->getTarget(), new Constraint('=', $this->version), $linkType, $this->prettyVersion); + if ($linkType === Link::TYPE_REQUIRE) { + $this->hasSelfVersionRequires = true; + } + $links[$index] = new Link($link->getSource(), $link->getTarget(), $constraint = new Constraint('=', $this->version), $linkType, $prettyVersion); + $constraint->setPrettyString($prettyVersion); } } } @@ -193,6 +208,16 @@ protected function replaceSelfVersionDependencies(array $links, $linkType) return $links; } + public function hasSelfVersionRequires() + { + return $this->hasSelfVersionRequires; + } + + public function __toString() + { + return parent::__toString().' ('.($this->rootPackageAlias ? 'root ' : ''). 'alias of '.$this->aliasOf->getVersion().')'; + } + /*************************************** * Wrappers around the aliased package * ***************************************/ @@ -244,12 +269,12 @@ public function getSourceReference() public function setSourceReference($reference) { - return $this->aliasOf->setSourceReference($reference); + $this->aliasOf->setSourceReference($reference); } public function setSourceMirrors($mirrors) { - return $this->aliasOf->setSourceMirrors($mirrors); + $this->aliasOf->setSourceMirrors($mirrors); } public function getSourceMirrors() @@ -279,7 +304,7 @@ public function getDistReference() public function setDistReference($reference) { - return $this->aliasOf->setDistReference($reference); + $this->aliasOf->setDistReference($reference); } public function getDistSha1Checksum() @@ -289,7 +314,7 @@ public function getDistSha1Checksum() public function setTransportOptions(array $options) { - return $this->aliasOf->setTransportOptions($options); + $this->aliasOf->setTransportOptions($options); } public function getTransportOptions() @@ -299,7 +324,7 @@ public function getTransportOptions() public function setDistMirrors($mirrors) { - return $this->aliasOf->setDistMirrors($mirrors); + $this->aliasOf->setDistMirrors($mirrors); } public function getDistMirrors() @@ -307,16 +332,6 @@ public function getDistMirrors() return $this->aliasOf->getDistMirrors(); } - public function getScripts() - { - return $this->aliasOf->getScripts(); - } - - public function getLicense() - { - return $this->aliasOf->getLicense(); - } - public function getAutoload() { return $this->aliasOf->getAutoload(); @@ -332,11 +347,6 @@ public function getIncludePaths() return $this->aliasOf->getIncludePaths(); } - public function getRepositories() - { - return $this->aliasOf->getRepositories(); - } - public function getReleaseDate() { return $this->aliasOf->getReleaseDate(); @@ -347,73 +357,33 @@ public function getBinaries() return $this->aliasOf->getBinaries(); } - public function getKeywords() - { - return $this->aliasOf->getKeywords(); - } - - public function getDescription() - { - return $this->aliasOf->getDescription(); - } - - public function getHomepage() - { - return $this->aliasOf->getHomepage(); - } - public function getSuggests() { return $this->aliasOf->getSuggests(); } - public function getAuthors() - { - return $this->aliasOf->getAuthors(); - } - - public function getSupport() - { - return $this->aliasOf->getSupport(); - } - - public function getFunding() - { - return $this->aliasOf->getFunding(); - } - public function getNotificationUrl() { return $this->aliasOf->getNotificationUrl(); } - public function getArchiveExcludes() + public function isDefaultBranch() { - return $this->aliasOf->getArchiveExcludes(); + return $this->aliasOf->isDefaultBranch(); } - public function isAbandoned() - { - return $this->aliasOf->isAbandoned(); - } - - public function getReplacementPackage() - { - return $this->aliasOf->getReplacementPackage(); - } - - public function __toString() + public function setDistUrl($url) { - return parent::__toString().' (alias of '.$this->aliasOf->getVersion().')'; + $this->aliasOf->setDistUrl($url); } - public function setDistUrl($url) + public function setDistType($type) { - return $this->aliasOf->setDistUrl($url); + $this->aliasOf->setDistType($type); } - public function setDistType($type) + public function setSourceDistReferences($reference) { - return $this->aliasOf->setDistType($type); + $this->aliasOf->setSourceDistReferences($reference); } } diff --git a/app/vendor/composer/composer/src/Composer/Package/Archiver/ArchivableFilesFilter.php b/app/vendor/composer/composer/src/Composer/Package/Archiver/ArchivableFilesFilter.php index 4a9a5af1f..f49486a04 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Archiver/ArchivableFilesFilter.php +++ b/app/vendor/composer/composer/src/Composer/Package/Archiver/ArchivableFilesFilter.php @@ -22,6 +22,7 @@ class ArchivableFilesFilter extends FilterIterator /** * @return bool true if the current element is acceptable, otherwise false. */ + #[\ReturnTypeWillChange] public function accept() { $file = $this->getInnerIterator()->current(); diff --git a/app/vendor/composer/composer/src/Composer/Package/Archiver/ArchivableFilesFinder.php b/app/vendor/composer/composer/src/Composer/Package/Archiver/ArchivableFilesFinder.php index 4a8042abe..98cdf941b 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Archiver/ArchivableFilesFinder.php +++ b/app/vendor/composer/composer/src/Composer/Package/Archiver/ArchivableFilesFinder.php @@ -89,6 +89,7 @@ public function __construct($sources, array $excludes, $ignoreFilters = false) parent::__construct($this->finder->getIterator()); } + #[\ReturnTypeWillChange] public function accept() { /** @var SplFileInfo $current */ diff --git a/app/vendor/composer/composer/src/Composer/Package/Archiver/ArchiveManager.php b/app/vendor/composer/composer/src/Composer/Package/Archiver/ArchiveManager.php index 6f8fa8a01..356c53646 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Archiver/ArchiveManager.php +++ b/app/vendor/composer/composer/src/Composer/Package/Archiver/ArchiveManager.php @@ -13,10 +13,12 @@ namespace Composer\Package\Archiver; use Composer\Downloader\DownloadManager; -use Composer\Package\PackageInterface; use Composer\Package\RootPackageInterface; use Composer\Util\Filesystem; +use Composer\Util\Loop; +use Composer\Util\SyncHelper; use Composer\Json\JsonFile; +use Composer\Package\CompletePackageInterface; /** * @author Matthieu Moquet @@ -24,8 +26,14 @@ */ class ArchiveManager { + /** @var DownloadManager */ protected $downloadManager; + /** @var Loop */ + protected $loop; + /** + * @var ArchiverInterface[] + */ protected $archivers = array(); /** @@ -36,9 +44,10 @@ class ArchiveManager /** * @param DownloadManager $downloadManager A manager used to download package sources */ - public function __construct(DownloadManager $downloadManager) + public function __construct(DownloadManager $downloadManager, Loop $loop) { $this->downloadManager = $downloadManager; + $this->loop = $loop; } /** @@ -66,15 +75,20 @@ public function setOverwriteFiles($overwriteFiles) /** * Generate a distinct filename for a particular version of a package. * - * @param PackageInterface $package The package to get a name for + * @param CompletePackageInterface $package The package to get a name for * * @return string A filename without an extension */ - public function getPackageFilename(PackageInterface $package) + public function getPackageFilename(CompletePackageInterface $package) { - $nameParts = array(preg_replace('#[^a-z0-9-_]#i', '-', $package->getName())); + if ($package->getArchiveName()) { + $baseName = $package->getArchiveName(); + } else { + $baseName = preg_replace('#[^a-z0-9-_]#i', '-', $package->getName()); + } + $nameParts = array($baseName); - if (preg_match('{^[a-f0-9]{40}$}', $package->getDistReference())) { + if (null !== $package->getDistReference() && preg_match('{^[a-f0-9]{40}$}', $package->getDistReference())) { array_push($nameParts, $package->getDistReference(), $package->getDistType()); } else { array_push($nameParts, $package->getPrettyVersion(), $package->getDistReference()); @@ -94,7 +108,7 @@ public function getPackageFilename(PackageInterface $package) /** * Create an archive of the specified package. * - * @param PackageInterface $package The package to archive + * @param CompletePackageInterface $package The package to archive * @param string $format The format of the archive (zip, tar, ...) * @param string $targetDir The directory where to build the archive * @param string|null $fileName The relative file name to use for the archive, or null to generate @@ -104,7 +118,7 @@ public function getPackageFilename(PackageInterface $package) * @throws \RuntimeException * @return string The path of the created archive */ - public function archive(PackageInterface $package, $format, $targetDir, $fileName = null, $ignoreFilters = false) + public function archive(CompletePackageInterface $package, $format, $targetDir, $fileName = null, $ignoreFilters = false) { if (empty($format)) { throw new \InvalidArgumentException('Format must be specified'); @@ -125,20 +139,6 @@ public function archive(PackageInterface $package, $format, $targetDir, $fileNam } $filesystem = new Filesystem(); - if (null === $fileName) { - $packageName = $this->getPackageFilename($package); - } else { - $packageName = $fileName; - } - - // Archive filename - $filesystem->ensureDirectoryExists($targetDir); - $target = realpath($targetDir).'/'.$packageName.'.'.$format; - $filesystem->ensureDirectoryExists(dirname($target)); - - if (!$this->overwriteFiles && file_exists($target)) { - return $target; - } if ($package instanceof RootPackageInterface) { $sourcePath = realpath('.'); @@ -149,7 +149,10 @@ public function archive(PackageInterface $package, $format, $targetDir, $fileNam try { // Download sources - $this->downloadManager->download($package, $sourcePath); + $promise = $this->downloadManager->download($package, $sourcePath); + SyncHelper::await($this->loop, $promise); + $promise = $this->downloadManager->install($package, $sourcePath); + SyncHelper::await($this->loop, $promise); } catch (\Exception $e) { $filesystem->removeDirectory($sourcePath); throw $e; @@ -159,12 +162,30 @@ public function archive(PackageInterface $package, $format, $targetDir, $fileNam if (file_exists($composerJsonPath = $sourcePath.'/composer.json')) { $jsonFile = new JsonFile($composerJsonPath); $jsonData = $jsonFile->read(); + if (!empty($jsonData['archive']['name'])) { + $package->setArchiveName($jsonData['archive']['name']); + } if (!empty($jsonData['archive']['exclude'])) { $package->setArchiveExcludes($jsonData['archive']['exclude']); } } } + if (null === $fileName) { + $packageName = $this->getPackageFilename($package); + } else { + $packageName = $fileName; + } + + // Archive filename + $filesystem->ensureDirectoryExists($targetDir); + $target = realpath($targetDir).'/'.$packageName.'.'.$format; + $filesystem->ensureDirectoryExists(dirname($target)); + + if (!$this->overwriteFiles && file_exists($target)) { + return $target; + } + // Create the archive $tempTarget = sys_get_temp_dir().'/composer_archive'.uniqid().'.'.$format; $filesystem->ensureDirectoryExists(dirname($tempTarget)); diff --git a/app/vendor/composer/composer/src/Composer/Package/Archiver/BaseExcludeFilter.php b/app/vendor/composer/composer/src/Composer/Package/Archiver/BaseExcludeFilter.php index 8e69ed069..a67ad272a 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Archiver/BaseExcludeFilter.php +++ b/app/vendor/composer/composer/src/Composer/Package/Archiver/BaseExcludeFilter.php @@ -123,26 +123,25 @@ protected function generatePatterns($rules) protected function generatePattern($rule) { $negate = false; - $pattern = '{'; + $pattern = ''; - if (strlen($rule) && $rule[0] === '!') { + if ($rule !== '' && $rule[0] === '!') { $negate = true; - $rule = substr($rule, 1); + $rule = ltrim($rule, '!'); } - if (strlen($rule) && $rule[0] === '/') { - $pattern .= '^/'; - $rule = substr($rule, 1); - } elseif (strlen($rule) - 1 === strpos($rule, '/')) { - $pattern .= '/'; - $rule = substr($rule, 0, -1); - } elseif (false === strpos($rule, '/')) { - $pattern .= '/'; + $firstSlashPosition = strpos($rule, '/'); + if (0 === $firstSlashPosition) { + $pattern = '^/'; + } elseif (false === $firstSlashPosition || strlen($rule) - 1 === $firstSlashPosition) { + $pattern = '/'; } + $rule = trim($rule, '/'); + // remove delimiters as well as caret (^) and dollar sign ($) from the regex - $pattern .= substr(Finder\Glob::toRegex($rule), 2, -2) . '(?=$|/)'; + $rule = substr(Finder\Glob::toRegex($rule), 2, -2); - return array($pattern . '}', $negate, false); + return array('{'.$pattern.$rule.'(?=$|/)}', $negate, false); } } diff --git a/app/vendor/composer/composer/src/Composer/Package/Archiver/PharArchiver.php b/app/vendor/composer/composer/src/Composer/Package/Archiver/PharArchiver.php index f9a392353..955d1b3c9 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Archiver/PharArchiver.php +++ b/app/vendor/composer/composer/src/Composer/Package/Archiver/PharArchiver.php @@ -52,7 +52,12 @@ public function archive($sources, $target, $format, array $excludes = array(), $ $target = $filename . '.tar'; } - $phar = new \PharData($target, null, null, static::$formats[$format]); + $phar = new \PharData( + $target, + \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO, + '', + static::$formats[$format] + ); $files = new ArchivableFilesFinder($sources, $excludes, $ignoreFilters); $filesOnly = new ArchivableFilesFilter($files); $phar->buildFromIterator($filesOnly, $sources); diff --git a/app/vendor/composer/composer/src/Composer/Package/Archiver/ZipArchiver.php b/app/vendor/composer/composer/src/Composer/Package/Archiver/ZipArchiver.php index b6cfaf73f..c0ce41d65 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Archiver/ZipArchiver.php +++ b/app/vendor/composer/composer/src/Composer/Package/Archiver/ZipArchiver.php @@ -39,7 +39,10 @@ public function archive($sources, $target, $format, array $excludes = array(), $ foreach ($files as $file) { /** @var \SplFileInfo $file */ $filepath = strtr($file->getPath()."/".$file->getFilename(), '\\', '/'); - $localname = str_replace($sources.'/', '', $filepath); + $localname = $filepath; + if (strpos($localname, $sources . '/') === 0) { + $localname = substr($localname, strlen($sources . '/')); + } if ($file->isDir()) { $zip->addEmptyDir($localname); } else { @@ -48,8 +51,9 @@ public function archive($sources, $target, $format, array $excludes = array(), $ /** * ZipArchive::setExternalAttributesName is available from >= PHP 5.6 + * setExternalAttributesName() is only available with libzip 0.11.2 or above */ - if (PHP_VERSION_ID >= 50600) { + if (PHP_VERSION_ID >= 50600 && method_exists($zip, 'setExternalAttributesName')) { $perms = fileperms($filepath); /** diff --git a/app/vendor/composer/composer/src/Composer/Package/BasePackage.php b/app/vendor/composer/composer/src/Composer/Package/BasePackage.php index 3987e7e87..04a57f5bc 100644 --- a/app/vendor/composer/composer/src/Composer/Package/BasePackage.php +++ b/app/vendor/composer/composer/src/Composer/Package/BasePackage.php @@ -22,12 +22,16 @@ */ abstract class BasePackage implements PackageInterface { + /** + * @phpstan-var array + * @internal + */ public static $supportedLinkTypes = array( - 'require' => array('description' => 'requires', 'method' => 'requires'), - 'conflict' => array('description' => 'conflicts', 'method' => 'conflicts'), - 'provide' => array('description' => 'provides', 'method' => 'provides'), - 'replace' => array('description' => 'replaces', 'method' => 'replaces'), - 'require-dev' => array('description' => 'requires (for development)', 'method' => 'devRequires'), + 'require' => array('description' => 'requires', 'method' => Link::TYPE_REQUIRE), + 'conflict' => array('description' => 'conflicts', 'method' => Link::TYPE_CONFLICT), + 'provide' => array('description' => 'provides', 'method' => Link::TYPE_PROVIDE), + 'replace' => array('description' => 'replaces', 'method' => Link::TYPE_REPLACE), + 'require-dev' => array('description' => 'requires (for development)', 'method' => Link::TYPE_DEV_REQUIRE), ); const STABILITY_STABLE = 0; @@ -47,16 +51,16 @@ abstract class BasePackage implements PackageInterface /** * READ-ONLY: The package id, public for fast access in dependency solver * @var int + * @internal + * @readonly */ public $id; /** @var string */ protected $name; /** @var string */ protected $prettyName; - /** @var RepositoryInterface */ - protected $repository; - /** @var array */ - protected $transportOptions = array(); + /** @var ?RepositoryInterface */ + protected $repository = null; /** * All descendants' constructors should call this parent constructor @@ -89,14 +93,16 @@ public function getPrettyName() /** * {@inheritDoc} */ - public function getNames() + public function getNames($provides = true) { $names = array( $this->getName() => true, ); - foreach ($this->getProvides() as $link) { - $names[$link->getTarget()] = true; + if ($provides) { + foreach ($this->getProvides() as $link) { + $names[$link->getTarget()] = true; + } } foreach ($this->getReplaces() as $link) { @@ -141,24 +147,6 @@ public function getRepository() return $this->repository; } - /** - * {@inheritDoc} - */ - public function getTransportOptions() - { - return $this->transportOptions; - } - - /** - * Configures the list of options to download package dist files - * - * @param array $options - */ - public function setTransportOptions(array $options) - { - $this->transportOptions = $options; - } - /** * checks if this package is a platform package * @@ -210,18 +198,36 @@ public function getPrettyString() /** * {@inheritDoc} */ - public function getFullPrettyVersion($truncate = true) + public function getFullPrettyVersion($truncate = true, $displayMode = PackageInterface::DISPLAY_SOURCE_REF_IF_DEV) { - if (!$this->isDev() || !in_array($this->getSourceType(), array('hg', 'git'))) { + if ($displayMode === PackageInterface::DISPLAY_SOURCE_REF_IF_DEV && + (!$this->isDev() || !\in_array($this->getSourceType(), array('hg', 'git'))) + ) { + return $this->getPrettyVersion(); + } + + switch ($displayMode) { + case PackageInterface::DISPLAY_SOURCE_REF_IF_DEV: + case PackageInterface::DISPLAY_SOURCE_REF: + $reference = $this->getSourceReference(); + break; + case PackageInterface::DISPLAY_DIST_REF: + $reference = $this->getDistReference(); + break; + default: + throw new \UnexpectedValueException('Display mode '.$displayMode.' is not supported'); + } + + if (null === $reference) { return $this->getPrettyVersion(); } // if source reference is a sha1 hash -- truncate - if ($truncate && strlen($this->getSourceReference()) === 40) { - return $this->getPrettyVersion() . ' ' . substr($this->getSourceReference(), 0, 7); + if ($truncate && \strlen($reference) === 40 && $this->getSourceType() !== 'svn') { + return $this->getPrettyVersion() . ' ' . substr($reference, 0, 7); } - return $this->getPrettyVersion() . ' ' . $this->getSourceReference(); + return $this->getPrettyVersion() . ' ' . $reference; } public function getStabilityPriority() @@ -238,14 +244,14 @@ public function __clone() /** * Build a regexp from a package name, expanding * globs as required * - * @param string $allowListPattern - * @param string $wrap Wrap the cleaned string by the given string + * @param string $allowPattern + * @param string $wrap Wrap the cleaned string by the given string * @return string */ - public static function packageNameToRegexp($allowListPattern, $wrap = '{^%s$}i') + public static function packageNameToRegexp($allowPattern, $wrap = '{^%s$}i') { - $cleanedAllowListPattern = str_replace('\\*', '.*', preg_quote($allowListPattern)); + $cleanedAllowPattern = str_replace('\\*', '.*', preg_quote($allowPattern)); - return sprintf($wrap, $cleanedAllowListPattern); + return sprintf($wrap, $cleanedAllowPattern); } } diff --git a/app/vendor/composer/composer/src/Composer/Package/Comparer/Comparer.php b/app/vendor/composer/composer/src/Composer/Package/Comparer/Comparer.php index ed08cad42..652a94d28 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Comparer/Comparer.php +++ b/app/vendor/composer/composer/src/Composer/Package/Comparer/Comparer.php @@ -112,7 +112,6 @@ private function doTree($dir, &$array) return false; } } elseif (is_file($dir.'/'.$file) && filesize($dir.'/'.$file)) { - set_time_limit(30); $array[$dir][$file] = md5_file($dir.'/'.$file); } } diff --git a/app/vendor/composer/composer/src/Composer/Package/CompleteAliasPackage.php b/app/vendor/composer/composer/src/Composer/Package/CompleteAliasPackage.php new file mode 100644 index 000000000..5c9e340f3 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Package/CompleteAliasPackage.php @@ -0,0 +1,167 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package; + +/** + * @author Jordi Boggiano + */ +class CompleteAliasPackage extends AliasPackage implements CompletePackageInterface +{ + /** @var CompletePackage */ + protected $aliasOf; + + /** + * All descendants' constructors should call this parent constructor + * + * @param CompletePackage $aliasOf The package this package is an alias of + * @param string $version The version the alias must report + * @param string $prettyVersion The alias's non-normalized version + */ + public function __construct(CompletePackage $aliasOf, $version, $prettyVersion) + { + parent::__construct($aliasOf, $version, $prettyVersion); + } + + /** + * @return CompletePackage + */ + public function getAliasOf() + { + return $this->aliasOf; + } + + public function getScripts() + { + return $this->aliasOf->getScripts(); + } + + public function setScripts(array $scripts) + { + $this->aliasOf->setScripts($scripts); + } + + public function getRepositories() + { + return $this->aliasOf->getRepositories(); + } + + public function setRepositories(array $repositories) + { + $this->aliasOf->setRepositories($repositories); + } + + public function getLicense() + { + return $this->aliasOf->getLicense(); + } + + public function setLicense(array $license) + { + $this->aliasOf->setLicense($license); + } + + public function getKeywords() + { + return $this->aliasOf->getKeywords(); + } + + public function setKeywords(array $keywords) + { + $this->aliasOf->setKeywords($keywords); + } + + public function getDescription() + { + return $this->aliasOf->getDescription(); + } + + public function setDescription($description) + { + $this->aliasOf->setDescription($description); + } + + public function getHomepage() + { + return $this->aliasOf->getHomepage(); + } + + public function setHomepage($homepage) + { + $this->aliasOf->setHomepage($homepage); + } + + public function getAuthors() + { + return $this->aliasOf->getAuthors(); + } + + public function setAuthors(array $authors) + { + $this->aliasOf->setAuthors($authors); + } + + public function getSupport() + { + return $this->aliasOf->getSupport(); + } + + public function setSupport(array $support) + { + $this->aliasOf->setSupport($support); + } + + public function getFunding() + { + return $this->aliasOf->getFunding(); + } + + public function setFunding(array $funding) + { + $this->aliasOf->setFunding($funding); + } + + public function isAbandoned() + { + return $this->aliasOf->isAbandoned(); + } + + public function getReplacementPackage() + { + return $this->aliasOf->getReplacementPackage(); + } + + public function setAbandoned($abandoned) + { + $this->aliasOf->setAbandoned($abandoned); + } + + public function getArchiveName() + { + return $this->aliasOf->getArchiveName(); + } + + public function setArchiveName($name) + { + $this->aliasOf->setArchiveName($name); + } + + public function getArchiveExcludes() + { + return $this->aliasOf->getArchiveExcludes(); + } + + public function setArchiveExcludes(array $excludes) + { + $this->aliasOf->setArchiveExcludes($excludes); + } +} diff --git a/app/vendor/composer/composer/src/Composer/Package/CompletePackage.php b/app/vendor/composer/composer/src/Composer/Package/CompletePackage.php index 785d5817c..dc0b115c3 100644 --- a/app/vendor/composer/composer/src/Composer/Package/CompletePackage.php +++ b/app/vendor/composer/composer/src/Composer/Package/CompletePackage.php @@ -19,19 +19,21 @@ */ class CompletePackage extends Package implements CompletePackageInterface { - protected $repositories; + protected $repositories = array(); protected $license = array(); - protected $keywords; - protected $authors; + protected $keywords = array(); + protected $authors = array(); protected $description; protected $homepage; protected $scripts = array(); protected $support = array(); protected $funding = array(); protected $abandoned = false; + protected $archiveName; + protected $archiveExcludes = array(); /** - * @param array $scripts + * {@inheritDoc} */ public function setScripts(array $scripts) { @@ -47,11 +49,9 @@ public function getScripts() } /** - * Set the repositories - * - * @param array $repositories + * {@inheritDoc} */ - public function setRepositories($repositories) + public function setRepositories(array $repositories) { $this->repositories = $repositories; } @@ -65,9 +65,7 @@ public function getRepositories() } /** - * Set the license - * - * @param array $license + * {@inheritDoc} */ public function setLicense(array $license) { @@ -83,9 +81,7 @@ public function getLicense() } /** - * Set the keywords - * - * @param array $keywords + * {@inheritDoc} */ public function setKeywords(array $keywords) { @@ -101,9 +97,7 @@ public function getKeywords() } /** - * Set the authors - * - * @param array $authors + * {@inheritDoc} */ public function setAuthors(array $authors) { @@ -119,9 +113,7 @@ public function getAuthors() } /** - * Set the description - * - * @param string $description + * {@inheritDoc} */ public function setDescription($description) { @@ -137,9 +129,7 @@ public function getDescription() } /** - * Set the homepage - * - * @param string $homepage + * {@inheritDoc} */ public function setHomepage($homepage) { @@ -155,9 +145,7 @@ public function getHomepage() } /** - * Set the support information - * - * @param array $support + * {@inheritDoc} */ public function setSupport(array $support) { @@ -173,9 +161,7 @@ public function getSupport() } /** - * Set the funding - * - * @param array $funding + * {@inheritDoc} */ public function setFunding(array $funding) { @@ -191,7 +177,7 @@ public function getFunding() } /** - * @return bool + * {@inheritDoc} */ public function isAbandoned() { @@ -199,7 +185,7 @@ public function isAbandoned() } /** - * @param bool|string $abandoned + * {@inheritDoc} */ public function setAbandoned($abandoned) { @@ -207,12 +193,42 @@ public function setAbandoned($abandoned) } /** - * If the package is abandoned and has a suggested replacement, this method returns it - * - * @return string|null + * {@inheritDoc} */ public function getReplacementPackage() { - return is_string($this->abandoned) ? $this->abandoned : null; + return \is_string($this->abandoned) ? $this->abandoned : null; + } + + /** + * {@inheritDoc} + */ + public function setArchiveName($name) + { + $this->archiveName = $name; + } + + /** + * {@inheritDoc} + */ + public function getArchiveName() + { + return $this->archiveName; + } + + /** + * {@inheritDoc} + */ + public function setArchiveExcludes(array $excludes) + { + $this->archiveExcludes = $excludes; + } + + /** + * {@inheritDoc} + */ + public function getArchiveExcludes() + { + return $this->archiveExcludes; } } diff --git a/app/vendor/composer/composer/src/Composer/Package/CompletePackageInterface.php b/app/vendor/composer/composer/src/Composer/Package/CompletePackageInterface.php index 7782886d3..0d9425496 100644 --- a/app/vendor/composer/composer/src/Composer/Package/CompletePackageInterface.php +++ b/app/vendor/composer/composer/src/Composer/Package/CompletePackageInterface.php @@ -22,33 +22,61 @@ interface CompletePackageInterface extends PackageInterface /** * Returns the scripts of this package * - * @return array array('script name' => array('listeners')) + * @return array array('script name' => array('listeners')) */ public function getScripts(); + /** + * @param array $scripts + * @return void + */ + public function setScripts(array $scripts); + /** * Returns an array of repositories * - * {"": {}} - * - * @return array Repositories + * @return array Repositories */ public function getRepositories(); + /** + * Set the repositories + * + * @param array $repositories + * @return void + */ + public function setRepositories(array $repositories); + /** * Returns the package license, e.g. MIT, BSD, GPL * - * @return array The package licenses + * @return string[] The package licenses */ public function getLicense(); + /** + * Set the license + * + * @param string[] $license + * @return void + */ + public function setLicense(array $license); + /** * Returns an array of keywords relating to the package * - * @return array + * @return string[] */ public function getKeywords(); + /** + * Set the keywords + * + * @param string[] $keywords + * @return void + */ + public function setKeywords(array $keywords); + /** * Returns the package description * @@ -56,6 +84,14 @@ public function getKeywords(); */ public function getDescription(); + /** + * Set the description + * + * @param string $description + * @return void + */ + public function setDescription($description); + /** * Returns the package homepage * @@ -63,31 +99,63 @@ public function getDescription(); */ public function getHomepage(); + /** + * Set the homepage + * + * @param string $homepage + * @return void + */ + public function setHomepage($homepage); + /** * Returns an array of authors of the package * * Each item can contain name/homepage/email keys * - * @return array + * @return array */ public function getAuthors(); + /** + * Set the authors + * + * @param array $authors + * @return void + */ + public function setAuthors(array $authors); + /** * Returns the support information * - * @return array + * @return array */ public function getSupport(); + /** + * Set the support information + * + * @param array $support + * @return void + */ + public function setSupport(array $support); + /** * Returns an array of funding options for the package * * Each item will contain type and url keys * - * @return array + * @return array */ public function getFunding(); + /** + * Set the funding + * + * @param array $funding + * @return void + */ + public function setFunding(array $funding); + /** * Returns if the package is abandoned or not * @@ -98,7 +166,43 @@ public function isAbandoned(); /** * If the package is abandoned and has a suggested replacement, this method returns it * - * @return string + * @return string|null */ public function getReplacementPackage(); + + /** + * @param bool|string $abandoned + * @return void + */ + public function setAbandoned($abandoned); + + /** + * Returns default base filename for archive + * + * @return array + */ + public function getArchiveName(); + + /** + * Sets default base filename for archive + * + * @param string $name + * @return void + */ + public function setArchiveName($name); + + /** + * Returns a list of patterns to exclude from package archives + * + * @return array + */ + public function getArchiveExcludes(); + + /** + * Sets a list of patterns to be excluded from archives + * + * @param string[] $excludes + * @return void + */ + public function setArchiveExcludes(array $excludes); } diff --git a/app/vendor/composer/composer/src/Composer/Package/Dumper/ArrayDumper.php b/app/vendor/composer/composer/src/Composer/Package/Dumper/ArrayDumper.php index dece598f1..e3b053b87 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Dumper/ArrayDumper.php +++ b/app/vendor/composer/composer/src/Composer/Package/Dumper/ArrayDumper.php @@ -70,10 +70,6 @@ public function dump(PackageInterface $package) } } - if ($package->getArchiveExcludes()) { - $data['archive']['exclude'] = $package->getArchiveExcludes(); - } - foreach (BasePackage::$supportedLinkTypes as $type => $opts) { if ($links = $package->{'get'.ucfirst($opts['method'])}()) { foreach ($links as $link) { @@ -92,9 +88,20 @@ public function dump(PackageInterface $package) $data['time'] = $package->getReleaseDate()->format(DATE_RFC3339); } + if ($package->isDefaultBranch()) { + $data['default-branch'] = true; + } + $data = $this->dumpValues($package, $keys, $data); if ($package instanceof CompletePackageInterface) { + if ($package->getArchiveName()) { + $data['archive']['name'] = $package->getArchiveName(); + } + if ($package->getArchiveExcludes()) { + $data['archive']['exclude'] = $package->getArchiveExcludes(); + } + $keys = array( 'scripts', 'license', @@ -109,7 +116,7 @@ public function dump(PackageInterface $package) $data = $this->dumpValues($package, $keys, $data); - if (isset($data['keywords']) && is_array($data['keywords'])) { + if (isset($data['keywords']) && \is_array($data['keywords'])) { sort($data['keywords']); } @@ -125,7 +132,7 @@ public function dump(PackageInterface $package) } } - if (count($package->getTransportOptions()) > 0) { + if (\count($package->getTransportOptions()) > 0) { $data['transport-options'] = $package->getTransportOptions(); } @@ -142,7 +149,7 @@ private function dumpValues(PackageInterface $package, array $keys, array $data) $getter = 'get'.ucfirst($method); $value = $package->$getter(); - if (null !== $value && !(is_array($value) && 0 === count($value))) { + if (null !== $value && !(\is_array($value) && 0 === \count($value))) { $data[$key] = $value; } } diff --git a/app/vendor/composer/composer/src/Composer/Package/Link.php b/app/vendor/composer/composer/src/Composer/Package/Link.php index 217da0713..af08705df 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Link.php +++ b/app/vendor/composer/composer/src/Composer/Package/Link.php @@ -21,6 +21,38 @@ */ class Link { + const TYPE_REQUIRE = 'requires'; + const TYPE_DEV_REQUIRE = 'devRequires'; + const TYPE_PROVIDE = 'provides'; + const TYPE_CONFLICT = 'conflicts'; + const TYPE_REPLACE = 'replaces'; + + /** + * Special type + * @internal + */ + const TYPE_DOES_NOT_REQUIRE = 'does not require'; + /** + * TODO should be marked private once 5.3 is dropped + * @private + */ + const TYPE_UNKNOWN = 'relates to'; + + /** + * Will be converted into a constant once the min PHP version allows this + * + * @internal + * @var string[] + * @phpstan-var array + */ + public static $TYPES = array( + self::TYPE_REQUIRE, + self::TYPE_DEV_REQUIRE, + self::TYPE_PROVIDE, + self::TYPE_CONFLICT, + self::TYPE_REPLACE, + ); + /** * @var string */ @@ -32,35 +64,41 @@ class Link protected $target; /** - * @var ConstraintInterface|null + * @var ConstraintInterface */ protected $constraint; /** * @var string + * @phpstan-var string $description */ protected $description; /** - * @var string|null + * @var ?string */ protected $prettyConstraint; /** * Creates a new package link. * - * @param string $source - * @param string $target - * @param ConstraintInterface|null $constraint Constraint applying to the target of this link - * @param string $description Used to create a descriptive string representation - * @param string|null $prettyConstraint + * @param string $source + * @param string $target + * @param ConstraintInterface $constraint Constraint applying to the target of this link + * @param self::TYPE_* $description Used to create a descriptive string representation + * @param string|null $prettyConstraint */ - public function __construct($source, $target, ConstraintInterface $constraint = null, $description = 'relates to', $prettyConstraint = null) - { + public function __construct( + $source, + $target, + ConstraintInterface $constraint, + $description = self::TYPE_UNKNOWN, + $prettyConstraint = null + ) { $this->source = strtolower($source); $this->target = strtolower($target); $this->constraint = $constraint; - $this->description = $description; + $this->description = self::TYPE_DEV_REQUIRE === $description ? 'requires (for development)' : $description; $this->prettyConstraint = $prettyConstraint; } @@ -89,7 +127,7 @@ public function getTarget() } /** - * @return ConstraintInterface|null + * @return ConstraintInterface */ public function getConstraint() { @@ -123,6 +161,6 @@ public function __toString() */ public function getPrettyString(PackageInterface $sourcePackage) { - return $sourcePackage->getPrettyString().' '.$this->description.' '.$this->target.' '.$this->constraint->getPrettyString().''; + return $sourcePackage->getPrettyString().' '.$this->description.' '.$this->target.' '.$this->constraint->getPrettyString(); } } diff --git a/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/EmptyConstraint.php b/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/EmptyConstraint.php deleted file mode 100644 index 33f9e2e82..000000000 --- a/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/EmptyConstraint.php +++ /dev/null @@ -1,24 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Package\LinkConstraint; - -use Composer\Semver\Constraint\EmptyConstraint as SemverEmptyConstraint; - -trigger_error('The ' . __NAMESPACE__ . '\EmptyConstraint class is deprecated, use Composer\Semver\Constraint\EmptyConstraint instead.', E_USER_DEPRECATED); - -/** - * @deprecated use Composer\Semver\Constraint\EmptyConstraint instead - */ -class EmptyConstraint extends SemverEmptyConstraint implements LinkConstraintInterface -{ -} diff --git a/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/LinkConstraintInterface.php b/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/LinkConstraintInterface.php deleted file mode 100644 index b8903ea8f..000000000 --- a/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/LinkConstraintInterface.php +++ /dev/null @@ -1,24 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Package\LinkConstraint; - -use Composer\Semver\Constraint\ConstraintInterface; - -trigger_error('The ' . __NAMESPACE__ . '\LinkConstraintInterface interface is deprecated, use Composer\Semver\Constraint\ConstraintInterface instead.', E_USER_DEPRECATED); - -/** - * @deprecated use Composer\Semver\Constraint\ConstraintInterface instead - */ -interface LinkConstraintInterface extends ConstraintInterface -{ -} diff --git a/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/MultiConstraint.php b/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/MultiConstraint.php deleted file mode 100644 index 10a996568..000000000 --- a/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/MultiConstraint.php +++ /dev/null @@ -1,24 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Package\LinkConstraint; - -use Composer\Semver\Constraint\MultiConstraint as SemverMultiConstraint; - -trigger_error('The ' . __NAMESPACE__ . '\MultiConstraint class is deprecated, use Composer\Semver\Constraint\MultiConstraint instead.', E_USER_DEPRECATED); - -/** - * @deprecated use Composer\Semver\Constraint\MultiConstraint instead - */ -class MultiConstraint extends SemverMultiConstraint implements LinkConstraintInterface -{ -} diff --git a/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/SpecificConstraint.php b/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/SpecificConstraint.php deleted file mode 100644 index 12d3194b6..000000000 --- a/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/SpecificConstraint.php +++ /dev/null @@ -1,24 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Package\LinkConstraint; - -use Composer\Semver\Constraint\AbstractConstraint; - -trigger_error('The ' . __NAMESPACE__ . '\SpecificConstraint abstract class is deprecated, there is no replacement for it.', E_USER_DEPRECATED); - -/** - * @deprecated use Composer\Semver\Constraint\AbstractConstraint instead - */ -abstract class SpecificConstraint extends AbstractConstraint implements LinkConstraintInterface -{ -} diff --git a/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/VersionConstraint.php b/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/VersionConstraint.php deleted file mode 100644 index d6b1cbe1d..000000000 --- a/app/vendor/composer/composer/src/Composer/Package/LinkConstraint/VersionConstraint.php +++ /dev/null @@ -1,24 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Package\LinkConstraint; - -use Composer\Semver\Constraint\Constraint; - -trigger_error('The ' . __NAMESPACE__ . '\VersionConstraint class is deprecated, use Composer\Semver\Constraint\Constraint instead.', E_USER_DEPRECATED); - -/** - * @deprecated use Composer\Semver\Constraint\Constraint instead - */ -class VersionConstraint extends Constraint implements LinkConstraintInterface -{ -} diff --git a/app/vendor/composer/composer/src/Composer/Package/Loader/ArrayLoader.php b/app/vendor/composer/composer/src/Composer/Package/Loader/ArrayLoader.php index b12ee7ffd..4367b9a3a 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Loader/ArrayLoader.php +++ b/app/vendor/composer/composer/src/Composer/Package/Loader/ArrayLoader.php @@ -12,13 +12,18 @@ namespace Composer\Package\Loader; -use Composer\Package; use Composer\Package\AliasPackage; +use Composer\Package\BasePackage; +use Composer\Package\CompleteAliasPackage; +use Composer\Package\CompletePackage; +use Composer\Package\RootPackage; +use Composer\Package\PackageInterface; +use Composer\Package\CompletePackageInterface; use Composer\Package\Link; +use Composer\Package\Package; use Composer\Package\RootAliasPackage; use Composer\Package\RootPackageInterface; use Composer\Package\Version\VersionParser; -use Composer\Semver\VersionParser as SemverVersionParser; /** * @author Konstantin Kudryashiv @@ -29,7 +34,7 @@ class ArrayLoader implements LoaderInterface protected $versionParser; protected $loadOptions; - public function __construct(SemverVersionParser $parser = null, $loadOptions = false) + public function __construct(VersionParser $parser = null, $loadOptions = false) { if (!$parser) { $parser = new VersionParser; @@ -38,7 +43,72 @@ public function __construct(SemverVersionParser $parser = null, $loadOptions = f $this->loadOptions = $loadOptions; } + /** + * @template PackageClass of CompletePackageInterface + * + * @param array $config package data + * @param string $class FQCN to be instantiated + * + * @return CompletePackage|CompleteAliasPackage|RootPackage|RootAliasPackage + * + * @phpstan-param class-string $class + */ public function load(array $config, $class = 'Composer\Package\CompletePackage') + { + if ($class !== 'Composer\Package\CompletePackage' && $class !== 'Composer\Package\RootPackage') { + trigger_error('The $class arg is deprecated, please reach out to Composer maintainers ASAP if you still need this.', E_USER_DEPRECATED); + } + + $package = $this->createObject($config, $class); + + foreach (BasePackage::$supportedLinkTypes as $type => $opts) { + if (isset($config[$type])) { + $method = 'set'.ucfirst($opts['method']); + $package->{$method}( + $this->parseLinks( + $package->getName(), + $package->getPrettyVersion(), + $opts['method'], + $config[$type] + ) + ); + } + } + + $package = $this->configureObject($package, $config); + + return $package; + } + + /** + * @param array $versions + * @return list + */ + public function loadPackages(array $versions) + { + $packages = array(); + $linkCache = array(); + + foreach ($versions as $version) { + $package = $this->createObject($version, 'Composer\Package\CompletePackage'); + + $this->configureCachedLinks($linkCache, $package, $version); + $package = $this->configureObject($package, $version); + + $packages[] = $package; + } + + return $packages; + } + + /** + * @template PackageClass of CompletePackageInterface + * @param array $config package data + * @param string $class FQCN to be instantiated + * @return CompletePackage|RootPackage + * @phpstan-param class-string $class + */ + private function createObject(array $config, $class) { if (!isset($config['name'])) { throw new \UnexpectedValueException('Unknown package has no name defined ('.json_encode($config).').'); @@ -50,17 +120,36 @@ public function load(array $config, $class = 'Composer\Package\CompletePackage') // handle already normalized versions if (isset($config['version_normalized'])) { $version = $config['version_normalized']; + + // handling of existing repos which need to remain composer v1 compatible, in case the version_normalized contained VersionParser::DEFAULT_BRANCH_ALIAS, we renormalize it + if ($version === VersionParser::DEFAULT_BRANCH_ALIAS) { + $version = $this->versionParser->normalize($config['version']); + } } else { $version = $this->versionParser->normalize($config['version']); } - $package = new $class($config['name'], $version, $config['version']); + + return new $class($config['name'], $version, $config['version']); + } + + /** + * @param CompletePackage $package + * @param array $config package data + * @return RootPackage|RootAliasPackage|CompletePackage|CompleteAliasPackage + */ + private function configureObject(PackageInterface $package, array $config) + { + if (!$package instanceof CompletePackage) { + throw new \LogicException('ArrayLoader expects instances of the Composer\Package\CompletePackage class to function correctly'); + } + $package->setType(isset($config['type']) ? strtolower($config['type']) : 'library'); if (isset($config['target-dir'])) { $package->setTargetDir($config['target-dir']); } - if (isset($config['extra']) && is_array($config['extra'])) { + if (isset($config['extra']) && \is_array($config['extra'])) { $package->setExtra($config['extra']); } @@ -78,8 +167,12 @@ public function load(array $config, $class = 'Composer\Package\CompletePackage') $package->setInstallationSource($config['installation-source']); } + if (isset($config['default-branch']) && $config['default-branch'] === true) { + $package->setIsDefaultBranch(true); + } + if (isset($config['source'])) { - if (!isset($config['source']['type']) || !isset($config['source']['url']) || !isset($config['source']['reference'])) { + if (!isset($config['source']['type'], $config['source']['url'], $config['source']['reference'])) { throw new \UnexpectedValueException(sprintf( "Package %s's source key should be specified as {\"type\": ..., \"url\": ..., \"reference\": ...},\n%s given.", $config['name'], @@ -95,8 +188,7 @@ public function load(array $config, $class = 'Composer\Package\CompletePackage') } if (isset($config['dist'])) { - if (!isset($config['dist']['type']) - || !isset($config['dist']['url'])) { + if (!isset($config['dist']['type'], $config['dist']['url'])) { throw new \UnexpectedValueException(sprintf( "Package %s's dist key should be specified as ". "{\"type\": ..., \"url\": ..., \"reference\": ..., \"shasum\": ...},\n%s given.", @@ -113,21 +205,7 @@ public function load(array $config, $class = 'Composer\Package\CompletePackage') } } - foreach (Package\BasePackage::$supportedLinkTypes as $type => $opts) { - if (isset($config[$type])) { - $method = 'set'.ucfirst($opts['method']); - $package->{$method}( - $this->parseLinks( - $package->getName(), - $package->getPrettyVersion(), - $opts['description'], - $config[$type] - ) - ); - } - } - - if (isset($config['suggest']) && is_array($config['suggest'])) { + if (isset($config['suggest']) && \is_array($config['suggest'])) { foreach ($config['suggest'] as $target => $reason) { if ('self.version' === trim($reason)) { $config['suggest'][$target] = $package->getPrettyVersion(); @@ -162,12 +240,15 @@ public function load(array $config, $class = 'Composer\Package\CompletePackage') $package->setNotificationUrl($config['notification-url']); } - if (!empty($config['archive']['exclude'])) { - $package->setArchiveExcludes($config['archive']['exclude']); - } + if ($package instanceof CompletePackageInterface) { + if (!empty($config['archive']['name'])) { + $package->setArchiveName($config['archive']['name']); + } + if (!empty($config['archive']['exclude'])) { + $package->setArchiveExcludes($config['archive']['exclude']); + } - if ($package instanceof Package\CompletePackageInterface) { - if (isset($config['scripts']) && is_array($config['scripts'])) { + if (isset($config['scripts']) && \is_array($config['scripts'])) { foreach ($config['scripts'] as $event => $listeners) { $config['scripts'][$event] = (array) $listeners; } @@ -177,23 +258,23 @@ public function load(array $config, $class = 'Composer\Package\CompletePackage') $package->setScripts($config['scripts']); } - if (!empty($config['description']) && is_string($config['description'])) { + if (!empty($config['description']) && \is_string($config['description'])) { $package->setDescription($config['description']); } - if (!empty($config['homepage']) && is_string($config['homepage'])) { + if (!empty($config['homepage']) && \is_string($config['homepage'])) { $package->setHomepage($config['homepage']); } - if (!empty($config['keywords']) && is_array($config['keywords'])) { + if (!empty($config['keywords']) && \is_array($config['keywords'])) { $package->setKeywords($config['keywords']); } if (!empty($config['license'])) { - $package->setLicense(is_array($config['license']) ? $config['license'] : array($config['license'])); + $package->setLicense(\is_array($config['license']) ? $config['license'] : array($config['license'])); } - if (!empty($config['authors']) && is_array($config['authors'])) { + if (!empty($config['authors']) && \is_array($config['authors'])) { $package->setAuthors($config['authors']); } @@ -201,7 +282,7 @@ public function load(array $config, $class = 'Composer\Package\CompletePackage') $package->setSupport($config['support']); } - if (!empty($config['funding']) && is_array($config['funding'])) { + if (!empty($config['funding']) && \is_array($config['funding'])) { $package->setFunding($config['funding']); } @@ -210,47 +291,97 @@ public function load(array $config, $class = 'Composer\Package\CompletePackage') } } + if ($this->loadOptions && isset($config['transport-options'])) { + $package->setTransportOptions($config['transport-options']); + } + if ($aliasNormalized = $this->getBranchAlias($config)) { - if ($package instanceof RootPackageInterface) { - $package = new RootAliasPackage($package, $aliasNormalized, preg_replace('{(\.9{7})+}', '.x', $aliasNormalized)); - } else { - $package = new AliasPackage($package, $aliasNormalized, preg_replace('{(\.9{7})+}', '.x', $aliasNormalized)); + $prettyAlias = preg_replace('{(\.9{7})+}', '.x', $aliasNormalized); + + if ($package instanceof RootPackage) { + return new RootAliasPackage($package, $aliasNormalized, $prettyAlias); } - } - if ($this->loadOptions && isset($config['transport-options'])) { - $package->setTransportOptions($config['transport-options']); + return new CompleteAliasPackage($package, $aliasNormalized, $prettyAlias); } return $package; } /** - * @param string $source source package name - * @param string $sourceVersion source package version (pretty version ideally) - * @param string $description link description (e.g. requires, replaces, ..) - * @param array $links array of package name => constraint mappings + * @param array $linkCache + * @param PackageInterface $package + * @param array $config + * @return void + */ + private function configureCachedLinks(&$linkCache, $package, array $config) + { + $name = $package->getName(); + $prettyVersion = $package->getPrettyVersion(); + + foreach (BasePackage::$supportedLinkTypes as $type => $opts) { + if (isset($config[$type])) { + $method = 'set'.ucfirst($opts['method']); + + $links = array(); + foreach ($config[$type] as $prettyTarget => $constraint) { + $target = strtolower($prettyTarget); + if ($constraint === 'self.version') { + $links[$target] = $this->createLink($name, $prettyVersion, $opts['method'], $target, $constraint); + } else { + if (!isset($linkCache[$name][$type][$target][$constraint])) { + $linkCache[$name][$type][$target][$constraint] = array($target, $this->createLink($name, $prettyVersion, $opts['method'], $target, $constraint)); + } + + list($target, $link) = $linkCache[$name][$type][$target][$constraint]; + $links[$target] = $link; + } + } + + $package->{$method}($links); + } + } + } + + /** + * @param string $source source package name + * @param string $sourceVersion source package version (pretty version ideally) + * @param Link::TYPE_* $description link description (e.g. requires, replaces, ..) + * @param array $links array of package name => constraint mappings * @return Link[] */ public function parseLinks($source, $sourceVersion, $description, $links) { $res = array(); foreach ($links as $target => $constraint) { - if (!is_string($constraint)) { - throw new \UnexpectedValueException('Link constraint in '.$source.' '.$description.' > '.$target.' should be a string, got '.gettype($constraint) . ' (' . var_export($constraint, true) . ')'); - } - if ('self.version' === $constraint) { - $parsedConstraint = $this->versionParser->parseConstraints($sourceVersion); - } else { - $parsedConstraint = $this->versionParser->parseConstraints($constraint); - } - - $res[strtolower($target)] = new Link($source, $target, $parsedConstraint, $description, $constraint); + $res[strtolower($target)] = $this->createLink($source, $sourceVersion, $description, $target, $constraint); } return $res; } + /** + * @param string $source source package name + * @param string $sourceVersion source package version (pretty version ideally) + * @param Link::TYPE_* $description link description (e.g. requires, replaces, ..) + * @param string $target target package name + * @param string $prettyConstraint constraint string + * @return Link + */ + private function createLink($source, $sourceVersion, $description, $target, $prettyConstraint) + { + if (!\is_string($prettyConstraint)) { + throw new \UnexpectedValueException('Link constraint in '.$source.' '.$description.' > '.$target.' should be a string, got '.\gettype($prettyConstraint) . ' (' . var_export($prettyConstraint, true) . ')'); + } + if ('self.version' === $prettyConstraint) { + $parsedConstraint = $this->versionParser->parseConstraints($sourceVersion); + } else { + $parsedConstraint = $this->versionParser->parseConstraints($prettyConstraint); + } + + return new Link($source, $target, $parsedConstraint, $description, $prettyConstraint); + } + /** * Retrieves a branch alias (dev-master => 1.0.x-dev for example) if it exists * @@ -259,39 +390,52 @@ public function parseLinks($source, $sourceVersion, $description, $links) */ public function getBranchAlias(array $config) { - if (('dev-' !== substr($config['version'], 0, 4) && '-dev' !== substr($config['version'], -4)) - || !isset($config['extra']['branch-alias']) - || !is_array($config['extra']['branch-alias']) - ) { - return; + if (strpos($config['version'], 'dev-') !== 0 && '-dev' !== substr($config['version'], -4)) { + return null; } - foreach ($config['extra']['branch-alias'] as $sourceBranch => $targetBranch) { - // ensure it is an alias to a -dev package - if ('-dev' !== substr($targetBranch, -4)) { - continue; - } + if (isset($config['extra']['branch-alias']) && \is_array($config['extra']['branch-alias'])) { + foreach ($config['extra']['branch-alias'] as $sourceBranch => $targetBranch) { + // ensure it is an alias to a -dev package + if ('-dev' !== substr($targetBranch, -4)) { + continue; + } - // normalize without -dev and ensure it's a numeric branch that is parseable - $validatedTargetBranch = $this->versionParser->normalizeBranch(substr($targetBranch, 0, -4)); - if ('-dev' !== substr($validatedTargetBranch, -4)) { - continue; - } + // normalize without -dev and ensure it's a numeric branch that is parseable + if ($targetBranch === VersionParser::DEFAULT_BRANCH_ALIAS) { + $validatedTargetBranch = VersionParser::DEFAULT_BRANCH_ALIAS; + } else { + $validatedTargetBranch = $this->versionParser->normalizeBranch(substr($targetBranch, 0, -4)); + } + if ('-dev' !== substr($validatedTargetBranch, -4)) { + continue; + } - // ensure that it is the current branch aliasing itself - if (strtolower($config['version']) !== strtolower($sourceBranch)) { - continue; - } + // ensure that it is the current branch aliasing itself + if (strtolower($config['version']) !== strtolower($sourceBranch)) { + continue; + } + + // If using numeric aliases ensure the alias is a valid subversion + if (($sourcePrefix = $this->versionParser->parseNumericAliasPrefix($sourceBranch)) + && ($targetPrefix = $this->versionParser->parseNumericAliasPrefix($targetBranch)) + && (stripos($targetPrefix, $sourcePrefix) !== 0) + ) { + continue; + } - // If using numeric aliases ensure the alias is a valid subversion - if (($sourcePrefix = $this->versionParser->parseNumericAliasPrefix($sourceBranch)) - && ($targetPrefix = $this->versionParser->parseNumericAliasPrefix($targetBranch)) - && (stripos($targetPrefix, $sourcePrefix) !== 0) - ) { - continue; + return $validatedTargetBranch; } + } - return $validatedTargetBranch; + if ( + isset($config['default-branch']) + && $config['default-branch'] === true + && false === $this->versionParser->parseNumericAliasPrefix($config['version']) + ) { + return VersionParser::DEFAULT_BRANCH_ALIAS; } + + return null; } } diff --git a/app/vendor/composer/composer/src/Composer/Package/Loader/JsonLoader.php b/app/vendor/composer/composer/src/Composer/Package/Loader/JsonLoader.php index a693e70c0..66f75c034 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Loader/JsonLoader.php +++ b/app/vendor/composer/composer/src/Composer/Package/Loader/JsonLoader.php @@ -13,6 +13,8 @@ namespace Composer\Package\Loader; use Composer\Json\JsonFile; +use Composer\Package\CompletePackage; +use Composer\Package\CompleteAliasPackage; /** * @author Konstantin Kudryashiv @@ -27,8 +29,8 @@ public function __construct(LoaderInterface $loader) } /** - * @param string|JsonFile $json A filename, json string or JsonFile instance to load the package from - * @return \Composer\Package\PackageInterface + * @param string|JsonFile $json A filename, json string or JsonFile instance to load the package from + * @return CompletePackage|CompleteAliasPackage */ public function load($json) { @@ -38,6 +40,11 @@ public function load($json) $config = JsonFile::parseJson(file_get_contents($json), $json); } elseif (is_string($json)) { $config = JsonFile::parseJson($json); + } else { + throw new \InvalidArgumentException(sprintf( + "JsonLoader: Unknown \$json parameter %s. Please report at https://github.com/composer/composer/issues/new.", + gettype($json) + )); } return $this->loader->load($config); diff --git a/app/vendor/composer/composer/src/Composer/Package/Loader/RootPackageLoader.php b/app/vendor/composer/composer/src/Composer/Package/Loader/RootPackageLoader.php index 32118b113..f2e7ebf86 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Loader/RootPackageLoader.php +++ b/app/vendor/composer/composer/src/Composer/Package/Loader/RootPackageLoader.php @@ -13,13 +13,14 @@ namespace Composer\Package\Loader; use Composer\Package\BasePackage; -use Composer\Package\AliasPackage; use Composer\Config; use Composer\IO\IOInterface; -use Composer\Package\RootPackageInterface; +use Composer\Package\Package; +use Composer\Package\RootAliasPackage; use Composer\Repository\RepositoryFactory; use Composer\Package\Version\VersionGuesser; use Composer\Package\Version\VersionParser; +use Composer\Package\RootPackage; use Composer\Repository\RepositoryManager; use Composer\Util\ProcessExecutor; @@ -47,35 +48,32 @@ class RootPackageLoader extends ArrayLoader */ private $versionGuesser; - /** - * @var IOInterface - */ - private $io; - public function __construct(RepositoryManager $manager, Config $config, VersionParser $parser = null, VersionGuesser $versionGuesser = null, IOInterface $io = null) { parent::__construct($parser); $this->manager = $manager; $this->config = $config; - $this->versionGuesser = $versionGuesser ?: new VersionGuesser($config, new ProcessExecutor(), $this->versionParser); - $this->io = $io; + $this->versionGuesser = $versionGuesser ?: new VersionGuesser($config, new ProcessExecutor($io), $this->versionParser); } /** - * @param array $config package data - * @param string $class FQCN to be instantiated - * @param string $cwd cwd of the root package to be used to guess the version if it is not provided - * @return RootPackageInterface + * @template PackageClass of RootPackage + * @param array $config package data + * @param class-string $class FQCN to be instantiated + * @param string $cwd cwd of the root package to be used to guess the version if it is not provided + * @return RootPackage|RootAliasPackage */ public function load(array $config, $class = 'Composer\Package\RootPackage', $cwd = null) { + if ($class !== 'Composer\Package\RootPackage') { + trigger_error('The $class arg is deprecated, please reach out to Composer maintainers ASAP if you still need this.', E_USER_DEPRECATED); + } + if (!isset($config['name'])) { $config['name'] = '__root__'; - } elseif ($this->io) { - if ($err = ValidatingArrayLoader::hasPackageNamingError($config['name'])) { - $this->io->writeError('Deprecation warning: Your package name '.$err.' Make sure you fix this as Composer 2.0 will error.'); - } + } elseif ($err = ValidatingArrayLoader::hasPackageNamingError($config['name'])) { + throw new \RuntimeException('Your package name '.$err); } $autoVersioned = false; if (!isset($config['version'])) { @@ -112,13 +110,20 @@ public function load(array $config, $class = 'Composer\Package\RootPackage', $cw } } - $realPackage = $package = parent::load($config, $class); - if ($realPackage instanceof AliasPackage) { + /** @var RootPackage|RootAliasPackage $package */ + $package = parent::load($config, $class); + if ($package instanceof RootAliasPackage) { $realPackage = $package->getAliasOf(); + } else { + $realPackage = $package; + } + + if (!$realPackage instanceof RootPackage) { + throw new \LogicException('Expecting a Composer\Package\RootPackage at this point'); } if ($autoVersioned) { - $realPackage->replaceVersion($realPackage->getVersion(), 'No version set (parsed as 1.0.0)'); + $realPackage->replaceVersion($realPackage->getVersion(), RootPackage::DEFAULT_PRETTY_VERSION); } if (isset($config['minimum-stability'])) { @@ -137,8 +142,8 @@ public function load(array $config, $class = 'Composer\Package\RootPackage', $cw $links[$link->getTarget()] = $link->getConstraint()->getPrettyString(); } $aliases = $this->extractAliases($links, $aliases); - $stabilityFlags = $this->extractStabilityFlags($links, $stabilityFlags, $realPackage->getMinimumStability()); - $references = $this->extractReferences($links, $references); + $stabilityFlags = self::extractStabilityFlags($links, $realPackage->getMinimumStability(), $stabilityFlags); + $references = self::extractReferences($links, $references); if (isset($links[$config['name']])) { throw new \RuntimeException(sprintf('Root package \'%s\' cannot require itself in its composer.json' . PHP_EOL . @@ -147,13 +152,11 @@ public function load(array $config, $class = 'Composer\Package\RootPackage', $cw } } - if ($this->io) { - foreach (array_keys(BasePackage::$supportedLinkTypes) as $linkType) { - if (isset($config[$linkType])) { - foreach ($config[$linkType] as $linkName => $constraint) { - if ($err = ValidatingArrayLoader::hasPackageNamingError($linkName, true)) { - $this->io->writeError('Deprecation warning: '.$linkType.'.'.$err.' Make sure you fix this as Composer 2.0 will error.'); - } + foreach (array_keys(BasePackage::$supportedLinkTypes) as $linkType) { + if (isset($config[$linkType])) { + foreach ($config[$linkType] as $linkName => $constraint) { + if ($err = ValidatingArrayLoader::hasPackageNamingError($linkName, true)) { + throw new \RuntimeException($linkType.'.'.$err); } } } @@ -198,7 +201,10 @@ private function extractAliases(array $requires, array $aliases) return $aliases; } - private function extractStabilityFlags(array $requires, array $stabilityFlags, $minimumStability) + /** + * @internal + */ + public static function extractStabilityFlags(array $requires, $minimumStability, array $stabilityFlags) { $stabilities = BasePackage::$stabilities; $minimumStability = $stabilities[$minimumStability]; @@ -251,7 +257,10 @@ private function extractStabilityFlags(array $requires, array $stabilityFlags, $ return $stabilityFlags; } - private function extractReferences(array $requires, array $references) + /** + * @internal + */ + public static function extractReferences(array $requires, array $references) { foreach ($requires as $reqName => $reqVersion) { $reqVersion = preg_replace('{^([^,\s@]+) as .+$}', '$1', $reqVersion); diff --git a/app/vendor/composer/composer/src/Composer/Package/Loader/ValidatingArrayLoader.php b/app/vendor/composer/composer/src/Composer/Package/Loader/ValidatingArrayLoader.php index 8c8c62fab..62fa84943 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Loader/ValidatingArrayLoader.php +++ b/app/vendor/composer/composer/src/Composer/Package/Loader/ValidatingArrayLoader.php @@ -179,8 +179,8 @@ public function load(array $config, $class = 'Composer\Package\CompletePackage') unset($this->config['support']['email']); } - if (isset($this->config['support']['irc']) && !$this->filterUrl($this->config['support']['irc'], array('irc'))) { - $this->warnings[] = 'support.irc : invalid value ('.$this->config['support']['irc'].'), must be a irc:/// URL'; + if (isset($this->config['support']['irc']) && !$this->filterUrl($this->config['support']['irc'], array('irc', 'ircs'))) { + $this->warnings[] = 'support.irc : invalid value ('.$this->config['support']['irc'].'), must be a irc:/// or ircs:// URL'; unset($this->config['support']['irc']); } @@ -221,7 +221,7 @@ public function load(array $config, $class = 'Composer\Package\CompletePackage') } } - $unboundConstraint = new Constraint('=', $this->versionParser->normalize('dev-master')); + $unboundConstraint = new Constraint('=', '10000000-dev'); $stableConstraint = new Constraint('=', '1.0.0'); foreach (array_keys(BasePackage::$supportedLinkTypes) as $linkType) { @@ -249,14 +249,14 @@ public function load(array $config, $class = 'Composer\Package\CompletePackage') ($this->flags & self::CHECK_UNBOUND_CONSTRAINTS) && 'require' === $linkType && $linkConstraint->matches($unboundConstraint) - && !preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $package) + && !PlatformRepository::isPlatformPackage($package) ) { $this->warnings[] = $linkType.'.'.$package.' : unbound version constraints ('.$constraint.') should be avoided'; } elseif ( // check requires for exact constraints ($this->flags & self::CHECK_STRICT_CONSTRAINTS) && 'require' === $linkType - && substr($linkConstraint, 0, 1) === '=' + && strpos($linkConstraint, '=') === 0 && $stableConstraint->versionCompare($stableConstraint, $linkConstraint, '<=') ) { $this->warnings[] = $linkType.'.'.$package.' : exact version constraints ('.$constraint.') should be avoided if the package follows semantic versioning'; @@ -276,7 +276,7 @@ public function load(array $config, $class = 'Composer\Package\CompletePackage') } if ($this->validateString('minimum-stability') && !empty($this->config['minimum-stability'])) { - if (!isset(BasePackage::$stabilities[$this->config['minimum-stability']])) { + if (!isset(BasePackage::$stabilities[strtolower($this->config['minimum-stability'])]) && $this->config['minimum-stability'] !== 'RC') { $this->errors[] = 'minimum-stability : invalid value ('.$this->config['minimum-stability'].'), must be one of '.implode(', ', array_keys(BasePackage::$stabilities)); unset($this->config['minimum-stability']); } @@ -306,8 +306,34 @@ public function load(array $config, $class = 'Composer\Package\CompletePackage') unset($this->config['autoload']['psr-4']); } - // TODO validate dist - // TODO validate source + foreach (array('source', 'dist') as $srcType) { + if ($this->validateArray($srcType) && !empty($this->config[$srcType])) { + if (!isset($this->config[$srcType]['type'])) { + $this->errors[] = $srcType . '.type : must be present'; + } + if (!isset($this->config[$srcType]['url'])) { + $this->errors[] = $srcType . '.url : must be present'; + } + if ($srcType === 'source' && !isset($this->config[$srcType]['reference'])) { + $this->errors[] = $srcType . '.reference : must be present'; + } + if (!is_string($this->config[$srcType]['type'])) { + $this->errors[] = $srcType . '.type : should be a string, '.gettype($this->config[$srcType]['type']).' given'; + } + if (!is_string($this->config[$srcType]['url'])) { + $this->errors[] = $srcType . '.url : should be a string, '.gettype($this->config[$srcType]['url']).' given'; + } + if (isset($this->config[$srcType]['reference']) && !is_string($this->config[$srcType]['reference']) && !is_int($this->config[$srcType]['reference'])) { + $this->errors[] = $srcType . '.reference : should be a string or int, '.gettype($this->config[$srcType]['reference']).' given'; + } + if (isset($this->config[$srcType]['reference']) && preg_match('{^\s*-}', (string) $this->config[$srcType]['reference'])) { + $this->errors[] = $srcType . '.reference : must not start with a "-", "'.$this->config[$srcType]['reference'].'" given'; + } + if (preg_match('{^\s*-}', $this->config[$srcType]['url'])) { + $this->errors[] = $srcType . '.url : must not start with a "-", "'.$this->config[$srcType]['url'].'" given'; + } + } + } // TODO validate repositories // TODO validate package repositories' packages using this recursively @@ -321,6 +347,13 @@ public function load(array $config, $class = 'Composer\Package\CompletePackage') $this->errors[] = 'extra.branch-alias : must be an array of versions => aliases'; } else { foreach ($this->config['extra']['branch-alias'] as $sourceBranch => $targetBranch) { + if (!is_string($targetBranch)) { + $this->warnings[] = 'extra.branch-alias.'.$sourceBranch.' : the target branch ('.json_encode($targetBranch).') must be a string, "'.gettype($targetBranch).'" received.'; + unset($this->config['extra']['branch-alias'][$sourceBranch]); + + continue; + } + // ensure it is an alias to a -dev package if ('-dev' !== substr($targetBranch, -4)) { $this->warnings[] = 'extra.branch-alias.'.$sourceBranch.' : the target branch ('.$targetBranch.') must end in -dev'; @@ -372,7 +405,7 @@ public function getErrors() public static function hasPackageNamingError($name, $isLink = false) { - if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) { + if (PlatformRepository::isPlatformPackage($name)) { return; } diff --git a/app/vendor/composer/composer/src/Composer/Package/Locker.php b/app/vendor/composer/composer/src/Composer/Package/Locker.php index f4e82691d..2ab1e2052 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Locker.php +++ b/app/vendor/composer/composer/src/Composer/Package/Locker.php @@ -14,11 +14,11 @@ use Composer\Json\JsonFile; use Composer\Installer\InstallationManager; -use Composer\Repository\RepositoryManager; +use Composer\Repository\LockArrayRepository; use Composer\Util\ProcessExecutor; -use Composer\Repository\ArrayRepository; use Composer\Package\Dumper\ArrayDumper; use Composer\Package\Loader\ArrayLoader; +use Composer\Package\Version\VersionParser; use Composer\Plugin\PluginInterface; use Composer\Util\Git as GitUtil; use Composer\IO\IOInterface; @@ -32,35 +32,40 @@ */ class Locker { + /** @var JsonFile */ private $lockFile; - private $repositoryManager; + /** @var InstallationManager */ private $installationManager; + /** @var string */ private $hash; + /** @var string */ private $contentHash; + /** @var ArrayLoader */ private $loader; + /** @var ArrayDumper */ private $dumper; + /** @var ProcessExecutor */ private $process; private $lockDataCache; + private $virtualFileWritten; /** * Initializes packages locker. * * @param IOInterface $io * @param JsonFile $lockFile lockfile loader - * @param RepositoryManager $repositoryManager repository manager instance * @param InstallationManager $installationManager installation manager instance * @param string $composerFileContents The contents of the composer file */ - public function __construct(IOInterface $io, JsonFile $lockFile, RepositoryManager $repositoryManager, InstallationManager $installationManager, $composerFileContents) + public function __construct(IOInterface $io, JsonFile $lockFile, InstallationManager $installationManager, $composerFileContents, ProcessExecutor $process = null) { $this->lockFile = $lockFile; - $this->repositoryManager = $repositoryManager; $this->installationManager = $installationManager; $this->hash = md5($composerFileContents); $this->contentHash = self::getContentHash($composerFileContents); $this->loader = new ArrayLoader(null, true); $this->dumper = new ArrayDumper(); - $this->process = new ProcessExecutor($io); + $this->process = $process ?: new ProcessExecutor($io); } /** @@ -109,7 +114,7 @@ public static function getContentHash($composerFileContents) */ public function isLocked() { - if (!$this->lockFile->exists()) { + if (!$this->virtualFileWritten && !$this->lockFile->exists()) { return false; } @@ -146,19 +151,19 @@ public function isFresh() * * @param bool $withDevReqs true to retrieve the locked dev packages * @throws \RuntimeException - * @return \Composer\Repository\RepositoryInterface + * @return \Composer\Repository\LockArrayRepository */ public function getLockedRepository($withDevReqs = false) { $lockData = $this->getLockData(); - $packages = new ArrayRepository(); + $packages = new LockArrayRepository(); $lockedPackages = $lockData['packages']; if ($withDevReqs) { if (isset($lockData['packages-dev'])) { $lockedPackages = array_merge($lockedPackages, $lockData['packages-dev']); } else { - throw new \RuntimeException('The lock file does not contain require-dev information, run install with the --no-dev option or run update to install those packages.'); + throw new \RuntimeException('The lock file does not contain require-dev information, run install with the --no-dev option or delete it and run composer update to generate a new lock file.'); } } @@ -167,14 +172,47 @@ public function getLockedRepository($withDevReqs = false) } if (isset($lockedPackages[0]['name'])) { + $packageByName = array(); foreach ($lockedPackages as $info) { - $packages->addPackage($this->loader->load($info)); + $package = $this->loader->load($info); + $packages->addPackage($package); + $packageByName[$package->getName()] = $package; + + if ($package instanceof AliasPackage) { + $packageByName[$package->getAliasOf()->getName()] = $package->getAliasOf(); + } + } + + if (isset($lockData['aliases'])) { + foreach ($lockData['aliases'] as $alias) { + if (isset($packageByName[$alias['package']])) { + $aliasPkg = new CompleteAliasPackage($packageByName[$alias['package']], $alias['alias_normalized'], $alias['alias']); + $aliasPkg->setRootPackageAlias(true); + $packages->addPackage($aliasPkg); + } + } } return $packages; } - throw new \RuntimeException('Your composer.lock was created before 2012-09-15, and is not supported anymore. Run "composer update" to generate a new one.'); + throw new \RuntimeException('Your composer.lock is invalid. Run "composer update" to generate a new one.'); + } + + /** + * @return string[] Names of dependencies installed through require-dev + */ + public function getDevPackageNames() + { + $names = array(); + $lockData = $this->getLockData(); + if (isset($lockData['packages-dev'])) { + foreach ($lockData['packages-dev'] as $package) { + $names[] = strtolower($package['name']); + } + } + + return $names; } /** @@ -190,18 +228,18 @@ public function getPlatformRequirements($withDevReqs = false) if (!empty($lockData['platform'])) { $requirements = $this->loader->parseLinks( - '__ROOT__', + '__root__', '1.0.0', - 'requires', + Link::TYPE_REQUIRE, isset($lockData['platform']) ? $lockData['platform'] : array() ); } if ($withDevReqs && !empty($lockData['platform-dev'])) { $devRequirements = $this->loader->parseLinks( - '__ROOT__', + '__root__', '1.0.0', - 'requires', + Link::TYPE_REQUIRE, isset($lockData['platform-dev']) ? $lockData['platform-dev'] : array() ); @@ -254,19 +292,7 @@ public function getAliases() { $lockData = $this->getLockData(); - if (!isset($lockData['aliases'])) { - return array(); - } - - // forward compatibility with Composer 2 lock files created - // before https://github.com/composer/composer/issues/9337 was fixed - foreach ($lockData['aliases'] as $index => $alias) { - if (in_array($alias['version'], array('dev-master', 'dev-default', 'dev-trunk'), true)) { - $lockData['aliases'][$index]['version'] = '9999999-dev'; - } - } - - return $lockData['aliases']; + return isset($lockData['aliases']) ? $lockData['aliases'] : array(); } public function getLockData() @@ -295,11 +321,22 @@ public function getLockData() * @param bool $preferStable * @param bool $preferLowest * @param array $platformOverrides + * @param bool $write Whether to actually write data to disk, useful in tests and for --dry-run * * @return bool */ - public function setLockData(array $packages, $devPackages, array $platformReqs, $platformDevReqs, array $aliases, $minimumStability, array $stabilityFlags, $preferStable, $preferLowest, array $platformOverrides) + public function setLockData(array $packages, $devPackages, array $platformReqs, $platformDevReqs, array $aliases, $minimumStability, array $stabilityFlags, $preferStable, $preferLowest, array $platformOverrides, $write = true) { + // keep old default branch names normalized to DEFAULT_BRANCH_ALIAS for BC as that is how Composer 1 outputs the lock file + // when loading the lock file the version is anyway ignored in Composer 2, so it has no adverse effect + $aliases = array_map(function ($alias) { + if (in_array($alias['version'], array('dev-master', 'dev-trunk', 'dev-default'), true)) { + $alias['version'] = VersionParser::DEFAULT_BRANCH_ALIAS; + } + + return $alias; + }, $aliases); + $lock = array( '_readme' => array('This file locks the dependencies of your project to a known state', 'Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies', @@ -307,24 +344,13 @@ public function setLockData(array $packages, $devPackages, array $platformReqs, 'content-hash' => $this->contentHash, 'packages' => null, 'packages-dev' => null, - 'aliases' => array(), + 'aliases' => $aliases, 'minimum-stability' => $minimumStability, 'stability-flags' => $stabilityFlags, 'prefer-stable' => $preferStable, 'prefer-lowest' => $preferLowest, ); - foreach ($aliases as $package => $versions) { - foreach ($versions as $version => $alias) { - $lock['aliases'][] = array( - 'alias' => $alias['alias'], - 'alias_normalized' => $alias['alias_normalized'], - 'version' => $version, - 'package' => $package, - ); - } - } - $lock['packages'] = $this->lockPackages($packages); if (null !== $devPackages) { $lock['packages-dev'] = $this->lockPackages($devPackages); @@ -337,22 +363,20 @@ public function setLockData(array $packages, $devPackages, array $platformReqs, } $lock['plugin-api-version'] = PluginInterface::PLUGIN_API_VERSION; - if (empty($lock['packages']) && empty($lock['packages-dev']) && empty($lock['platform']) && empty($lock['platform-dev'])) { - if ($this->lockFile->exists()) { - unlink($this->lockFile->getPath()); - } - - return false; - } - try { $isLocked = $this->isLocked(); } catch (ParsingException $e) { $isLocked = false; } if (!$isLocked || $lock !== $this->getLockData()) { - $this->lockFile->write($lock); - $this->lockDataCache = null; + if ($write) { + $this->lockFile->write($lock); + $this->lockDataCache = null; + $this->virtualFileWritten = false; + } else { + $this->virtualFileWritten = true; + $this->lockDataCache = JsonFile::parseJson(JsonFile::encode($lock, 448 & JsonFile::JSON_PRETTY_PRINT)); + } return true; } diff --git a/app/vendor/composer/composer/src/Composer/Package/Package.php b/app/vendor/composer/composer/src/Composer/Package/Package.php index 6c7b426e7..2b3ccb1aa 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Package.php +++ b/app/vendor/composer/composer/src/Composer/Package/Package.php @@ -22,42 +22,69 @@ */ class Package extends BasePackage { + /** @var string */ protected $type; + /** @var ?string */ protected $targetDir; + /** @var 'source'|'dist'|null */ protected $installationSource; + /** @var ?string */ protected $sourceType; + /** @var ?string */ protected $sourceUrl; + /** @var ?string */ protected $sourceReference; + /** @var ?array */ protected $sourceMirrors; + /** @var ?string */ protected $distType; + /** @var ?string */ protected $distUrl; + /** @var ?string */ protected $distReference; + /** @var ?string */ protected $distSha1Checksum; + /** @var ?array */ protected $distMirrors; + /** @var string */ protected $version; + /** @var string */ protected $prettyVersion; + /** @var ?\DateTime */ protected $releaseDate; + /** @var mixed[] */ protected $extra = array(); + /** @var string[] */ protected $binaries = array(); + /** @var bool */ protected $dev; + /** @var string */ protected $stability; + /** @var ?string */ protected $notificationUrl; - /** @var Link[] */ + /** @var array */ protected $requires = array(); - /** @var Link[] */ + /** @var array */ protected $conflicts = array(); - /** @var Link[] */ + /** @var array */ protected $provides = array(); - /** @var Link[] */ + /** @var array */ protected $replaces = array(); - /** @var Link[] */ + /** @var array */ protected $devRequires = array(); + /** @var array */ protected $suggests = array(); + /** @var array{psr-0?: array, psr-4?: array, classmap?: list, files?: list} */ protected $autoload = array(); + /** @var array{psr-0?: array, psr-4?: array, classmap?: list, files?: list} */ protected $devAutoload = array(); + /** @var string[] */ protected $includePaths = array(); - protected $archiveExcludes = array(); + /** @var bool */ + protected $isDefaultBranch = false; + /** @var mixed[] */ + protected $transportOptions = array(); /** * Creates a new in memory package. @@ -123,7 +150,7 @@ public function setTargetDir($targetDir) public function getTargetDir() { if (null === $this->targetDir) { - return; + return null; } return ltrim(preg_replace('{ (?:^|[\\\\/]+) \.\.? (?:[\\\\/]+|$) (?:\.\.? (?:[\\\\/]+|$) )*}x', '/', $this->targetDir), '/'); @@ -226,7 +253,7 @@ public function getSourceReference() } /** - * @param array|null $mirrors + * {@inheritDoc} */ public function setSourceMirrors($mirrors) { @@ -314,7 +341,7 @@ public function getDistSha1Checksum() } /** - * @param array|null $mirrors + * {@inheritDoc} */ public function setDistMirrors($mirrors) { @@ -337,6 +364,22 @@ public function getDistUrls() return $this->getUrls($this->distUrl, $this->distMirrors, $this->distReference, $this->distType, 'dist'); } + /** + * {@inheritDoc} + */ + public function getTransportOptions() + { + return $this->transportOptions; + } + + /** + * {@inheritDoc} + */ + public function setTransportOptions(array $options) + { + $this->transportOptions = $options; + } + /** * {@inheritDoc} */ @@ -374,7 +417,7 @@ public function getReleaseDate() /** * Set the required packages * - * @param Link[] $requires A set of package links + * @param array $requires A set of package links */ public function setRequires(array $requires) { @@ -392,7 +435,7 @@ public function getRequires() /** * Set the conflicting packages * - * @param Link[] $conflicts A set of package links + * @param array $conflicts A set of package links */ public function setConflicts(array $conflicts) { @@ -401,6 +444,7 @@ public function setConflicts(array $conflicts) /** * {@inheritDoc} + * @return array */ public function getConflicts() { @@ -410,7 +454,7 @@ public function getConflicts() /** * Set the provided virtual packages * - * @param Link[] $provides A set of package links + * @param array $provides A set of package links */ public function setProvides(array $provides) { @@ -419,6 +463,7 @@ public function setProvides(array $provides) /** * {@inheritDoc} + * @return array */ public function getProvides() { @@ -428,7 +473,7 @@ public function getProvides() /** * Set the packages this one replaces * - * @param Link[] $replaces A set of package links + * @param array $replaces A set of package links */ public function setReplaces(array $replaces) { @@ -437,6 +482,7 @@ public function setReplaces(array $replaces) /** * {@inheritDoc} + * @return array */ public function getReplaces() { @@ -446,7 +492,7 @@ public function getReplaces() /** * Set the recommended packages * - * @param Link[] $devRequires A set of package links + * @param array $devRequires A set of package links */ public function setDevRequires(array $devRequires) { @@ -464,7 +510,7 @@ public function getDevRequires() /** * Set the suggested packages * - * @param array $suggests A set of package names/comments + * @param array $suggests A set of package names/comments */ public function setSuggests(array $suggests) { @@ -552,21 +598,39 @@ public function getNotificationUrl() } /** - * Sets a list of patterns to be excluded from archives - * - * @param array $excludes + * @param bool $defaultBranch */ - public function setArchiveExcludes(array $excludes) + public function setIsDefaultBranch($defaultBranch) { - $this->archiveExcludes = $excludes; + $this->isDefaultBranch = $defaultBranch; } /** * {@inheritDoc} */ - public function getArchiveExcludes() + public function isDefaultBranch() { - return $this->archiveExcludes; + return $this->isDefaultBranch; + } + + /** + * {@inheritDoc} + */ + public function setSourceDistReferences($reference) + { + $this->setSourceReference($reference); + + // only bitbucket, github and gitlab have auto generated dist URLs that easily allow replacing the reference in the dist URL + // TODO generalize this a bit for self-managed/on-prem versions? Some kind of replace token in dist urls which allow this? + if ( + $this->getDistUrl() !== null + && preg_match('{^https?://(?:(?:www\.)?bitbucket\.org|(api\.)?github\.com|(?:www\.)?gitlab\.com)/}i', $this->getDistUrl()) + ) { + $this->setDistReference($reference); + $this->setDistUrl(preg_replace('{(?<=/|sha=)[a-f0-9]{40}(?=/|$)}i', $reference, $this->getDistUrl())); + } elseif ($this->getDistReference()) { // update the dist reference if there was one, but if none was provided ignore it + $this->setDistReference($reference); + } } /** @@ -590,17 +654,24 @@ protected function getUrls($url, $mirrors, $ref, $type, $urlType) if (!$url) { return array(); } + + if ($urlType === 'dist' && false !== strpos($url, '%')) { + $url = ComposerMirror::processUrl($url, $this->name, $this->version, $ref, $type, $this->prettyVersion); + } + $urls = array($url); if ($mirrors) { foreach ($mirrors as $mirror) { if ($urlType === 'dist') { - $mirrorUrl = ComposerMirror::processUrl($mirror['url'], $this->name, $this->version, $ref, $type); + $mirrorUrl = ComposerMirror::processUrl($mirror['url'], $this->name, $this->version, $ref, $type, $this->prettyVersion); } elseif ($urlType === 'source' && $type === 'git') { $mirrorUrl = ComposerMirror::processGitUrl($mirror['url'], $this->name, $url, $type); } elseif ($urlType === 'source' && $type === 'hg') { $mirrorUrl = ComposerMirror::processHgUrl($mirror['url'], $this->name, $url, $type); + } else { + continue; } - if (!in_array($mirrorUrl, $urls)) { + if (!\in_array($mirrorUrl, $urls)) { $func = $mirror['preferred'] ? 'array_unshift' : 'array_push'; $func($urls, $mirrorUrl); } diff --git a/app/vendor/composer/composer/src/Composer/Package/PackageInterface.php b/app/vendor/composer/composer/src/Composer/Package/PackageInterface.php index cb16efa7e..c3df0fd63 100644 --- a/app/vendor/composer/composer/src/Composer/Package/PackageInterface.php +++ b/app/vendor/composer/composer/src/Composer/Package/PackageInterface.php @@ -21,6 +21,10 @@ */ interface PackageInterface { + const DISPLAY_SOURCE_REF_IF_DEV = 0; + const DISPLAY_SOURCE_REF = 1; + const DISPLAY_DIST_REF = 2; + /** * Returns the package's name without version info, thus not a unique identifier * @@ -41,9 +45,11 @@ public function getPrettyName(); * No version or release type information should be included in any of the * names. Provided or replaced package names need to be returned as well. * - * @return array An array of strings referring to this package + * @param bool $provides Whether provided names should be included + * + * @return string[] An array of strings referring to this package */ - public function getNames(); + public function getNames($provides = true); /** * Allows the solver to set an id for this package to refer to it. @@ -76,14 +82,14 @@ public function getType(); /** * Returns the package targetDir property * - * @return string The package targetDir + * @return ?string The package targetDir */ public function getTargetDir(); /** * Returns the package extra data * - * @return array The package extra data + * @return mixed[] The package extra data */ public function getExtra(); @@ -91,93 +97,107 @@ public function getExtra(); * Sets source from which this package was installed (source/dist). * * @param string $type source/dist + * @phpstan-param 'source'|'dist'|null $type */ public function setInstallationSource($type); /** * Returns source from which this package was installed (source/dist). * - * @return string source/dist + * @return ?string source/dist + * @phpstan-return 'source'|'dist'|null */ public function getInstallationSource(); /** * Returns the repository type of this package, e.g. git, svn * - * @return string The repository type + * @return ?string The repository type */ public function getSourceType(); /** * Returns the repository url of this package, e.g. git://github.com/naderman/composer.git * - * @return string The repository url + * @return ?string The repository url */ public function getSourceUrl(); /** * Returns the repository urls of this package including mirrors, e.g. git://github.com/naderman/composer.git * - * @return array + * @return string[] */ public function getSourceUrls(); /** * Returns the repository reference of this package, e.g. master, 1.0.0 or a commit hash for git * - * @return string The repository reference + * @return ?string The repository reference */ public function getSourceReference(); /** * Returns the source mirrors of this package * - * @return array|null + * @return ?array */ public function getSourceMirrors(); + /** + * @param ?array $mirrors + * @return void + */ + public function setSourceMirrors($mirrors); + /** * Returns the type of the distribution archive of this version, e.g. zip, tarball * - * @return string The repository type + * @return ?string The repository type */ public function getDistType(); /** * Returns the url of the distribution archive of this version * - * @return string + * @return ?string */ public function getDistUrl(); /** * Returns the urls of the distribution archive of this version, including mirrors * - * @return array + * @return string[] */ public function getDistUrls(); /** * Returns the reference of the distribution archive of this version, e.g. master, 1.0.0 or a commit hash for git * - * @return string + * @return ?string */ public function getDistReference(); /** * Returns the sha1 checksum for the distribution archive of this version * - * @return string + * @return ?string */ public function getDistSha1Checksum(); /** * Returns the dist mirrors of this package * - * @return array|null + * @return ?array */ public function getDistMirrors(); + /** + * @param ?array $mirrors + * @return void + */ + public function setDistMirrors($mirrors); + /** * Returns the version of this package * @@ -197,15 +217,18 @@ public function getPrettyVersion(); * * @see getPrettyVersion * - * @param bool $truncate If the source reference is a sha1 hash, truncate it + * @param bool $truncate If the source reference is a sha1 hash, truncate it + * @param int $displayMode One of the DISPLAY_ constants on this interface determining display of references * @return string version + * + * @phpstan-param self::DISPLAY_SOURCE_REF_IF_DEV|self::DISPLAY_SOURCE_REF|self::DISPLAY_DIST_REF $displayMode */ - public function getFullPrettyVersion($truncate = true); + public function getFullPrettyVersion($truncate = true, $displayMode = self::DISPLAY_SOURCE_REF_IF_DEV); /** * Returns the release date of the package * - * @return \DateTime + * @return ?\DateTime */ public function getReleaseDate(); @@ -220,7 +243,7 @@ public function getStability(); * Returns a set of links to packages which need to be installed before * this package can be installed * - * @return Link[] An array of package links defining required packages + * @return array An array of package links defining required packages */ public function getRequires(); @@ -252,7 +275,7 @@ public function getReplaces(); * Returns a set of links to packages which are required to develop * this package. These are installed if in dev mode. * - * @return Link[] An array of package links defining packages required for development + * @return array An array of package links defining packages required for development */ public function getDevRequires(); @@ -261,6 +284,7 @@ public function getDevRequires(); * combination with this package. * * @return array An array of package suggestions with descriptions + * @phpstan-return array */ public function getSuggests(); @@ -273,6 +297,7 @@ public function getSuggests(); * directories for autoloading using the type specified. * * @return array Mapping of autoloading rules + * @phpstan-return array{psr-0?: array, psr-4?: array, classmap?: list, files?: list} */ public function getAutoload(); @@ -285,6 +310,7 @@ public function getAutoload(); * directories for autoloading using the type specified. * * @return array Mapping of dev autoloading rules + * @phpstan-return array{psr-0?: array, psr-4?: array, classmap?: list, files?: list} */ public function getDevAutoload(); @@ -292,7 +318,7 @@ public function getDevAutoload(); * Returns a list of directories which should get added to PHP's * include path. * - * @return array + * @return string[] */ public function getIncludePaths(); @@ -306,14 +332,14 @@ public function setRepository(RepositoryInterface $repository); /** * Returns a reference to the repository that owns the package * - * @return RepositoryInterface + * @return ?RepositoryInterface */ public function getRepository(); /** * Returns the package binaries * - * @return array + * @return string[] */ public function getBinaries(); @@ -327,7 +353,7 @@ public function getUniqueName(); /** * Returns the package notification url * - * @return string + * @return ?string */ public function getNotificationUrl(); @@ -346,19 +372,24 @@ public function __toString(); public function getPrettyString(); /** - * Returns a list of patterns to exclude from package archives - * - * @return array + * @return bool */ - public function getArchiveExcludes(); + public function isDefaultBranch(); /** * Returns a list of options to download package dist files * - * @return array + * @return mixed[] */ public function getTransportOptions(); + /** + * Configures the list of options to download package dist files + * + * @return void + */ + public function setTransportOptions(array $options); + /** * @param string $reference * @@ -386,4 +417,13 @@ public function setDistType($type); * @return void */ public function setDistReference($reference); + + /** + * Set dist and source references and update dist URL for ones that contain a reference + * + * @param string $reference + * + * @return void + */ + public function setSourceDistReferences($reference); } diff --git a/app/vendor/composer/composer/src/Composer/Package/RootAliasPackage.php b/app/vendor/composer/composer/src/Composer/Package/RootAliasPackage.php index 862cb21a5..78139eb02 100644 --- a/app/vendor/composer/composer/src/Composer/Package/RootAliasPackage.php +++ b/app/vendor/composer/composer/src/Composer/Package/RootAliasPackage.php @@ -15,13 +15,31 @@ /** * @author Jordi Boggiano */ -class RootAliasPackage extends AliasPackage implements RootPackageInterface +class RootAliasPackage extends CompleteAliasPackage implements RootPackageInterface { - public function __construct(RootPackageInterface $aliasOf, $version, $prettyVersion) + /** @var RootPackage */ + protected $aliasOf; + + /** + * All descendants' constructors should call this parent constructor + * + * @param RootPackage $aliasOf The package this package is an alias of + * @param string $version The version the alias must report + * @param string $prettyVersion The alias's non-normalized version + */ + public function __construct(RootPackage $aliasOf, $version, $prettyVersion) { parent::__construct($aliasOf, $version, $prettyVersion); } + /** + * @return RootPackage + */ + public function getAliasOf() + { + return $this->aliasOf; + } + /** * {@inheritDoc} */ @@ -75,7 +93,7 @@ public function getConfig() */ public function setRequires(array $require) { - $this->requires = $this->replaceSelfVersionDependencies($require, 'requires'); + $this->requires = $this->replaceSelfVersionDependencies($require, Link::TYPE_REQUIRE); $this->aliasOf->setRequires($require); } @@ -85,7 +103,7 @@ public function setRequires(array $require) */ public function setDevRequires(array $devRequire) { - $this->devRequires = $this->replaceSelfVersionDependencies($devRequire, 'devRequires'); + $this->devRequires = $this->replaceSelfVersionDependencies($devRequire, Link::TYPE_DEV_REQUIRE); $this->aliasOf->setDevRequires($devRequire); } @@ -95,7 +113,7 @@ public function setDevRequires(array $devRequire) */ public function setConflicts(array $conflicts) { - $this->conflicts = $this->replaceSelfVersionDependencies($conflicts, 'conflicts'); + $this->conflicts = $this->replaceSelfVersionDependencies($conflicts, Link::TYPE_CONFLICT); $this->aliasOf->setConflicts($conflicts); } @@ -104,7 +122,7 @@ public function setConflicts(array $conflicts) */ public function setProvides(array $provides) { - $this->provides = $this->replaceSelfVersionDependencies($provides, 'provides'); + $this->provides = $this->replaceSelfVersionDependencies($provides, Link::TYPE_PROVIDE); $this->aliasOf->setProvides($provides); } @@ -113,18 +131,10 @@ public function setProvides(array $provides) */ public function setReplaces(array $replaces) { - $this->replaces = $this->replaceSelfVersionDependencies($replaces, 'replaces'); + $this->replaces = $this->replaceSelfVersionDependencies($replaces, Link::TYPE_REPLACE); $this->aliasOf->setReplaces($replaces); } - /** - * {@inheritDoc} - */ - public function setRepositories($repositories) - { - $this->aliasOf->setRepositories($repositories); - } - /** * {@inheritDoc} */ @@ -149,6 +159,46 @@ public function setStabilityFlags(array $stabilityFlags) $this->aliasOf->setStabilityFlags($stabilityFlags); } + /** + * {@inheritDoc} + */ + public function setMinimumStability($minimumStability) + { + $this->aliasOf->setMinimumStability($minimumStability); + } + + /** + * {@inheritDoc} + */ + public function setPreferStable($preferStable) + { + $this->aliasOf->setPreferStable($preferStable); + } + + /** + * {@inheritDoc} + */ + public function setConfig(array $config) + { + $this->aliasOf->setConfig($config); + } + + /** + * {@inheritDoc} + */ + public function setReferences(array $references) + { + $this->aliasOf->setReferences($references); + } + + /** + * {@inheritDoc} + */ + public function setAliases(array $aliases) + { + $this->aliasOf->setAliases($aliases); + } + /** * {@inheritDoc} */ diff --git a/app/vendor/composer/composer/src/Composer/Package/RootPackage.php b/app/vendor/composer/composer/src/Composer/Package/RootPackage.php index 52e851a5c..945bfd17e 100644 --- a/app/vendor/composer/composer/src/Composer/Package/RootPackage.php +++ b/app/vendor/composer/composer/src/Composer/Package/RootPackage.php @@ -19,6 +19,8 @@ */ class RootPackage extends CompletePackage implements RootPackageInterface { + const DEFAULT_PRETTY_VERSION = '1.0.0+no-version-set'; + protected $minimumStability = 'stable'; protected $preferStable = false; protected $stabilityFlags = array(); @@ -27,9 +29,7 @@ class RootPackage extends CompletePackage implements RootPackageInterface protected $aliases = array(); /** - * Set the minimumStability - * - * @param string $minimumStability + * {@inerhitDoc} */ public function setMinimumStability($minimumStability) { @@ -45,9 +45,7 @@ public function getMinimumStability() } /** - * Set the stabilityFlags - * - * @param array $stabilityFlags + * {@inheritDoc} */ public function setStabilityFlags(array $stabilityFlags) { @@ -63,9 +61,7 @@ public function getStabilityFlags() } /** - * Set the preferStable - * - * @param bool $preferStable + * {@inerhitDoc} */ public function setPreferStable($preferStable) { @@ -81,9 +77,7 @@ public function getPreferStable() } /** - * Set the config - * - * @param array $config + * {@inerhitDoc} */ public function setConfig(array $config) { @@ -99,9 +93,7 @@ public function getConfig() } /** - * Set the references - * - * @param array $references + * {@inerhitDoc} */ public function setReferences(array $references) { @@ -117,9 +109,7 @@ public function getReferences() } /** - * Set the aliases - * - * @param array $aliases + * {@inerhitDoc} */ public function setAliases(array $aliases) { diff --git a/app/vendor/composer/composer/src/Composer/Package/RootPackageInterface.php b/app/vendor/composer/composer/src/Composer/Package/RootPackageInterface.php index 7a3be9d8e..ecd72e444 100644 --- a/app/vendor/composer/composer/src/Composer/Package/RootPackageInterface.php +++ b/app/vendor/composer/composer/src/Composer/Package/RootPackageInterface.php @@ -105,7 +105,7 @@ public function setReplaces(array $replaces); * * @param array $repositories */ - public function setRepositories($repositories); + public function setRepositories(array $repositories); /** * Set the autoload mapping @@ -128,6 +128,41 @@ public function setDevAutoload(array $devAutoload); */ public function setStabilityFlags(array $stabilityFlags); + /** + * Set the minimumStability + * + * @param string $minimumStability + */ + public function setMinimumStability($minimumStability); + + /** + * Set the preferStable + * + * @param bool $preferStable + */ + public function setPreferStable($preferStable); + + /** + * Set the config + * + * @param array $config + */ + public function setConfig(array $config); + + /** + * Set the references + * + * @param array $references + */ + public function setReferences(array $references); + + /** + * Set the aliases + * + * @param array $aliases + */ + public function setAliases(array $aliases); + /** * Set the suggested packages * diff --git a/app/vendor/composer/composer/src/Composer/Package/Version/StabilityFilter.php b/app/vendor/composer/composer/src/Composer/Package/Version/StabilityFilter.php new file mode 100644 index 000000000..8eda2033b --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Package/Version/StabilityFilter.php @@ -0,0 +1,49 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Version; + +use Composer\Package\BasePackage; + +/** + * @author Jordi Boggiano + */ +class StabilityFilter +{ + /** + * Checks if any of the provided package names in the given stability match the configured acceptable stability and flags + * + * @param int[] $acceptableStabilities array of stability => BasePackage::STABILITY_* value + * @phpstan-param array $acceptableStabilities + * @param int[] $stabilityFlags an array of package name => BasePackage::STABILITY_* value + * @phpstan-param array $stabilityFlags + * @param string[] $names The package name(s) to check for stability flags + * @param string $stability one of 'stable', 'RC', 'beta', 'alpha' or 'dev' + * @return bool true if any package name is acceptable + */ + public static function isPackageAcceptable(array $acceptableStabilities, array $stabilityFlags, array $names, $stability) + { + foreach ($names as $name) { + // allow if package matches the package-specific stability flag + if (isset($stabilityFlags[$name])) { + if (BasePackage::$stabilities[$stability] <= $stabilityFlags[$name]) { + return true; + } + } elseif (isset($acceptableStabilities[$stability])) { + // allow if package matches the global stability requirement and has no exception + return true; + } + } + + return false; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Package/Version/VersionGuesser.php b/app/vendor/composer/composer/src/Composer/Package/Version/VersionGuesser.php index a71c915ad..ef8a057bb 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Version/VersionGuesser.php +++ b/app/vendor/composer/composer/src/Composer/Package/Version/VersionGuesser.php @@ -17,6 +17,7 @@ use Composer\IO\NullIO; use Composer\Semver\VersionParser as SemverVersionParser; use Composer\Util\Git as GitUtil; +use Composer\Util\HttpDownloader; use Composer\Util\ProcessExecutor; use Composer\Util\Svn as SvnUtil; @@ -77,7 +78,7 @@ public function guessVersion(array $packageConfig, $path) return $this->postprocess($versionData); } - $versionData = $this->guessFossilVersion($packageConfig, $path); + $versionData = $this->guessFossilVersion($path); if (null !== $versionData && null !== $versionData['version']) { return $this->postprocess($versionData); } @@ -92,7 +93,7 @@ public function guessVersion(array $packageConfig, $path) private function postprocess(array $versionData) { - if (!empty($versionData['feature_version']) && $versionData['feature_version'] === $versionData['version'] && $versionData['feature_pretty_version'] === $versionData['feature_pretty_version']) { + if (!empty($versionData['feature_version']) && $versionData['feature_version'] === $versionData['version'] && $versionData['feature_pretty_version'] === $versionData['pretty_version']) { unset($versionData['feature_version'], $versionData['feature_pretty_version']); } @@ -125,7 +126,11 @@ private function guessGitVersion(array $packageConfig, $path) // find current branch and collect all branch names foreach ($this->process->splitLines($output) as $branch) { if ($branch && preg_match('{^(?:\* ) *(\(no branch\)|\(detached from \S+\)|\(HEAD detached at \S+\)|\S+) *([a-f0-9]+) .*$}', $branch, $match)) { - if ($match[1] === '(no branch)' || substr($match[1], 0, 10) === '(detached ' || substr($match[1], 0, 17) === '(HEAD detached at') { + if ( + $match[1] === '(no branch)' + || strpos($match[1], '(detached ') === 0 + || strpos($match[1], '(HEAD detached at') === 0 + ) { $version = 'dev-' . $match[2]; $prettyVersion = $version; $isFeatureBranch = true; @@ -133,7 +138,7 @@ private function guessGitVersion(array $packageConfig, $path) } else { $version = $this->versionParser->normalizeBranch($match[1]); $prettyVersion = 'dev-' . $match[1]; - $isFeatureBranch = 0 === strpos($version, 'dev-'); + $isFeatureBranch = $this->isFeatureBranch($packageConfig, $match[1]); } if ($match[2]) { @@ -151,6 +156,7 @@ private function guessGitVersion(array $packageConfig, $path) if ($isFeatureBranch) { $featureVersion = $version; $featurePrettyVersion = $prettyVersion; + // try to find the best (nearest) version branch to assume this feature's version $result = $this->guessFeatureVersion($packageConfig, $version, $branches, 'git rev-list %candidate%..%branch%', $path); $version = $result['version']; @@ -205,7 +211,7 @@ private function guessHgVersion(array $packageConfig, $path) $version = $this->versionParser->normalizeBranch($branch); $isFeatureBranch = 0 === strpos($version, 'dev-'); - if ('9999999-dev' === $version) { + if (VersionParser::DEFAULT_BRANCH_ALIAS === $version) { return array('version' => $version, 'commit' => null, 'pretty_version' => 'dev-'.$branch); } @@ -214,7 +220,8 @@ private function guessHgVersion(array $packageConfig, $path) } // re-use the HgDriver to fetch branches (this properly includes bookmarks) - $driver = new HgDriver(array('url' => $path), new NullIO(), $this->config, $this->process); + $io = new NullIO(); + $driver = new HgDriver(array('url' => $path), $io, $this->config, new HttpDownloader($io, $this->config), $this->process); $branches = array_keys($driver->getBranches()); // try to find the best (nearest) version branch to assume this feature's version @@ -233,19 +240,14 @@ private function guessFeatureVersion(array $packageConfig, $version, array $bran // ignore feature branches if they have no branch-alias or self.version is used // and find the branch they came from to use as a version instead - if ((isset($packageConfig['extra']['branch-alias']) && !isset($packageConfig['extra']['branch-alias'][$version])) + if (!isset($packageConfig['extra']['branch-alias'][$version]) || strpos(json_encode($packageConfig), '"self.version"') ) { $branch = preg_replace('{^dev-}', '', $version); $length = PHP_INT_MAX; - $nonFeatureBranches = ''; - if (!empty($packageConfig['non-feature-branches'])) { - $nonFeatureBranches = implode('|', $packageConfig['non-feature-branches']); - } - // return directly, if branch is configured to be non-feature branch - if (preg_match('{^(' . $nonFeatureBranches . ')$}', $branch) && in_array($branch, $branches, true)) { + if (!$this->isFeatureBranch($packageConfig, $branch)) { return array('version' => $version, 'pretty_version' => $prettyVersion); } @@ -267,7 +269,7 @@ private function guessFeatureVersion(array $packageConfig, $version, array $bran $candidateVersion = preg_replace('{^remotes/\S+/}', '', $candidate); // do not compare against itself or other feature branches - if ($candidate === $branch || !preg_match('{^(' . $nonFeatureBranches . '|master|trunk|default|develop|\d+\..+)$}', $candidateVersion, $match)) { + if ($candidate === $branch || $this->isFeatureBranch($packageConfig, $candidateVersion)) { continue; } @@ -290,7 +292,17 @@ private function guessFeatureVersion(array $packageConfig, $version, array $bran return array('version' => $version, 'pretty_version' => $prettyVersion); } - private function guessFossilVersion(array $packageConfig, $path) + private function isFeatureBranch(array $packageConfig, $branchName) + { + $nonFeatureBranches = ''; + if (!empty($packageConfig['non-feature-branches'])) { + $nonFeatureBranches = implode('|', $packageConfig['non-feature-branches']); + } + + return !preg_match('{^(' . $nonFeatureBranches . '|master|main|latest|next|current|support|tip|trunk|default|develop|\d+\..+)$}', $branchName, $match); + } + + private function guessFossilVersion($path) { $version = null; $prettyVersion = null; @@ -336,7 +348,11 @@ private function guessSvnVersion(array $packageConfig, $path) } $prettyVersion = trim($matches[1]); - $version = $this->versionParser->normalize($prettyVersion); + if ($prettyVersion === 'trunk') { + $version = 'dev-trunk'; + } else { + $version = $this->versionParser->normalize($prettyVersion); + } return array('version' => $version, 'commit' => '', 'pretty_version' => $prettyVersion); } diff --git a/app/vendor/composer/composer/src/Composer/Package/Version/VersionParser.php b/app/vendor/composer/composer/src/Composer/Package/Version/VersionParser.php index 831c61d5f..df3e9feb2 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Version/VersionParser.php +++ b/app/vendor/composer/composer/src/Composer/Package/Version/VersionParser.php @@ -18,6 +18,8 @@ class VersionParser extends SemverVersionParser { + const DEFAULT_BRANCH_ALIAS = '9999999-dev'; + private static $constraints = array(); /** @@ -49,7 +51,7 @@ public function parseNameVersionPairs(array $pairs) for ($i = 0, $count = count($pairs); $i < $count; $i++) { $pair = preg_replace('{^([^=: ]+)[=: ](.*)$}', '$1 $2', trim($pairs[$i])); - if (false === strpos($pair, ' ') && isset($pairs[$i + 1]) && false === strpos($pairs[$i + 1], '/') && !preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $pairs[$i + 1])) { + if (false === strpos($pair, ' ') && isset($pairs[$i + 1]) && false === strpos($pairs[$i + 1], '/') && !preg_match('{(?<=[a-z0-9_/-])\*|\*(?=[a-z0-9_/-])}i', $pairs[$i + 1]) && !PlatformRepository::isPlatformPackage($pairs[$i + 1])) { $pair .= ' '.$pairs[$i + 1]; $i++; } @@ -70,7 +72,18 @@ public function parseNameVersionPairs(array $pairs) */ public static function isUpgrade($normalizedFrom, $normalizedTo) { - if (substr($normalizedFrom, 0, 4) === 'dev-' || substr($normalizedTo, 0, 4) === 'dev-') { + if ($normalizedFrom === $normalizedTo) { + return true; + } + + if (in_array($normalizedFrom, array('dev-master', 'dev-trunk', 'dev-default'), true)) { + $normalizedFrom = VersionParser::DEFAULT_BRANCH_ALIAS; + } + if (in_array($normalizedTo, array('dev-master', 'dev-trunk', 'dev-default'), true)) { + $normalizedTo = VersionParser::DEFAULT_BRANCH_ALIAS; + } + + if (strpos($normalizedFrom, 'dev-') === 0 || strpos($normalizedTo, 'dev-') === 0) { return true; } diff --git a/app/vendor/composer/composer/src/Composer/Package/Version/VersionSelector.php b/app/vendor/composer/composer/src/Composer/Package/Version/VersionSelector.php index 200f56e0f..ab5355d1a 100644 --- a/app/vendor/composer/composer/src/Composer/Package/Version/VersionSelector.php +++ b/app/vendor/composer/composer/src/Composer/Package/Version/VersionSelector.php @@ -12,13 +12,14 @@ namespace Composer\Package\Version; -use Composer\DependencyResolver\Pool; use Composer\Package\BasePackage; +use Composer\Package\AliasPackage; use Composer\Package\PackageInterface; -use Composer\Plugin\PluginInterface; use Composer\Composer; use Composer\Package\Loader\ArrayLoader; use Composer\Package\Dumper\ArrayDumper; +use Composer\Repository\RepositorySet; +use Composer\Repository\PlatformRepository; use Composer\Semver\Constraint\Constraint; /** @@ -29,40 +30,64 @@ */ class VersionSelector { - private $pool; + private $repositorySet; + + private $platformConstraints = array(); private $parser; - public function __construct(Pool $pool) + /** + * @param PlatformRepository $platformRepo If passed in, the versions found will be filtered against their requirements to eliminate any not matching the current platform packages + */ + public function __construct(RepositorySet $repositorySet, PlatformRepository $platformRepo = null) { - $this->pool = $pool; + $this->repositorySet = $repositorySet; + if ($platformRepo) { + foreach ($platformRepo->getPackages() as $package) { + $this->platformConstraints[$package->getName()][] = new Constraint('==', $package->getVersion()); + } + } } /** * Given a package name and optional version, returns the latest PackageInterface * that matches. * - * @param string $packageName - * @param string $targetPackageVersion - * @param string $targetPhpVersion - * @param string $preferredStability - * @return PackageInterface|bool + * @param string $packageName + * @param string $targetPackageVersion + * @param string $preferredStability + * @param bool|array $ignorePlatformReqs + * @return PackageInterface|false */ - public function findBestCandidate($packageName, $targetPackageVersion = null, $targetPhpVersion = null, $preferredStability = 'stable') + public function findBestCandidate($packageName, $targetPackageVersion = null, $preferredStability = 'stable', $ignorePlatformReqs = false, $repoSetFlags = 0) { + if (!isset(BasePackage::$stabilities[$preferredStability])) { + // If you get this, maybe you are still relying on the Composer 1.x signature where the 3rd arg was the php version + throw new \UnexpectedValueException('Expected a valid stability name as 3rd argument, got '.$preferredStability); + } + $constraint = $targetPackageVersion ? $this->getParser()->parseConstraints($targetPackageVersion) : null; - $candidates = $this->pool->whatProvides(strtolower($packageName), $constraint, true); + $candidates = $this->repositorySet->findPackages(strtolower($packageName), $constraint, $repoSetFlags); - if ($targetPhpVersion) { - $phpConstraint = new Constraint('==', $this->getParser()->normalize($targetPhpVersion)); - $composerRuntimeConstraint = new Constraint('==', $this->getParser()->normalize(Composer::RUNTIME_API_VERSION)); - $composerPluginConstraint = new Constraint('==', $this->getParser()->normalize(PluginInterface::PLUGIN_API_VERSION)); - $candidates = array_filter($candidates, function ($pkg) use ($phpConstraint, $composerPluginConstraint, $composerRuntimeConstraint) { + if ($this->platformConstraints && true !== $ignorePlatformReqs) { + $platformConstraints = $this->platformConstraints; + $ignorePlatformReqs = $ignorePlatformReqs ?: array(); + $candidates = array_filter($candidates, function ($pkg) use ($platformConstraints, $ignorePlatformReqs) { $reqs = $pkg->getRequires(); - return (!isset($reqs['php']) || $reqs['php']->getConstraint()->matches($phpConstraint)) - && (!isset($reqs['composer-plugin-api']) || $reqs['composer-plugin-api']->getConstraint()->matches($composerPluginConstraint)) - && (!isset($reqs['composer-runtime-api']) || $reqs['composer-runtime-api']->getConstraint()->matches($composerRuntimeConstraint)); + foreach ($reqs as $name => $link) { + if (!in_array($name, $ignorePlatformReqs, true) && isset($platformConstraints[$name])) { + foreach ($platformConstraints[$name] as $constraint) { + if ($link->getConstraint()->matches($constraint)) { + continue 2; + } + } + + return false; + } + } + + return true; }); } @@ -103,11 +128,16 @@ public function findBestCandidate($packageName, $targetPackageVersion = null, $t } } + // if we end up with 9999999-dev as selected package, make sure we use the original version instead of the alias + if ($package instanceof AliasPackage && $package->getVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) { + $package = $package->getAliasOf(); + } + return $package; } /** - * Given a concrete version, this returns a ~ constraint (when possible) + * Given a concrete version, this returns a ^ constraint (when possible) * that should be used, for example, in composer.json. * * For example: @@ -123,6 +153,16 @@ public function findBestCandidate($packageName, $targetPackageVersion = null, $t */ public function findRecommendedRequireVersion(PackageInterface $package) { + // Extensions which are versioned in sync with PHP should rather be required as "*" to simplify + // the requires and have only one required version to change when bumping the php requirement + if (0 === strpos($package->getName(), 'ext-')) { + $phpVersion = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION; + $extVersion = implode('.', array_slice(explode('.', $package->getVersion()), 0, 3)); + if ($phpVersion === $extVersion) { + return '*'; + } + } + $version = $package->getVersion(); if (!$package->isDev()) { return $this->transformVersion($version, $package->getPrettyVersion(), $package->getStability()); @@ -131,7 +171,7 @@ public function findRecommendedRequireVersion(PackageInterface $package) $loader = new ArrayLoader($this->getParser()); $dumper = new ArrayDumper(); $extra = $loader->getBranchAlias($dumper->dump($package)); - if ($extra) { + if ($extra && $extra !== VersionParser::DEFAULT_BRANCH_ALIAS) { $extra = preg_replace('{^(\d+\.\d+\.\d+)(\.9999999)-dev$}', '$1.0', $extra, -1, $count); if ($count) { $extra = str_replace('.9999999', '.0', $extra); diff --git a/app/vendor/composer/composer/src/Composer/Platform/HhvmDetector.php b/app/vendor/composer/composer/src/Composer/Platform/HhvmDetector.php new file mode 100644 index 000000000..7bf1beb2c --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Platform/HhvmDetector.php @@ -0,0 +1,65 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Platform; + +use Composer\Util\Platform; +use Composer\Util\ProcessExecutor; +use Symfony\Component\Process\ExecutableFinder; + +class HhvmDetector +{ + /** @var string|false|null */ + private static $hhvmVersion = null; + /** @var ?ExecutableFinder */ + private $executableFinder; + /** @var ?ProcessExecutor */ + private $processExecutor; + + public function __construct(ExecutableFinder $executableFinder = null, ProcessExecutor $processExecutor = null) + { + $this->executableFinder = $executableFinder; + $this->processExecutor = $processExecutor; + } + + public function reset() + { + self::$hhvmVersion = null; + } + + public function getVersion() + { + if (null !== self::$hhvmVersion) { + return self::$hhvmVersion ?: null; + } + + self::$hhvmVersion = defined('HHVM_VERSION') ? HHVM_VERSION : null; + if (self::$hhvmVersion === null && !Platform::isWindows()) { + self::$hhvmVersion = false; + $this->executableFinder = $this->executableFinder ?: new ExecutableFinder(); + $hhvmPath = $this->executableFinder->find('hhvm'); + if ($hhvmPath !== null) { + $this->processExecutor = $this->processExecutor ?: new ProcessExecutor(); + $exitCode = $this->processExecutor->execute( + ProcessExecutor::escape($hhvmPath). + ' --php -d hhvm.jit=0 -r "echo HHVM_VERSION;" 2>/dev/null', + self::$hhvmVersion + ); + if ($exitCode !== 0) { + self::$hhvmVersion = false; + } + } + } + + return self::$hhvmVersion; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Platform/Runtime.php b/app/vendor/composer/composer/src/Composer/Platform/Runtime.php new file mode 100644 index 000000000..4e9caff57 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Platform/Runtime.php @@ -0,0 +1,109 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Platform; + +class Runtime +{ + /** + * @param string $constant + * @param class-string $class + * @return bool + */ + public function hasConstant($constant, $class = null) + { + return defined(ltrim($class.'::'.$constant, ':')); + } + + /** + * @param string $constant + * @param class-string $class + * @return mixed + */ + public function getConstant($constant, $class = null) + { + return constant(ltrim($class.'::'.$constant, ':')); + } + + /** + * @param string $fn + * @return bool + */ + public function hasFunction($fn) + { + return function_exists($fn); + } + + /** + * @param callable $callable + * @param array $arguments + * @return mixed + */ + public function invoke($callable, array $arguments = array()) + { + return call_user_func_array($callable, $arguments); + } + + /** + * @param class-string $class + * @return bool + */ + public function hasClass($class) + { + return class_exists($class, false); + } + + /** + * @param class-string $class + * @param array $arguments + * @return object + */ + public function construct($class, array $arguments = array()) + { + if (empty($arguments)) { + return new $class; + } + + $refl = new \ReflectionClass($class); + + return $refl->newInstanceArgs($arguments); + } + + /** @return string[] */ + public function getExtensions() + { + return get_loaded_extensions(); + } + + /** + * @param string $extension + * @return string + */ + public function getExtensionVersion($extension) + { + return phpversion($extension); + } + + /** + * @param string $extension + * @return string + */ + public function getExtensionInfo($extension) + { + $reflector = new \ReflectionExtension($extension); + + ob_start(); + $reflector->info(); + + return ob_get_clean(); + } +} diff --git a/app/vendor/composer/composer/src/Composer/Platform/Version.php b/app/vendor/composer/composer/src/Composer/Platform/Version.php new file mode 100644 index 000000000..a55266c86 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Platform/Version.php @@ -0,0 +1,104 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Platform; + +/** + * @author Lars Strojny + */ +class Version +{ + /** + * @param string $opensslVersion + * @param bool $isFips + * @return string|null + */ + public static function parseOpenssl($opensslVersion, &$isFips) + { + $isFips = false; + + if (!preg_match('/^(?[0-9.]+)(?[a-z]{0,2})?(?(?:-?(?:dev|pre|alpha|beta|rc|fips)[\d]*)*)?(?-\w+)?$/', $opensslVersion, $matches)) { + return null; + } + + $isFips = strpos($matches['suffix'], 'fips') !== false; + $suffix = strtr('-'.ltrim($matches['suffix'], '-'), array('-fips' => '', '-pre' => '-alpha')); + $patch = self::convertAlphaVersionToIntVersion($matches['patch']); + + return rtrim($matches['version'].'.'.$patch.$suffix, '-'); + } + + /** + * @param string $libjpegVersion + * @return string|null + */ + public static function parseLibjpeg($libjpegVersion) + { + if (!preg_match('/^(?\d+)(?[a-z]*)$/', $libjpegVersion, $matches)) { + return null; + } + + return $matches['major'].'.'.self::convertAlphaVersionToIntVersion($matches['minor']); + } + + /** + * @param string $zoneinfoVersion + * @return string|null + */ + public static function parseZoneinfoVersion($zoneinfoVersion) + { + if (!preg_match('/^(?\d{4})(?[a-z]*)$/', $zoneinfoVersion, $matches)) { + return null; + } + + return $matches['year'].'.'.self::convertAlphaVersionToIntVersion($matches['revision']); + } + + /** + * "" => 0, "a" => 1, "zg" => 33 + * + * @param string $alpha + * @return int + */ + private static function convertAlphaVersionToIntVersion($alpha) + { + return strlen($alpha) * (-ord('a') + 1) + array_sum(array_map('ord', str_split($alpha))); + } + + /** + * @param int $versionId + * @return string + */ + public static function convertLibxpmVersionId($versionId) + { + return self::convertVersionId($versionId, 100); + } + + /** + * @param int $versionId + * @return string + */ + public static function convertOpenldapVersionId($versionId) + { + return self::convertVersionId($versionId, 100); + } + + private static function convertVersionId($versionId, $base) + { + return sprintf( + '%d.%d.%d', + $versionId / ($base * $base), + (int) ($versionId / $base) % $base, + $versionId % $base + ); + } +} diff --git a/app/vendor/composer/composer/src/Composer/Plugin/PluginEvents.php b/app/vendor/composer/composer/src/Composer/Plugin/PluginEvents.php index 1fb368baf..ff476ba40 100644 --- a/app/vendor/composer/composer/src/Composer/Plugin/PluginEvents.php +++ b/app/vendor/composer/composer/src/Composer/Plugin/PluginEvents.php @@ -49,6 +49,16 @@ class PluginEvents */ const PRE_FILE_DOWNLOAD = 'pre-file-download'; + /** + * The POST_FILE_DOWNLOAD event occurs after downloading a package dist file + * + * The event listener method receives a + * Composer\Plugin\PostFileDownloadEvent instance. + * + * @var string + */ + const POST_FILE_DOWNLOAD = 'post-file-download'; + /** * The PRE_COMMAND_RUN event occurs before a command is executed and lets you modify the input arguments/options * @@ -58,4 +68,15 @@ class PluginEvents * @var string */ const PRE_COMMAND_RUN = 'pre-command-run'; + + /** + * The PRE_POOL_CREATE event occurs before the Pool of packages is created, and lets + * you filter the list of packages which is going to enter the Solver + * + * The event listener method receives a + * Composer\Plugin\PrePoolCreateEvent instance. + * + * @var string + */ + const PRE_POOL_CREATE = 'pre-pool-create'; } diff --git a/app/vendor/composer/composer/src/Composer/Plugin/PluginInterface.php b/app/vendor/composer/composer/src/Composer/Plugin/PluginInterface.php index 6eaca4e90..305a784bc 100644 --- a/app/vendor/composer/composer/src/Composer/Plugin/PluginInterface.php +++ b/app/vendor/composer/composer/src/Composer/Plugin/PluginInterface.php @@ -25,9 +25,14 @@ interface PluginInterface /** * Version number of the internal composer-plugin-api package * + * This is used to denote the API version of Plugin specific + * features, but is also bumped to a new major if Composer + * includes a major break in internal APIs which are susceptible + * to be used by plugins. + * * @var string */ - const PLUGIN_API_VERSION = '1.1.0'; + const PLUGIN_API_VERSION = '2.1.0'; /** * Apply plugin modifications to Composer @@ -36,4 +41,26 @@ interface PluginInterface * @param IOInterface $io */ public function activate(Composer $composer, IOInterface $io); + + /** + * Remove any hooks from Composer + * + * This will be called when a plugin is deactivated before being + * uninstalled, but also before it gets upgraded to a new version + * so the old one can be deactivated and the new one activated. + * + * @param Composer $composer + * @param IOInterface $io + */ + public function deactivate(Composer $composer, IOInterface $io); + + /** + * Prepare the plugin to be uninstalled + * + * This will be called after deactivate. + * + * @param Composer $composer + * @param IOInterface $io + */ + public function uninstall(Composer $composer, IOInterface $io); } diff --git a/app/vendor/composer/composer/src/Composer/Plugin/PluginManager.php b/app/vendor/composer/composer/src/Composer/Plugin/PluginManager.php index bb9b66d83..eb13aad0e 100644 --- a/app/vendor/composer/composer/src/Composer/Plugin/PluginManager.php +++ b/app/vendor/composer/composer/src/Composer/Plugin/PluginManager.php @@ -19,10 +19,11 @@ use Composer\Package\Package; use Composer\Package\Version\VersionParser; use Composer\Repository\RepositoryInterface; +use Composer\Repository\InstalledRepository; +use Composer\Repository\RootPackageRepository; use Composer\Package\PackageInterface; use Composer\Package\Link; use Composer\Semver\Constraint\Constraint; -use Composer\DependencyResolver\Pool; use Composer\Plugin\Capability\Capability; use Composer\Util\PackageSorter; @@ -34,15 +35,23 @@ */ class PluginManager { + /** @var Composer */ protected $composer; + /** @var IOInterface */ protected $io; + /** @var ?Composer */ protected $globalComposer; + /** @var VersionParser */ protected $versionParser; + /** @var bool */ protected $disablePlugins = false; + /** @var array */ protected $plugins = array(); + /** @var array */ protected $registeredPlugins = array(); + /** @var int */ private static $classCounter = 0; /** @@ -73,11 +82,9 @@ public function loadInstalledPlugins() $repo = $this->composer->getRepositoryManager()->getLocalRepository(); $globalRepo = $this->globalComposer ? $this->globalComposer->getRepositoryManager()->getLocalRepository() : null; - if ($repo) { - $this->loadRepository($repo); - } + $this->loadRepository($repo, false); if ($globalRepo) { - $this->loadRepository($globalRepo); + $this->loadRepository($globalRepo, true); } } @@ -109,10 +116,11 @@ public function getGlobalComposer() * * @param PackageInterface $package * @param bool $failOnMissingClasses By default this silently skips plugins that can not be found, but if set to true it fails with an exception + * @param bool $isGlobalPlugin Set to true to denote plugins which are installed in the global Composer directory * * @throws \UnexpectedValueException */ - public function registerPackage(PackageInterface $package, $failOnMissingClasses = false) + public function registerPackage(PackageInterface $package, $failOnMissingClasses = false, $isGlobalPlugin = false) { if ($this->disablePlugins) { return; @@ -134,10 +142,16 @@ public function registerPackage(PackageInterface $package, $failOnMissingClasses $currentPluginApiVersion = $this->getPluginApiVersion(); $currentPluginApiConstraint = new Constraint('==', $this->versionParser->normalize($currentPluginApiVersion)); - if ($requiresComposer->getPrettyString() === '1.0.0' && $this->getPluginApiVersion() === '1.0.0') { - $this->io->writeError('The "' . $package->getName() . '" plugin requires composer-plugin-api 1.0.0, this *WILL* break in the future and it should be fixed ASAP (require ^1.0 for example).'); + if ($requiresComposer->getPrettyString() === $this->getPluginApiVersion()) { + $this->io->writeError('The "' . $package->getName() . '" plugin requires composer-plugin-api '.$this->getPluginApiVersion().', this *WILL* break in the future and it should be fixed ASAP (require ^'.$this->getPluginApiVersion().' instead for example).'); } elseif (!$requiresComposer->matches($currentPluginApiConstraint)) { - $this->io->writeError('The "' . $package->getName() . '" plugin was skipped because it requires a Plugin API version ("' . $requiresComposer->getPrettyString() . '") that does not match your Composer installation ("' . $currentPluginApiVersion . '"). You may need to run composer update with the "--no-plugins" option.'); + $this->io->writeError('The "' . $package->getName() . '" plugin '.($isGlobalPlugin ? '(installed globally) ' : '').'was skipped because it requires a Plugin API version ("' . $requiresComposer->getPrettyString() . '") that does not match your Composer installation ("' . $currentPluginApiVersion . '"). You may need to run composer update with the "--no-plugins" option.'); + + return; + } + + if ($package->getName() === 'symfony/flex' && preg_match('{^[0-9.]+$}', $package->getVersion()) && version_compare($package->getVersion(), '1.9.8', '<')) { + $this->io->writeError('The "' . $package->getName() . '" plugin '.($isGlobalPlugin ? '(installed globally) ' : '').'was skipped because it is not compatible with Composer 2+. Make sure to update it to version 1.9.8 or greater.'); return; } @@ -145,7 +159,7 @@ public function registerPackage(PackageInterface $package, $failOnMissingClasses $oldInstallerPlugin = ($package->getType() === 'composer-installer'); - if (in_array($package->getName(), $this->registeredPlugins)) { + if (isset($this->registeredPlugins[$package->getName()])) { return; } @@ -158,25 +172,30 @@ public function registerPackage(PackageInterface $package, $failOnMissingClasses $localRepo = $this->composer->getRepositoryManager()->getLocalRepository(); $globalRepo = $this->globalComposer ? $this->globalComposer->getRepositoryManager()->getLocalRepository() : null; - $pool = new Pool('dev'); - $pool->addRepository($localRepo); + $rootPackage = clone $this->composer->getPackage(); + $rootPackageRepo = new RootPackageRepository($rootPackage); + $installedRepo = new InstalledRepository(array($localRepo, $rootPackageRepo)); if ($globalRepo) { - $pool->addRepository($globalRepo); + $installedRepo->addRepository($globalRepo); } $autoloadPackages = array($package->getName() => $package); - $autoloadPackages = $this->collectDependencies($pool, $autoloadPackages, $package); + $autoloadPackages = $this->collectDependencies($installedRepo, $autoloadPackages, $package); $generator = $this->composer->getAutoloadGenerator(); - $autoloads = array(); + $autoloads = array(array($rootPackage, '')); foreach ($autoloadPackages as $autoloadPackage) { + if ($autoloadPackage === $rootPackage) { + continue; + } + $downloadPath = $this->getInstallPath($autoloadPackage, $globalRepo && $globalRepo->hasPackage($autoloadPackage)); $autoloads[] = array($autoloadPackage, $downloadPath); } - $map = $generator->parseAutoloads($autoloads, new Package('dummy', '1.0.0.0', '1.0.0')); - $classLoader = $generator->createLoader($map); - $classLoader->register(); + $map = $generator->parseAutoloads($autoloads, $rootPackage); + $classLoader = $generator->createLoader($map, $this->composer->getConfig()->get('vendor-dir')); + $classLoader->register(false); foreach ($classes as $class) { if (class_exists($class, false)) { @@ -189,9 +208,11 @@ public function registerPackage(PackageInterface $package, $failOnMissingClasses $className = substr($class, $separatorPos + 1); } $code = preg_replace('{^((?:final\s+)?(?:\s*))class\s+('.preg_quote($className).')}mi', '$1class $2_composer_tmp'.self::$classCounter, $code, 1); - $code = str_replace('__FILE__', var_export($path, true), $code); - $code = str_replace('__DIR__', var_export(dirname($path), true), $code); - $code = str_replace('__CLASS__', var_export($class, true), $code); + $code = strtr($code, array( + '__FILE__' => var_export($path, true), + '__DIR__' => var_export(dirname($path), true), + '__CLASS__' => var_export($class, true), + )); $code = preg_replace('/^\s*<\?(php)?/i', '', $code, 1); eval($code); $class .= '_composer_tmp'.self::$classCounter; @@ -199,18 +220,86 @@ public function registerPackage(PackageInterface $package, $failOnMissingClasses } if ($oldInstallerPlugin) { + $this->io->writeError('Loading "'.$package->getName() . '" '.($isGlobalPlugin ? '(installed globally) ' : '').'which is a legacy composer-installer built for Composer 1.x, it is likely to cause issues as you are running Composer 2.x.'); $installer = new $class($this->io, $this->composer); $this->composer->getInstallationManager()->addInstaller($installer); + $this->registeredPlugins[$package->getName()] = $installer; } elseif (class_exists($class)) { $plugin = new $class(); - $this->addPlugin($plugin); - $this->registeredPlugins[] = $package->getName(); + $this->addPlugin($plugin, $isGlobalPlugin, $package); + $this->registeredPlugins[$package->getName()] = $plugin; } elseif ($failOnMissingClasses) { throw new \UnexpectedValueException('Plugin '.$package->getName().' could not be initialized, class not found: '.$class); } } } + /** + * Deactivates a plugin package + * + * If it's of type composer-installer it is unregistered from the installers + * instead for BC + * + * @param PackageInterface $package + * + * @throws \UnexpectedValueException + */ + public function deactivatePackage(PackageInterface $package) + { + if ($this->disablePlugins) { + return; + } + + $oldInstallerPlugin = ($package->getType() === 'composer-installer'); + + if (!isset($this->registeredPlugins[$package->getName()])) { + return; + } + + if ($oldInstallerPlugin) { + /** @var \Composer\Installer\InstallerInterface $installer */ + $installer = $this->registeredPlugins[$package->getName()]; + unset($this->registeredPlugins[$package->getName()]); + $this->composer->getInstallationManager()->removeInstaller($installer); + } else { + $plugin = $this->registeredPlugins[$package->getName()]; + unset($this->registeredPlugins[$package->getName()]); + $this->removePlugin($plugin); + } + } + + /** + * Uninstall a plugin package + * + * If it's of type composer-installer it is unregistered from the installers + * instead for BC + * + * @param PackageInterface $package + * + * @throws \UnexpectedValueException + */ + public function uninstallPackage(PackageInterface $package) + { + if ($this->disablePlugins) { + return; + } + + $oldInstallerPlugin = ($package->getType() === 'composer-installer'); + + if (!isset($this->registeredPlugins[$package->getName()])) { + return; + } + + if ($oldInstallerPlugin) { + $this->deactivatePackage($package); + } else { + $plugin = $this->registeredPlugins[$package->getName()]; + unset($this->registeredPlugins[$package->getName()]); + $this->removePlugin($plugin); + $this->uninstallPlugin($plugin); + } + } + /** * Returns the version of the internal composer-plugin-api package. * @@ -228,11 +317,19 @@ protected function getPluginApiVersion() * programmatically and want to register a plugin class directly this is a valid way * to do it. * - * @param PluginInterface $plugin plugin instance + * @param PluginInterface $plugin plugin instance + * @param ?PackageInterface $sourcePackage Package from which the plugin comes from */ - public function addPlugin(PluginInterface $plugin) + public function addPlugin(PluginInterface $plugin, $isGlobalPlugin = false, PackageInterface $sourcePackage = null) { - $this->io->writeError('Loading plugin '.get_class($plugin), true, IOInterface::DEBUG); + $details = array(); + if ($sourcePackage) { + $details[] = 'from '.$sourcePackage->getName(); + } + if ($isGlobalPlugin) { + $details[] = 'installed globally'; + } + $this->io->writeError('Loading plugin '.get_class($plugin).($details ? ' ('.implode(', ', $details).')' : ''), true, IOInterface::DEBUG); $this->plugins[] = $plugin; $plugin->activate($this->composer, $this->io); @@ -241,9 +338,49 @@ public function addPlugin(PluginInterface $plugin) } } + /** + * Removes a plugin, deactivates it and removes any listener the plugin has set on the plugin instance + * + * Ideally plugin packages should be deactivated via deactivatePackage, but if you use Composer + * programmatically and want to deregister a plugin class directly this is a valid way + * to do it. + * + * @param PluginInterface $plugin plugin instance + */ + public function removePlugin(PluginInterface $plugin) + { + $index = array_search($plugin, $this->plugins, true); + if ($index === false) { + return; + } + + $this->io->writeError('Unloading plugin '.get_class($plugin), true, IOInterface::DEBUG); + unset($this->plugins[$index]); + $plugin->deactivate($this->composer, $this->io); + + $this->composer->getEventDispatcher()->removeListener($plugin); + } + + /** + * Notifies a plugin it is being uninstalled and should clean up + * + * Ideally plugin packages should be uninstalled via uninstallPackage, but if you use Composer + * programmatically and want to deregister a plugin class directly this is a valid way + * to do it. + * + * @param PluginInterface $plugin plugin instance + */ + public function uninstallPlugin(PluginInterface $plugin) + { + $this->io->writeError('Uninstalling plugin '.get_class($plugin), true, IOInterface::DEBUG); + $plugin->uninstall($this->composer, $this->io); + } + /** * Load all plugins and installers from a repository * + * If a plugin requires another plugin, the required one will be loaded first + * * Note that plugins in the specified repository that rely on events that * have fired prior to loading will be missed. This means you likely want to * call this method as early as possible. @@ -252,19 +389,19 @@ public function addPlugin(PluginInterface $plugin) * * @throws \RuntimeException */ - private function loadRepository(RepositoryInterface $repo) + private function loadRepository(RepositoryInterface $repo, $isGlobalRepo) { $packages = $repo->getPackages(); - $sortedPackages = array_reverse(PackageSorter::sortPackages($packages)); + $sortedPackages = PackageSorter::sortPackages($packages); foreach ($sortedPackages as $package) { if (!($package instanceof CompletePackage)) { continue; } if ('composer-plugin' === $package->getType()) { - $this->registerPackage($package); + $this->registerPackage($package, false, $isGlobalRepo); // Backward compatibility } elseif ('composer-installer' === $package->getType()) { - $this->registerPackage($package); + $this->registerPackage($package, false, $isGlobalRepo); } } } @@ -272,47 +409,26 @@ private function loadRepository(RepositoryInterface $repo) /** * Recursively generates a map of package names to packages for all deps * - * @param Pool $pool Package pool of installed packages - * @param array $collected Current state of the map for recursion - * @param PackageInterface $package The package to analyze + * @param InstalledRepository $installedRepo Set of local repos + * @param array $collected Current state of the map for recursion + * @param PackageInterface $package The package to analyze * * @return array Map of package names to packages */ - private function collectDependencies(Pool $pool, array $collected, PackageInterface $package) + private function collectDependencies(InstalledRepository $installedRepo, array $collected, PackageInterface $package) { - $requires = array_merge( - $package->getRequires(), - $package->getDevRequires() - ); - - foreach ($requires as $requireLink) { - $requiredPackage = $this->lookupInstalledPackage($pool, $requireLink); - if ($requiredPackage && !isset($collected[$requiredPackage->getName()])) { - $collected[$requiredPackage->getName()] = $requiredPackage; - $collected = $this->collectDependencies($pool, $collected, $requiredPackage); + foreach ($package->getRequires() as $requireLink) { + foreach ($installedRepo->findPackagesWithReplacersAndProviders($requireLink->getTarget()) as $requiredPackage) { + if (!isset($collected[$requiredPackage->getName()])) { + $collected[$requiredPackage->getName()] = $requiredPackage; + $collected = $this->collectDependencies($installedRepo, $collected, $requiredPackage); + } } } return $collected; } - /** - * Resolves a package link to a package in the installed pool - * - * Since dependencies are already installed this should always find one. - * - * @param Pool $pool Pool of installed packages only - * @param Link $link Package link to look up - * - * @return PackageInterface|null The found package - */ - private function lookupInstalledPackage(Pool $pool, Link $link) - { - $packages = $pool->whatProvides($link->getTarget(), $link->getConstraint()); - - return !empty($packages) ? $packages[0] : null; - } - /** * Retrieves the path a package is installed to. * @@ -352,17 +468,22 @@ protected function getCapabilityImplementationClassName(PluginInterface $plugin, array_key_exists($capability, $capabilities) && (empty($capabilities[$capability]) || !is_string($capabilities[$capability]) || !trim($capabilities[$capability])) ) { - throw new \UnexpectedValueException('Plugin '.get_class($plugin).' provided invalid capability class name(s), got '.var_export($capabilities[$capability], 1)); + throw new \UnexpectedValueException('Plugin '.get_class($plugin).' provided invalid capability class name(s), got '.var_export($capabilities[$capability], true)); } + + return null; } /** - * @param PluginInterface $plugin - * @param string $capabilityClassName The fully qualified name of the API interface which the plugin may provide - * an implementation of. - * @param array $ctorArgs Arguments passed to Capability's constructor. - * Keeping it an array will allow future values to be passed w\o changing the signature. + * @template CapabilityClass of Capability + * @param PluginInterface $plugin + * @param class-string $capabilityClassName The fully qualified name of the API interface which the plugin may provide + * an implementation of. + * @param array $ctorArgs Arguments passed to Capability's constructor. + * Keeping it an array will allow future values to be passed w\o changing the signature. * @return null|Capability + * @phpstan-param class-string $capabilityClassName + * @phpstan-return null|CapabilityClass */ public function getPluginCapability(PluginInterface $plugin, $capabilityClassName, array $ctorArgs = array()) { @@ -383,14 +504,17 @@ public function getPluginCapability(PluginInterface $plugin, $capabilityClassNam return $capabilityObj; } + + return null; } /** - * @param string $capabilityClassName The fully qualified name of the API interface which the plugin may provide - * an implementation of. - * @param array $ctorArgs Arguments passed to Capability's constructor. - * Keeping it an array will allow future values to be passed w\o changing the signature. - * @return Capability[] + * @template CapabilityClass of Capability + * @param class-string $capabilityClassName The fully qualified name of the API interface which the plugin may provide + * an implementation of. + * @param array $ctorArgs Arguments passed to Capability's constructor. + * Keeping it an array will allow future values to be passed w\o changing the signature. + * @return CapabilityClass[] */ public function getPluginCapabilities($capabilityClassName, array $ctorArgs = array()) { diff --git a/app/vendor/composer/composer/src/Composer/Plugin/PostFileDownloadEvent.php b/app/vendor/composer/composer/src/Composer/Plugin/PostFileDownloadEvent.php new file mode 100644 index 000000000..7be34d728 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Plugin/PostFileDownloadEvent.php @@ -0,0 +1,147 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Plugin; + +use Composer\EventDispatcher\Event; +use Composer\Package\PackageInterface; + +/** + * The post file download event. + * + * @author Nils Adermann + */ +class PostFileDownloadEvent extends Event +{ + /** + * @var string + */ + private $fileName; + + /** + * @var string|null + */ + private $checksum; + + /** + * @var string + */ + private $url; + + /** + * @var mixed + */ + private $context; + + /** + * @var string + */ + private $type; + + /** + * Constructor. + * + * @param string $name The event name + * @param string|null $fileName The file name + * @param string|null $checksum The checksum + * @param string $url The processed url + * @param string $type The type (package or metadata). + * @param mixed $context Additional context for the download. + */ + public function __construct($name, $fileName, $checksum, $url, $type, $context = null) + { + /** @phpstan-ignore-next-line */ + if ($context === null && $type instanceof PackageInterface) { + $context = $type; + $type = 'package'; + trigger_error('PostFileDownloadEvent::__construct should receive a $type=package and the package object in $context since Composer 2.1.', E_USER_DEPRECATED); + } + + parent::__construct($name); + $this->fileName = $fileName; + $this->checksum = $checksum; + $this->url = $url; + $this->context = $context; + $this->type = $type; + } + + /** + * Retrieves the target file name location. + * + * If this download is of type metadata, null is returned. + * + * @return string|null + */ + public function getFileName() + { + return $this->fileName; + } + + /** + * Gets the checksum. + * + * @return string|null + */ + public function getChecksum() + { + return $this->checksum; + } + + /** + * Gets the processed URL. + * + * @return string + */ + public function getUrl() + { + return $this->url; + } + + /** + * Returns the context of this download, if any. + * + * If this download is of type package, the package object is returned. If + * this download is of type metadata, an array{response: Response, repository: RepositoryInterface} is returned. + * + * @return mixed + */ + public function getContext() + { + return $this->context; + } + + /** + * Get the package. + * + * If this download is of type metadata, null is returned. + * + * @return \Composer\Package\PackageInterface|null The package. + * @deprecated Use getContext instead + */ + public function getPackage() + { + trigger_error('PostFileDownloadEvent::getPackage is deprecated since Composer 2.1, use getContext instead.', E_USER_DEPRECATED); + $context = $this->getContext(); + + return $context instanceof PackageInterface ? $context : null; + } + + /** + * Returns the type of this download (package, metadata). + * + * @return string + */ + public function getType() + { + return $this->type; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Plugin/PreFileDownloadEvent.php b/app/vendor/composer/composer/src/Composer/Plugin/PreFileDownloadEvent.php index 7ae6821ce..1c07d5ca6 100644 --- a/app/vendor/composer/composer/src/Composer/Plugin/PreFileDownloadEvent.php +++ b/app/vendor/composer/composer/src/Composer/Plugin/PreFileDownloadEvent.php @@ -13,7 +13,7 @@ namespace Composer\Plugin; use Composer\EventDispatcher\Event; -use Composer\Util\RemoteFilesystem; +use Composer\Util\HttpDownloader; /** * The pre file download event. @@ -23,56 +23,145 @@ class PreFileDownloadEvent extends Event { /** - * @var RemoteFilesystem + * @var HttpDownloader */ - private $rfs; + private $httpDownloader; /** * @var string */ private $processedUrl; + /** + * @var string|null + */ + private $customCacheKey; + + /** + * @var string + */ + private $type; + + /** + * @var mixed + */ + private $context; + + /** + * @var mixed[] + */ + private $transportOptions = array(); + /** * Constructor. * - * @param string $name The event name - * @param RemoteFilesystem $rfs - * @param string $processedUrl + * @param string $name The event name + * @param HttpDownloader $httpDownloader + * @param string $processedUrl + * @param string $type + * @param mixed $context */ - public function __construct($name, RemoteFilesystem $rfs, $processedUrl) + public function __construct($name, HttpDownloader $httpDownloader, $processedUrl, $type, $context = null) { parent::__construct($name); - $this->rfs = $rfs; + $this->httpDownloader = $httpDownloader; $this->processedUrl = $processedUrl; + $this->type = $type; + $this->context = $context; } /** - * Returns the remote filesystem + * @return HttpDownloader + */ + public function getHttpDownloader() + { + return $this->httpDownloader; + } + + /** + * Retrieves the processed URL that will be downloaded. + * + * @return string + */ + public function getProcessedUrl() + { + return $this->processedUrl; + } + + /** + * Sets the processed URL that will be downloaded. * - * @return RemoteFilesystem + * @param string $processedUrl New processed URL */ - public function getRemoteFilesystem() + public function setProcessedUrl($processedUrl) { - return $this->rfs; + $this->processedUrl = $processedUrl; } /** - * Sets the remote filesystem + * Retrieves a custom package cache key for this download. * - * @param RemoteFilesystem $rfs + * @return string|null */ - public function setRemoteFilesystem(RemoteFilesystem $rfs) + public function getCustomCacheKey() { - $this->rfs = $rfs; + return $this->customCacheKey; } /** - * Retrieves the processed URL this remote filesystem will be used for + * Sets a custom package cache key for this download. + * + * @param string|null $customCacheKey New cache key + */ + public function setCustomCacheKey($customCacheKey) + { + $this->customCacheKey = $customCacheKey; + } + + /** + * Returns the type of this download (package, metadata). * * @return string */ - public function getProcessedUrl() + public function getType() { - return $this->processedUrl; + return $this->type; + } + + /** + * Returns the context of this download, if any. + * + * If this download is of type package, the package object is returned. + * If the type is metadata, an array{repository: RepositoryInterface} is returned. + * + * @return mixed + */ + public function getContext() + { + return $this->context; + } + + /** + * Returns transport options for the download. + * + * Only available for events with type metadata, for packages set the transport options on the package itself. + * + * @return array + */ + public function getTransportOptions() + { + return $this->transportOptions; + } + + /** + * Sets transport options for the download. + * + * Only available for events with type metadata, for packages set the transport options on the package itself. + * + * @param array $options + */ + public function setTransportOptions(array $options) + { + $this->transportOptions = $options; } } diff --git a/app/vendor/composer/composer/src/Composer/Plugin/PrePoolCreateEvent.php b/app/vendor/composer/composer/src/Composer/Plugin/PrePoolCreateEvent.php new file mode 100644 index 000000000..baa4bc985 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Plugin/PrePoolCreateEvent.php @@ -0,0 +1,175 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Plugin; + +use Composer\EventDispatcher\Event; +use Composer\Repository\RepositoryInterface; +use Composer\DependencyResolver\Request; +use Composer\Package\PackageInterface; +use Composer\Package\BasePackage; + +/** + * The pre command run event. + * + * @author Jordi Boggiano + */ +class PrePoolCreateEvent extends Event +{ + /** + * @var RepositoryInterface[] + */ + private $repositories; + /** + * @var Request + */ + private $request; + /** + * @var int[] array of stability => BasePackage::STABILITY_* value + * @phpstan-var array + */ + private $acceptableStabilities; + /** + * @var int[] array of package name => BasePackage::STABILITY_* value + * @phpstan-var array + */ + private $stabilityFlags; + /** + * @var array[] of package => version => [alias, alias_normalized] + * @phpstan-var array> + */ + private $rootAliases; + /** + * @var string[] + * @phpstan-var array + */ + private $rootReferences; + /** + * @var PackageInterface[] + */ + private $packages; + /** + * @var PackageInterface[] + */ + private $unacceptableFixedPackages; + + /** + * @param string $name The event name + * @param RepositoryInterface[] $repositories + * @param int[] $acceptableStabilities array of stability => BasePackage::STABILITY_* value + * @param int[] $stabilityFlags array of package name => BasePackage::STABILITY_* value + * @param array[] $rootAliases array of package => version => [alias, alias_normalized] + * @param string[] $rootReferences + * + * @phpstan-param array $acceptableStabilities + * @phpstan-param array $stabilityFlags + * @phpstan-param array> $rootAliases + * @phpstan-param array $rootReferences + */ + public function __construct($name, array $repositories, Request $request, array $acceptableStabilities, array $stabilityFlags, array $rootAliases, array $rootReferences, array $packages, array $unacceptableFixedPackages) + { + parent::__construct($name); + + $this->repositories = $repositories; + $this->request = $request; + $this->acceptableStabilities = $acceptableStabilities; + $this->stabilityFlags = $stabilityFlags; + $this->rootAliases = $rootAliases; + $this->rootReferences = $rootReferences; + $this->packages = $packages; + $this->unacceptableFixedPackages = $unacceptableFixedPackages; + } + + /** + * @return RepositoryInterface[] + */ + public function getRepositories() + { + return $this->repositories; + } + + /** + * @return Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * @return int[] array of stability => BasePackage::STABILITY_* value + * @phpstan-return array + */ + public function getAcceptableStabilities() + { + return $this->acceptableStabilities; + } + + /** + * @return int[] array of package name => BasePackage::STABILITY_* value + * @phpstan-return array + */ + public function getStabilityFlags() + { + return $this->stabilityFlags; + } + + /** + * @return array[] of package => version => [alias, alias_normalized] + * @phpstan-return array> + */ + public function getRootAliases() + { + return $this->rootAliases; + } + + /** + * @return string[] + * @phpstan-return array + */ + public function getRootReferences() + { + return $this->rootReferences; + } + + /** + * @return PackageInterface[] + */ + public function getPackages() + { + return $this->packages; + } + + /** + * @return PackageInterface[] + */ + public function getUnacceptableFixedPackages() + { + return $this->unacceptableFixedPackages; + } + + /** + * @param PackageInterface[] $packages + */ + public function setPackages(array $packages) + { + $this->packages = $packages; + } + + /** + * @param PackageInterface[] $packages + */ + public function setUnacceptableFixedPackages(array $packages) + { + $this->unacceptableFixedPackages = $packages; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Question/StrictConfirmationQuestion.php b/app/vendor/composer/composer/src/Composer/Question/StrictConfirmationQuestion.php index c64890b8d..19432201d 100644 --- a/app/vendor/composer/composer/src/Composer/Question/StrictConfirmationQuestion.php +++ b/app/vendor/composer/composer/src/Composer/Question/StrictConfirmationQuestion.php @@ -24,7 +24,9 @@ */ class StrictConfirmationQuestion extends Question { + /** @var string */ private $trueAnswerRegex; + /** @var string */ private $falseAnswerRegex; /** diff --git a/app/vendor/composer/composer/src/Composer/Repository/ArrayRepository.php b/app/vendor/composer/composer/src/Composer/Repository/ArrayRepository.php index c11c7fe68..8b44547a1 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/ArrayRepository.php +++ b/app/vendor/composer/composer/src/Composer/Repository/ArrayRepository.php @@ -13,9 +13,13 @@ namespace Composer\Repository; use Composer\Package\AliasPackage; +use Composer\Package\BasePackage; +use Composer\Package\CompleteAliasPackage; +use Composer\Package\CompletePackage; use Composer\Package\PackageInterface; use Composer\Package\CompletePackageInterface; use Composer\Package\Version\VersionParser; +use Composer\Package\Version\StabilityFilter; use Composer\Semver\Constraint\ConstraintInterface; use Composer\Semver\Constraint\Constraint; @@ -24,15 +28,15 @@ * * @author Nils Adermann */ -class ArrayRepository extends BaseRepository +class ArrayRepository implements RepositoryInterface { - /** @var PackageInterface[] */ - protected $packages; - - /** - * @var PackageInterface[] indexed by package unique name and used to cache hasPackage calls - */ - protected $packageMap; + /** @var ?array */ + protected $packages = null; + + /** + * @var ?array indexed by package unique name and used to cache hasPackage calls + */ + protected $packageMap = null; public function __construct(array $packages = array()) { @@ -41,6 +45,51 @@ public function __construct(array $packages = array()) } } + public function getRepoName() + { + return 'array repo (defining '.$this->count().' package'.($this->count() > 1 ? 's' : '').')'; + } + + /** + * {@inheritDoc} + */ + public function loadPackages(array $packageMap, array $acceptableStabilities, array $stabilityFlags, array $alreadyLoaded = array()) + { + $packages = $this->getPackages(); + + $result = array(); + $namesFound = array(); + foreach ($packages as $package) { + if (array_key_exists($package->getName(), $packageMap)) { + if ( + (!$packageMap[$package->getName()] || $packageMap[$package->getName()]->matches(new Constraint('==', $package->getVersion()))) + && StabilityFilter::isPackageAcceptable($acceptableStabilities, $stabilityFlags, $package->getNames(), $package->getStability()) + && !isset($alreadyLoaded[$package->getName()][$package->getVersion()]) + ) { + // add selected packages which match stability requirements + $result[spl_object_hash($package)] = $package; + // add the aliased package for packages where the alias matches + if ($package instanceof AliasPackage && !isset($result[spl_object_hash($package->getAliasOf())])) { + $result[spl_object_hash($package->getAliasOf())] = $package->getAliasOf(); + } + } + + $namesFound[$package->getName()] = true; + } + } + + // add aliases of packages that were selected, even if the aliases did not match + foreach ($packages as $package) { + if ($package instanceof AliasPackage) { + if (isset($result[spl_object_hash($package->getAliasOf())])) { + $result[spl_object_hash($package)] = $package; + } + } + } + + return array('namesFound' => array_keys($namesFound), 'packages' => $result); + } + /** * {@inheritDoc} */ @@ -81,8 +130,7 @@ public function findPackages($name, $constraint = null) foreach ($this->getPackages() as $package) { if ($name === $package->getName()) { - $pkgConstraint = new Constraint('==', $package->getVersion()); - if (null === $constraint || $constraint->matches($pkgConstraint)) { + if (null === $constraint || $constraint->matches(new Constraint('==', $package->getVersion()))) { $packages[] = $package; } } @@ -160,9 +208,47 @@ public function addPackage(PackageInterface $package) $this->packageMap = null; } + /** + * {@inheritDoc} + */ + public function getProviders($packageName) + { + $result = array(); + + foreach ($this->getPackages() as $candidate) { + if (isset($result[$candidate->getName()])) { + continue; + } + foreach ($candidate->getProvides() as $link) { + if ($packageName === $link->getTarget()) { + $result[$candidate->getName()] = array( + 'name' => $candidate->getName(), + 'description' => $candidate instanceof CompletePackageInterface ? $candidate->getDescription() : null, + 'type' => $candidate->getType(), + ); + continue 2; + } + } + } + + return $result; + } + + /** + * @phpstan-param PackageInterface&BasePackage $package + * @return AliasPackage|CompleteAliasPackage + */ protected function createAliasPackage(PackageInterface $package, $alias, $prettyAlias) { - return new AliasPackage($package instanceof AliasPackage ? $package->getAliasOf() : $package, $alias, $prettyAlias); + while ($package instanceof AliasPackage) { + $package = $package->getAliasOf(); + } + + if ($package instanceof CompletePackage) { + return new CompleteAliasPackage($package, $alias, $prettyAlias); + } + + return new AliasPackage($package, $alias, $prettyAlias); } /** @@ -195,6 +281,10 @@ public function getPackages() $this->initialize(); } + if (null === $this->packages) { + throw new \LogicException('initialize failed to initialize the packages array'); + } + return $this->packages; } @@ -203,8 +293,13 @@ public function getPackages() * * @return int Number of packages */ + #[\ReturnTypeWillChange] public function count() { + if (null === $this->packages) { + $this->initialize(); + } + return count($this->packages); } diff --git a/app/vendor/composer/composer/src/Composer/Repository/ArtifactRepository.php b/app/vendor/composer/composer/src/Composer/Repository/ArtifactRepository.php index aff80e4cd..02b593a0b 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/ArtifactRepository.php +++ b/app/vendor/composer/composer/src/Composer/Repository/ArtifactRepository.php @@ -16,6 +16,7 @@ use Composer\Json\JsonFile; use Composer\Package\Loader\ArrayLoader; use Composer\Package\Loader\LoaderInterface; +use Composer\Util\Tar; use Composer\Util\Zip; /** @@ -26,8 +27,11 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito /** @var LoaderInterface */ protected $loader; + /** @var string */ protected $lookup; + /** @var mixed[] */ protected $repoConfig; + /** @var IOInterface */ private $io; public function __construct(array $repoConfig, IOInterface $io) @@ -43,6 +47,11 @@ public function __construct(array $repoConfig, IOInterface $io) $this->repoConfig = $repoConfig; } + public function getRepoName() + { + return 'artifact repo ('.$this->lookup.')'; + } + public function getRepoConfig() { return $this->repoConfig; @@ -61,7 +70,7 @@ private function scanDirectory($path) $directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS); $iterator = new \RecursiveIteratorIterator($directory); - $regex = new \RegexIterator($iterator, '/^.+\.(zip|phar)$/i'); + $regex = new \RegexIterator($iterator, '/^.+\.(zip|phar|tar|gz|tgz)$/i'); foreach ($regex as $file) { /* @var $file \SplFileInfo */ if (!$file->isFile()) { @@ -83,7 +92,26 @@ private function scanDirectory($path) private function getComposerInformation(\SplFileInfo $file) { - $json = Zip::getComposerJson($file->getPathname()); + $json = null; + $fileType = null; + $fileExtension = pathinfo($file->getPathname(), PATHINFO_EXTENSION); + if (in_array($fileExtension, array('gz', 'tar', 'tgz'), true)) { + $fileType = 'tar'; + } elseif ($fileExtension === 'zip') { + $fileType = 'zip'; + } else { + throw new \RuntimeException('Files with "'.$fileExtension.'" extensions aren\'t supported. Only ZIP and TAR/TAR.GZ/TGZ archives are supported.'); + } + + try { + if ($fileType === 'tar') { + $json = Tar::getComposerJson($file->getPathname()); + } else { + $json = Zip::getComposerJson($file->getPathname()); + } + } catch (\Exception $exception) { + $this->io->write('Failed loading package '.$file->getPathname().': '.$exception->getMessage(), false, IOInterface::VERBOSE); + } if (null === $json) { return false; @@ -91,7 +119,7 @@ private function getComposerInformation(\SplFileInfo $file) $package = JsonFile::parseJson($json, $file->getPathname().'#composer.json'); $package['dist'] = array( - 'type' => 'zip', + 'type' => $fileType, 'url' => strtr($file->getPathname(), '\\', '/'), 'shasum' => sha1_file($file->getRealPath()), ); diff --git a/app/vendor/composer/composer/src/Composer/Repository/BaseRepository.php b/app/vendor/composer/composer/src/Composer/Repository/BaseRepository.php deleted file mode 100644 index a122d2e88..000000000 --- a/app/vendor/composer/composer/src/Composer/Repository/BaseRepository.php +++ /dev/null @@ -1,189 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Repository; - -use Composer\Package\RootPackageInterface; -use Composer\Semver\Constraint\ConstraintInterface; -use Composer\Semver\Constraint\Constraint; -use Composer\Package\Link; - -/** - * Common ancestor class for generic repository functionality. - * - * @author Niels Keurentjes - */ -abstract class BaseRepository implements RepositoryInterface -{ - /** - * Returns a list of links causing the requested needle packages to be installed, as an associative array with the - * dependent's name as key, and an array containing in order the PackageInterface and Link describing the relationship - * as values. If recursive lookup was requested a third value is returned containing an identically formed array up - * to the root package. That third value will be false in case a circular recursion was detected. - * - * @param string|string[] $needle The package name(s) to inspect. - * @param ConstraintInterface|null $constraint Optional constraint to filter by. - * @param bool $invert Whether to invert matches to discover reasons for the package *NOT* to be installed. - * @param bool $recurse Whether to recursively expand the requirement tree up to the root package. - * @param string[] $packagesFound Used internally when recurring - * @return array An associative array of arrays as described above. - */ - public function getDependents($needle, $constraint = null, $invert = false, $recurse = true, $packagesFound = null) - { - $needles = array_map('strtolower', (array) $needle); - $results = array(); - - // initialize the array with the needles before any recursion occurs - if (null === $packagesFound) { - $packagesFound = $needles; - } - - // locate root package for use below - $rootPackage = null; - foreach ($this->getPackages() as $package) { - if ($package instanceof RootPackageInterface) { - $rootPackage = $package; - break; - } - } - - // Loop over all currently installed packages. - foreach ($this->getPackages() as $package) { - $links = $package->getRequires(); - - // each loop needs its own "tree" as we want to show the complete dependent set of every needle - // without warning all the time about finding circular deps - $packagesInTree = $packagesFound; - - // Replacements are considered valid reasons for a package to be installed during forward resolution - if (!$invert) { - $links += $package->getReplaces(); - - // On forward search, check if any replaced package was required and add the replaced - // packages to the list of needles. Contrary to the cross-reference link check below, - // replaced packages are the target of links. - foreach ($package->getReplaces() as $link) { - foreach ($needles as $needle) { - if ($link->getSource() === $needle) { - if ($constraint === null || ($link->getConstraint()->matches($constraint) === !$invert)) { - // already displayed this node's dependencies, cutting short - if (in_array($link->getTarget(), $packagesInTree)) { - $results[] = array($package, $link, false); - continue; - } - $packagesInTree[] = $link->getTarget(); - $dependents = $recurse ? $this->getDependents($link->getTarget(), null, false, true, $packagesInTree) : array(); - $results[] = array($package, $link, $dependents); - $needles[] = $link->getTarget(); - } - } - } - } - } - - // Require-dev is only relevant for the root package - if ($package instanceof RootPackageInterface) { - $links += $package->getDevRequires(); - } - - // Cross-reference all discovered links to the needles - foreach ($links as $link) { - foreach ($needles as $needle) { - if ($link->getTarget() === $needle) { - if ($constraint === null || ($link->getConstraint()->matches($constraint) === !$invert)) { - // already displayed this node's dependencies, cutting short - if (in_array($link->getSource(), $packagesInTree)) { - $results[] = array($package, $link, false); - continue; - } - $packagesInTree[] = $link->getSource(); - $dependents = $recurse ? $this->getDependents($link->getSource(), null, false, true, $packagesInTree) : array(); - $results[] = array($package, $link, $dependents); - } - } - } - } - - // When inverting, we need to check for conflicts of the needles against installed packages - if ($invert && in_array($package->getName(), $needles)) { - foreach ($package->getConflicts() as $link) { - foreach ($this->findPackages($link->getTarget()) as $pkg) { - $version = new Constraint('=', $pkg->getVersion()); - if ($link->getConstraint()->matches($version) === $invert) { - $results[] = array($package, $link, false); - } - } - } - } - - // When inverting, we need to check for conflicts of the needles' requirements against installed packages - if ($invert && $constraint && in_array($package->getName(), $needles) && $constraint->matches(new Constraint('=', $package->getVersion()))) { - foreach ($package->getRequires() as $link) { - if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { - if ($this->findPackage($link->getTarget(), $link->getConstraint())) { - continue; - } - - $platformPkg = $this->findPackage($link->getTarget(), '*'); - $description = $platformPkg ? 'but '.$platformPkg->getPrettyVersion().' is installed' : 'but it is missing'; - $results[] = array($package, new Link($package->getName(), $link->getTarget(), null, 'requires', $link->getPrettyConstraint().' '.$description), false); - - continue; - } - - foreach ($this->getPackages() as $pkg) { - if (!in_array($link->getTarget(), $pkg->getNames())) { - continue; - } - - $version = new Constraint('=', $pkg->getVersion()); - - if ($link->getTarget() !== $pkg->getName()) { - foreach (array_merge($pkg->getReplaces(), $pkg->getProvides()) as $prov) { - if ($link->getTarget() === $prov->getTarget()) { - $version = $prov->getConstraint(); - break; - } - } - } - - if (!$link->getConstraint()->matches($version)) { - // if we have a root package (we should but can not guarantee..) we show - // the root requires as well to perhaps allow to find an issue there - if ($rootPackage) { - foreach (array_merge($rootPackage->getRequires(), $rootPackage->getDevRequires()) as $rootReq) { - if (in_array($rootReq->getTarget(), $pkg->getNames()) && !$rootReq->getConstraint()->matches($link->getConstraint())) { - $results[] = array($package, $link, false); - $results[] = array($rootPackage, $rootReq, false); - continue 3; - } - } - - $results[] = array($package, $link, false); - $results[] = array($rootPackage, new Link($rootPackage->getName(), $link->getTarget(), null, 'does not require', 'but ' . $pkg->getPrettyVersion() . ' is installed'), false); - } else { - // no root so let's just print whatever we found - $results[] = array($package, $link, false); - } - } - - continue 2; - } - } - } - } - - ksort($results); - - return $results; - } -} diff --git a/app/vendor/composer/composer/src/Composer/Repository/ComposerRepository.php b/app/vendor/composer/composer/src/Composer/Repository/ComposerRepository.php index 57a95449f..75b3aabec 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/ComposerRepository.php +++ b/app/vendor/composer/composer/src/Composer/Repository/ComposerRepository.php @@ -12,58 +12,123 @@ namespace Composer\Repository; +use Composer\Package\BasePackage; use Composer\Package\Loader\ArrayLoader; use Composer\Package\PackageInterface; use Composer\Package\AliasPackage; +use Composer\Package\CompletePackage; +use Composer\Package\CompleteAliasPackage; use Composer\Package\Version\VersionParser; -use Composer\DependencyResolver\Pool; +use Composer\Package\Version\StabilityFilter; use Composer\Json\JsonFile; use Composer\Cache; use Composer\Config; -use Composer\Composer; -use Composer\Factory; use Composer\IO\IOInterface; -use Composer\Util\RemoteFilesystem; +use Composer\Plugin\PostFileDownloadEvent; +use Composer\Semver\CompilingMatcher; +use Composer\Util\HttpDownloader; +use Composer\Util\Loop; use Composer\Plugin\PluginEvents; use Composer\Plugin\PreFileDownloadEvent; use Composer\EventDispatcher\EventDispatcher; use Composer\Downloader\TransportException; use Composer\Semver\Constraint\ConstraintInterface; use Composer\Semver\Constraint\Constraint; +use Composer\Semver\Constraint\MatchAllConstraint; +use Composer\Util\Http\Response; +use Composer\MetadataMinifier\MetadataMinifier; +use Composer\Util\Url; /** * @author Jordi Boggiano */ class ComposerRepository extends ArrayRepository implements ConfigurableRepositoryInterface { - protected $config; - protected $repoConfig; - protected $options; - protected $url; - protected $baseUrl; - protected $io; - protected $rfs; + /** + * @var mixed[] + * @phpstan-var array{url: string, options?: mixed[], type?: 'composer', allow_ssl_downgrade?: bool} + */ + private $repoConfig; + /** @var mixed[] */ + private $options; + /** @var string */ + private $url; + /** @var string */ + private $baseUrl; + /** @var IOInterface */ + private $io; + /** @var HttpDownloader */ + private $httpDownloader; + /** @var Loop */ + private $loop; + /** @var Cache */ protected $cache; - protected $notifyUrl; - protected $searchUrl; + /** @var ?string */ + protected $notifyUrl = null; + /** @var ?string */ + protected $searchUrl = null; + /** @var ?string a URL containing %package% which can be queried to get providers of a given name */ + protected $providersApiUrl = null; + /** @var bool */ protected $hasProviders = false; - protected $providersUrl; - protected $lazyProvidersUrl; + /** @var ?string */ + protected $providersUrl = null; + /** @var ?string */ + protected $listUrl = null; + /** @var bool Indicates whether a comprehensive list of packages this repository might provide is expressed in the repository root. **/ + protected $hasAvailablePackageList = false; + /** @var ?array */ + protected $availablePackages = null; + /** @var ?array */ + protected $availablePackagePatterns = null; + /** @var ?string */ + protected $lazyProvidersUrl = null; + /** @var ?array */ protected $providerListing; - protected $providers = array(); - protected $providersByUid = array(); + /** @var ArrayLoader */ protected $loader; - protected $rootAliases; - protected $allowSslDowngrade = false; - protected $eventDispatcher; - protected $sourceMirrors; - protected $distMirrors; + /** @var bool */ + private $allowSslDowngrade = false; + /** @var ?EventDispatcher */ + private $eventDispatcher; + /** @var ?array */ + private $sourceMirrors; + /** @var ?array */ + private $distMirrors; + /** @var bool */ private $degradedMode = false; + /** @var mixed[]|true */ private $rootData; - private $hasPartialPackages; - private $partialPackagesByName; + /** @var bool */ + private $hasPartialPackages = false; + /** @var ?array */ + private $partialPackagesByName = null; + + /** + * TODO v3 should make this private once we can drop PHP 5.3 support + * @private + * @var array list of package names which are fresh and can be loaded from the cache directly in case loadPackage is called several times + * useful for v2 metadata repositories with lazy providers + * @phpstan-var array + */ + public $freshMetadataUrls = array(); + + /** + * TODO v3 should make this private once we can drop PHP 5.3 support + * @private + * @var array list of package names which returned a 404 and should not be re-fetched in case loadPackage is called several times + * useful for v2 metadata repositories with lazy providers + * @phpstan-var array + */ + public $packagesNotFoundCache = array(); + /** + * TODO v3 should make this private once we can drop PHP 5.3 support + * @private + * @var VersionParser + */ + public $versionParser; - public function __construct(array $repoConfig, IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, RemoteFilesystem $rfs = null) + public function __construct(array $repoConfig, IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $eventDispatcher = null) { parent::__construct(); if (!preg_match('{^[\w.]+\??://}', $repoConfig['url'])) { @@ -72,7 +137,7 @@ public function __construct(array $repoConfig, IOInterface $io, Config $config, } $repoConfig['url'] = rtrim($repoConfig['url'], '/'); - if ('https?' === substr($repoConfig['url'], 0, 6)) { + if (strpos($repoConfig['url'], 'https?') === 0) { $repoConfig['url'] = (extension_loaded('openssl') ? 'https' : 'http') . substr($repoConfig['url'], 6); } @@ -88,7 +153,6 @@ public function __construct(array $repoConfig, IOInterface $io, Config $config, $this->allowSslDowngrade = true; } - $this->config = $config; $this->options = $repoConfig['options']; $this->url = $repoConfig['url']; @@ -99,25 +163,24 @@ public function __construct(array $repoConfig, IOInterface $io, Config $config, $this->baseUrl = rtrim(preg_replace('{(?:/[^/\\\\]+\.json)?(?:[?#].*)?$}', '', $this->url), '/'); $this->io = $io; - $this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url), 'a-z0-9.$'); - $this->loader = new ArrayLoader(); - if ($rfs && $this->options) { - $rfs = clone $rfs; - $rfs->setOptions($this->options); - } - $this->rfs = $rfs ?: Factory::createRemoteFilesystem($this->io, $this->config, $this->options); + $this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', Url::sanitize($this->url)), 'a-z0-9.$~'); + $this->cache->setReadOnly($config->get('cache-read-only')); + $this->versionParser = new VersionParser(); + $this->loader = new ArrayLoader($this->versionParser); + $this->httpDownloader = $httpDownloader; $this->eventDispatcher = $eventDispatcher; $this->repoConfig = $repoConfig; + $this->loop = new Loop($this->httpDownloader); } - public function getRepoConfig() + public function getRepoName() { - return $this->repoConfig; + return 'composer repo ('.Url::sanitize($this->url).')'; } - public function setRootAliases(array $rootAliases) + public function getRepoConfig() { - $this->rootAliases = $rootAliases; + return $this->repoConfig; } /** @@ -125,30 +188,39 @@ public function setRootAliases(array $rootAliases) */ public function findPackage($name, $constraint) { - if (!$this->hasProviders()) { - return parent::findPackage($name, $constraint); - } + // this call initializes loadRootServerFile which is needed for the rest below to work + $hasProviders = $this->hasProviders(); $name = strtolower($name); if (!$constraint instanceof ConstraintInterface) { - $versionParser = new VersionParser(); - $constraint = $versionParser->parseConstraints($constraint); - } - - foreach ($this->getProviderNames() as $providerName) { - if ($name === $providerName) { - $packages = $this->whatProvides(new Pool('dev'), $providerName); - foreach ($packages as $package) { - if ($name === $package->getName()) { - $pkgConstraint = new Constraint('==', $package->getVersion()); - if ($constraint->matches($pkgConstraint)) { - return $package; - } - } + $constraint = $this->versionParser->parseConstraints($constraint); + } + + if ($this->lazyProvidersUrl) { + if ($this->hasPartialPackages() && isset($this->partialPackagesByName[$name])) { + return $this->filterPackages($this->whatProvides($name), $constraint, true); + } + + if ($this->hasAvailablePackageList && !$this->lazyProvidersRepoContains($name)) { + return null; + } + + $packages = $this->loadAsyncPackages(array($name => $constraint)); + + return reset($packages['packages']); + } + + if ($hasProviders) { + foreach ($this->getProviderNames() as $providerName) { + if ($name === $providerName) { + return $this->filterPackages($this->whatProvides($providerName), $constraint, true); } - break; } + + return null; } + + return parent::findPackage($name, $constraint); } /** @@ -156,46 +228,221 @@ public function findPackage($name, $constraint) */ public function findPackages($name, $constraint = null) { - if (!$this->hasProviders()) { - return parent::findPackages($name, $constraint); - } - // normalize name - $name = strtolower($name); + // this call initializes loadRootServerFile which is needed for the rest below to work + $hasProviders = $this->hasProviders(); + $name = strtolower($name); if (null !== $constraint && !$constraint instanceof ConstraintInterface) { - $versionParser = new VersionParser(); - $constraint = $versionParser->parseConstraints($constraint); + $constraint = $this->versionParser->parseConstraints($constraint); } - $packages = array(); + if ($this->lazyProvidersUrl) { + if ($this->hasPartialPackages() && isset($this->partialPackagesByName[$name])) { + return $this->filterPackages($this->whatProvides($name), $constraint); + } - foreach ($this->getProviderNames() as $providerName) { - if ($name === $providerName) { - $candidates = $this->whatProvides(new Pool('dev'), $providerName); - foreach ($candidates as $package) { - if ($name === $package->getName()) { - $pkgConstraint = new Constraint('==', $package->getVersion()); - if (null === $constraint || $constraint->matches($pkgConstraint)) { - $packages[] = $package; - } - } + if ($this->hasAvailablePackageList && !$this->lazyProvidersRepoContains($name)) { + return array(); + } + + $result = $this->loadAsyncPackages(array($name => $constraint)); + + return $result['packages']; + } + + if ($hasProviders) { + foreach ($this->getProviderNames() as $providerName) { + if ($name === $providerName) { + return $this->filterPackages($this->whatProvides($providerName), $constraint); } - break; } + + return array(); + } + + return parent::findPackages($name, $constraint); + } + + private function filterPackages(array $packages, $constraint = null, $returnFirstMatch = false) + { + if (null === $constraint) { + if ($returnFirstMatch) { + return reset($packages); + } + + return $packages; } - return $packages; + $filteredPackages = array(); + + foreach ($packages as $package) { + $pkgConstraint = new Constraint('==', $package->getVersion()); + + if ($constraint->matches($pkgConstraint)) { + if ($returnFirstMatch) { + return $package; + } + + $filteredPackages[] = $package; + } + } + + if ($returnFirstMatch) { + return null; + } + + return $filteredPackages; } public function getPackages() { - if ($this->hasProviders()) { - throw new \LogicException('Composer repositories that have providers can not load the complete list of packages, use getProviderNames instead.'); + $hasProviders = $this->hasProviders(); + + if ($this->lazyProvidersUrl) { + if (is_array($this->availablePackages) && !$this->availablePackagePatterns) { + $packageMap = array(); + foreach ($this->availablePackages as $name) { + $packageMap[$name] = new MatchAllConstraint(); + } + + $result = $this->loadAsyncPackages($packageMap); + + return array_values($result['packages']); + } + + if ($this->hasPartialPackages()) { + return array_values($this->partialPackagesByName); + } + + throw new \LogicException('Composer repositories that have lazy providers and no available-packages list can not load the complete list of packages, use getPackageNames instead.'); + } + + if ($hasProviders) { + throw new \LogicException('Composer repositories that have providers can not load the complete list of packages, use getPackageNames instead.'); } return parent::getPackages(); } + public function getPackageNames($packageFilter = null) + { + $hasProviders = $this->hasProviders(); + + $packageFilterCb = function ($name) { + return true; + }; + if (null !== $packageFilter) { + $packageFilterRegex = '{^'.str_replace('\\*', '.*?', preg_quote($packageFilter)).'$}i'; + $packageFilterCb = function ($name) use ($packageFilterRegex) { + return (bool) preg_match($packageFilterRegex, $name); + }; + } + + if ($this->lazyProvidersUrl) { + if (is_array($this->availablePackages)) { + return array_filter(array_keys($this->availablePackages), $packageFilterCb); + } + + if ($this->listUrl) { + $url = $this->listUrl; + if ($packageFilter) { + $url .= '?filter='.urlencode($packageFilter); + } + + $result = $this->httpDownloader->get($url, $this->options)->decodeJson(); + + return $result['packageNames']; + } + + if ($this->hasPartialPackages()) { + return array_filter(array_keys($this->partialPackagesByName), $packageFilterCb); + } + + return array(); + } + + if ($hasProviders) { + return array_filter($this->getProviderNames(), $packageFilterCb); + } + + $names = array(); + foreach ($this->getPackages() as $package) { + if ($packageFilterCb($package->getName())) { + $names[] = $package->getPrettyName(); + } + } + + return $names; + } + + public function loadPackages(array $packageNameMap, array $acceptableStabilities, array $stabilityFlags, array $alreadyLoaded = array()) + { + // this call initializes loadRootServerFile which is needed for the rest below to work + $hasProviders = $this->hasProviders(); + + if (!$hasProviders && !$this->hasPartialPackages() && !$this->lazyProvidersUrl) { + return parent::loadPackages($packageNameMap, $acceptableStabilities, $stabilityFlags, $alreadyLoaded); + } + + $packages = array(); + $namesFound = array(); + + if ($hasProviders || $this->hasPartialPackages()) { + foreach ($packageNameMap as $name => $constraint) { + $matches = array(); + + // if a repo has no providers but only partial packages and the partial packages are missing + // then we don't want to call whatProvides as it would try to load from the providers and fail + if (!$hasProviders && !isset($this->partialPackagesByName[$name])) { + continue; + } + + $candidates = $this->whatProvides($name, $acceptableStabilities, $stabilityFlags, $alreadyLoaded); + foreach ($candidates as $candidate) { + if ($candidate->getName() !== $name) { + throw new \LogicException('whatProvides should never return a package with a different name than the requested one'); + } + $namesFound[$name] = true; + + if (!$constraint || $constraint->matches(new Constraint('==', $candidate->getVersion()))) { + $matches[spl_object_hash($candidate)] = $candidate; + if ($candidate instanceof AliasPackage && !isset($matches[spl_object_hash($candidate->getAliasOf())])) { + $matches[spl_object_hash($candidate->getAliasOf())] = $candidate->getAliasOf(); + } + } + } + + // add aliases of matched packages even if they did not match the constraint + foreach ($candidates as $candidate) { + if ($candidate instanceof AliasPackage) { + if (isset($matches[spl_object_hash($candidate->getAliasOf())])) { + $matches[spl_object_hash($candidate)] = $candidate; + } + } + } + $packages = array_merge($packages, $matches); + + unset($packageNameMap[$name]); + } + } + + if ($this->lazyProvidersUrl && count($packageNameMap)) { + if ($this->hasAvailablePackageList) { + foreach ($packageNameMap as $name => $constraint) { + if (!$this->lazyProvidersRepoContains(strtolower($name))) { + unset($packageNameMap[$name]); + } + } + } + + $result = $this->loadAsyncPackages($packageNameMap, $acceptableStabilities, $stabilityFlags, $alreadyLoaded); + $packages = array_merge($packages, $result['packages']); + $namesFound = array_merge($namesFound, $result['namesFound']); + } + + return array('namesFound' => array_keys($namesFound), 'packages' => $packages); + } + /** * {@inheritDoc} */ @@ -206,9 +453,7 @@ public function search($query, $mode = 0, $type = null) if ($this->searchUrl && $mode === self::SEARCH_FULLTEXT) { $url = str_replace(array('%query%', '%type%'), array($query, $type), $this->searchUrl); - $origin = RemoteFilesystem::getOrigin($url); - $json = $this->rfs->getContents($origin, $url, false); - $search = JsonFile::parseJson($json, $url); + $search = $this->httpDownloader->get($url, $this->options)->decodeJson(); if (empty($search['results'])) { return array(); @@ -218,20 +463,20 @@ public function search($query, $mode = 0, $type = null) foreach ($search['results'] as $result) { // do not show virtual packages in results as they are not directly useful from a composer perspective if (empty($result['virtual'])) { - $results[] = $result; + $results[] = array('name' => $result['name'], 'description' => $result['description']); } } return $results; } - if ($this->hasProviders()) { + if ($this->hasProviders() || $this->lazyProvidersUrl) { $results = array(); $regex = '{(?:'.implode('|', preg_split('{\s+}', $query)).')}i'; - foreach ($this->getProviderNames() as $name) { + foreach ($this->getPackageNames() as $name) { if (preg_match($regex, $name)) { - $results[] = array('name' => $name); + $results[] = array('name' => $name, 'description' => ''); } } @@ -241,20 +486,49 @@ public function search($query, $mode = 0, $type = null) return parent::search($query, $mode); } - public function getProviderNames() + public function getProviders($packageName) { $this->loadRootServerFile(); + $result = array(); - if (null === $this->providerListing) { - $this->loadProviderListings($this->loadRootServerFile()); + if ($this->providersApiUrl) { + $apiResult = $this->httpDownloader->get(str_replace('%package%', $packageName, $this->providersApiUrl), $this->options)->decodeJson(); + + foreach ($apiResult['providers'] as $provider) { + $result[$provider['name']] = $provider; + } + + return $result; } - if ($this->hasPartialPackages) { - if (null === $this->partialPackagesByName) { - $this->initializePartialPackages(); + if ($this->hasPartialPackages()) { + foreach ($this->partialPackagesByName as $versions) { + foreach ($versions as $candidate) { + if (isset($result[$candidate['name']]) || !isset($candidate['provide'][$packageName])) { + continue; + } + $result[$candidate['name']] = array( + 'name' => $candidate['name'], + 'description' => isset($candidate['description']) ? $candidate['description'] : '', + 'type' => isset($candidate['type']) ? $candidate['type'] : '', + ); + } } + } - return array_keys($this->partialPackagesByName); + if ($this->packages) { + $result = array_merge($result, parent::getProviders($packageName)); + } + + return $result; + } + + private function getProviderNames() + { + $this->loadRootServerFile(); + + if (null === $this->providerListing) { + $this->loadProviderListings($this->loadRootServerFile()); } if ($this->lazyProvidersUrl) { @@ -280,42 +554,23 @@ protected function configurePackageTransportOptions(PackageInterface $package) } } - public function hasProviders() + private function hasProviders() { $this->loadRootServerFile(); return $this->hasProviders; } - public function resetPackageIds() - { - foreach ($this->providersByUid as $package) { - if ($package instanceof AliasPackage) { - $package->getAliasOf()->setId(-1); - } - $package->setId(-1); - } - } - /** - * @param Pool $pool - * @param string $name package name - * @param bool $bypassFilters If set to true, this bypasses the stability filtering, and forces a recompute without cache + * @param string $name package name * @return array|mixed */ - public function whatProvides(Pool $pool, $name, $bypassFilters = false) + private function whatProvides($name, array $acceptableStabilities = null, array $stabilityFlags = null, array $alreadyLoaded = array()) { - if (isset($this->providers[$name]) && !$bypassFilters) { - return $this->providers[$name]; - } - - if ($this->hasPartialPackages && null === $this->partialPackagesByName) { - $this->initializePartialPackages(); - } - - if (!$this->hasPartialPackages || !isset($this->partialPackagesByName[$name])) { + $packagesSource = null; + if (!$this->hasPartialPackages() || !isset($this->partialPackagesByName[$name])) { // skip platform packages, root package and composer-plugin-api - if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name) || '__root__' === $name || 'composer-plugin-api' === $name) { + if (PlatformRepository::isPlatformPackage($name) || '__root__' === $name) { return array(); } @@ -343,20 +598,20 @@ public function whatProvides(Pool $pool, $name, $bypassFilters = false) } $packages = null; - if ($cacheKey) { - if (!$useLastModifiedCheck && $hash && $this->cache->sha256($cacheKey) === $hash) { - $packages = json_decode($this->cache->read($cacheKey), true); - } elseif ($useLastModifiedCheck) { - if ($contents = $this->cache->read($cacheKey)) { - $contents = json_decode($contents, true); - if (isset($contents['last-modified'])) { - $response = $this->fetchFileIfLastModified($url, $cacheKey, $contents['last-modified']); - if (true === $response) { - $packages = $contents; - } elseif ($response) { - $packages = $response; - } - } + if (!$useLastModifiedCheck && $hash && $this->cache->sha256($cacheKey) === $hash) { + $packages = json_decode($this->cache->read($cacheKey), true); + $packagesSource = 'cached file ('.$cacheKey.' originating from '.Url::sanitize($url).')'; + } elseif ($useLastModifiedCheck) { + if ($contents = $this->cache->read($cacheKey)) { + $contents = json_decode($contents, true); + // we already loaded some packages from this file, so assume it is fresh and avoid fetching it again + if (isset($alreadyLoaded[$name])) { + $packages = $contents; + $packagesSource = 'cached file ('.$cacheKey.' originating from '.Url::sanitize($url).')'; + } elseif (isset($contents['last-modified'])) { + $response = $this->fetchFileIfLastModified($url, $cacheKey, $contents['last-modified']); + $packages = true === $response ? $contents : $response; + $packagesSource = true === $response ? 'cached file ('.$cacheKey.' originating from '.Url::sanitize($url).')' : 'downloaded file ('.Url::sanitize($url).')'; } } } @@ -364,10 +619,15 @@ public function whatProvides(Pool $pool, $name, $bypassFilters = false) if (!$packages) { try { $packages = $this->fetchFile($url, $cacheKey, $hash, $useLastModifiedCheck); + $packagesSource = 'downloaded file ('.Url::sanitize($url).')'; } catch (TransportException $e) { // 404s are acceptable for lazy provider repos - if ($e->getStatusCode() === 404 && $this->lazyProvidersUrl) { + if ($this->lazyProvidersUrl && in_array($e->getStatusCode(), array(404, 499), true)) { $packages = array('packages' => array()); + $packagesSource = 'not-found file ('.Url::sanitize($url).')'; + if ($e->getStatusCode() === 499) { + $this->io->error('' . $e->getMessage() . ''); + } } else { throw $e; } @@ -377,84 +637,62 @@ public function whatProvides(Pool $pool, $name, $bypassFilters = false) $loadingPartialPackage = false; } else { $packages = array('packages' => array('versions' => $this->partialPackagesByName[$name])); + $packagesSource = 'root file ('.Url::sanitize($this->getPackagesJsonUrl()).')'; $loadingPartialPackage = true; } - $this->providers[$name] = array(); + $result = array(); + $versionsToLoad = array(); foreach ($packages['packages'] as $versions) { foreach ($versions as $version) { - if (!$loadingPartialPackage && $this->hasPartialPackages && isset($this->partialPackagesByName[$version['name']])) { + $normalizedName = strtolower($version['name']); + + // only load the actual named package, not other packages that might find themselves in the same file + if ($normalizedName !== $name) { continue; } - // avoid loading the same objects twice - if (isset($this->providersByUid[$version['uid']])) { - // skip if already assigned - if (!isset($this->providers[$name][$version['uid']])) { - // expand alias in two packages - if ($this->providersByUid[$version['uid']] instanceof AliasPackage) { - $this->providers[$name][$version['uid']] = $this->providersByUid[$version['uid']]->getAliasOf(); - $this->providers[$name][$version['uid'].'-alias'] = $this->providersByUid[$version['uid']]; - } else { - $this->providers[$name][$version['uid']] = $this->providersByUid[$version['uid']]; - } - // check for root aliases - if (isset($this->providersByUid[$version['uid'].'-root'])) { - $this->providers[$name][$version['uid'].'-root'] = $this->providersByUid[$version['uid'].'-root']; - } - } - } else { - if (!$bypassFilters && !$pool->isPackageAcceptable(strtolower($version['name']), VersionParser::parseStability($version['version']))) { - continue; - } - - // load acceptable packages in the providers - $package = $this->createPackage($version, 'Composer\Package\CompletePackage'); - $package->setRepository($this); - - if ($package instanceof AliasPackage) { - $aliased = $package->getAliasOf(); - $aliased->setRepository($this); - - $this->providers[$name][$version['uid']] = $aliased; - $this->providers[$name][$version['uid'].'-alias'] = $package; + if (!$loadingPartialPackage && $this->hasPartialPackages() && isset($this->partialPackagesByName[$normalizedName])) { + continue; + } - // override provider with its alias so it can be expanded in the if block above - $this->providersByUid[$version['uid']] = $package; - } else { - $this->providers[$name][$version['uid']] = $package; - $this->providersByUid[$version['uid']] = $package; + if (!isset($versionsToLoad[$version['uid']])) { + if (!isset($version['version_normalized'])) { + $version['version_normalized'] = $this->versionParser->normalize($version['version']); + } elseif ($version['version_normalized'] === VersionParser::DEFAULT_BRANCH_ALIAS) { + // handling of existing repos which need to remain composer v1 compatible, in case the version_normalized contained VersionParser::DEFAULT_BRANCH_ALIAS, we renormalize it + $version['version_normalized'] = $this->versionParser->normalize($version['version']); } - // handle root package aliases - unset($rootAliasData); - - if (isset($this->rootAliases[$package->getName()][$package->getVersion()])) { - $rootAliasData = $this->rootAliases[$package->getName()][$package->getVersion()]; - } elseif ($package instanceof AliasPackage && isset($this->rootAliases[$package->getName()][$package->getAliasOf()->getVersion()])) { - $rootAliasData = $this->rootAliases[$package->getName()][$package->getAliasOf()->getVersion()]; + // avoid loading packages which have already been loaded + if (isset($alreadyLoaded[$name][$version['version_normalized']])) { + continue; } - if (isset($rootAliasData)) { - $alias = $this->createAliasPackage($package, $rootAliasData['alias_normalized'], $rootAliasData['alias']); - $alias->setRepository($this); - - $this->providers[$name][$version['uid'].'-root'] = $alias; - $this->providersByUid[$version['uid'].'-root'] = $alias; + if ($this->isVersionAcceptable(null, $normalizedName, $version, $acceptableStabilities, $stabilityFlags)) { + $versionsToLoad[$version['uid']] = $version; } } } } - $result = $this->providers[$name]; + // load acceptable packages in the providers + $loadedPackages = $this->createPackages($versionsToLoad, $packagesSource); + $uids = array_keys($versionsToLoad); + + foreach ($loadedPackages as $index => $package) { + $package->setRepository($this); + $uid = $uids[$index]; - // clean up the cache because otherwise using this puts the repo in an inconsistent state with a polluted unfiltered cache - // which is likely not an issue but might cause hard to track behaviors depending on how the repo is used - if ($bypassFilters) { - foreach ($this->providers[$name] as $uid => $provider) { - unset($this->providersByUid[$uid]); + if ($package instanceof AliasPackage) { + $aliased = $package->getAliasOf(); + $aliased->setRepository($this); + + $result[$uid] = $aliased; + $result[$uid.'-alias'] = $package; + } else { + $result[$uid] = $package; } - unset($this->providers[$name]); } return $result; @@ -469,8 +707,8 @@ protected function initialize() $repoData = $this->loadDataFromServer(); - foreach ($repoData as $package) { - $this->addPackage($this->createPackage($package, 'Composer\Package\CompletePackage')); + foreach ($this->createPackages($repoData, 'root file ('.Url::sanitize($this->getPackagesJsonUrl()).')') as $package) { + $this->addPackage($package); } } @@ -485,25 +723,160 @@ public function addPackage(PackageInterface $package) $this->configurePackageTransportOptions($package); } - protected function loadRootServerFile() + /** + * @param array $packageNames array of package name => ConstraintInterface|null - if a constraint is provided, only packages matching it will be loaded + */ + private function loadAsyncPackages(array $packageNames, array $acceptableStabilities = null, array $stabilityFlags = null, array $alreadyLoaded = array()) { - if (null !== $this->rootData) { - return $this->rootData; + $this->loadRootServerFile(); + + $packages = array(); + $namesFound = array(); + $promises = array(); + $repo = $this; + + if (!$this->lazyProvidersUrl) { + throw new \LogicException('loadAsyncPackages only supports v2 protocol composer repos with a metadata-url'); } - if (!extension_loaded('openssl') && 'https' === substr($this->url, 0, 5)) { - throw new \RuntimeException('You must enable the openssl extension in your php.ini to load information from '.$this->url); + // load ~dev versions of the packages as well if needed + foreach ($packageNames as $name => $constraint) { + if ($acceptableStabilities === null || $stabilityFlags === null || StabilityFilter::isPackageAcceptable($acceptableStabilities, $stabilityFlags, array($name), 'dev')) { + $packageNames[$name.'~dev'] = $constraint; + } + // if only dev stability is requested, we skip loading the non dev file + if (isset($acceptableStabilities['dev']) && count($acceptableStabilities) === 1 && count($stabilityFlags) === 0) { + unset($packageNames[$name]); + } + } + + foreach ($packageNames as $name => $constraint) { + $name = strtolower($name); + + $realName = preg_replace('{~dev$}', '', $name); + // skip platform packages, root package and composer-plugin-api + if (PlatformRepository::isPlatformPackage($realName) || '__root__' === $realName) { + continue; + } + + $url = str_replace('%package%', $name, $this->lazyProvidersUrl); + $cacheKey = 'provider-'.strtr($name, '/', '~').'.json'; + + $lastModified = null; + if ($contents = $this->cache->read($cacheKey)) { + $contents = json_decode($contents, true); + $lastModified = isset($contents['last-modified']) ? $contents['last-modified'] : null; + } + + $promises[] = $this->asyncFetchFile($url, $cacheKey, $lastModified) + ->then(function ($response) use (&$packages, &$namesFound, $url, $cacheKey, $contents, $realName, $constraint, $repo, $acceptableStabilities, $stabilityFlags, $alreadyLoaded) { + $packagesSource = 'downloaded file ('.Url::sanitize($url).')'; + + if (true === $response) { + $packagesSource = 'cached file ('.$cacheKey.' originating from '.Url::sanitize($url).')'; + $response = $contents; + } + + if (!isset($response['packages'][$realName])) { + return; + } + + $versions = $response['packages'][$realName]; + + if (isset($response['minified']) && $response['minified'] === 'composer/2.0') { + $versions = MetadataMinifier::expand($versions); + } + + $namesFound[$realName] = true; + $versionsToLoad = array(); + foreach ($versions as $version) { + if (!isset($version['version_normalized'])) { + $version['version_normalized'] = $repo->versionParser->normalize($version['version']); + } elseif ($version['version_normalized'] === VersionParser::DEFAULT_BRANCH_ALIAS) { + // handling of existing repos which need to remain composer v1 compatible, in case the version_normalized contained VersionParser::DEFAULT_BRANCH_ALIAS, we renormalize it + $version['version_normalized'] = $repo->versionParser->normalize($version['version']); + } + + // avoid loading packages which have already been loaded + if (isset($alreadyLoaded[$realName][$version['version_normalized']])) { + continue; + } + + if ($repo->isVersionAcceptable($constraint, $realName, $version, $acceptableStabilities, $stabilityFlags)) { + $versionsToLoad[] = $version; + } + } + + $loadedPackages = $repo->createPackages($versionsToLoad, $packagesSource); + foreach ($loadedPackages as $package) { + $package->setRepository($repo); + $packages[spl_object_hash($package)] = $package; + + if ($package instanceof AliasPackage && !isset($packages[spl_object_hash($package->getAliasOf())])) { + $package->getAliasOf()->setRepository($repo); + $packages[spl_object_hash($package->getAliasOf())] = $package->getAliasOf(); + } + } + }); + } + + $this->loop->wait($promises); + + return array('namesFound' => $namesFound, 'packages' => $packages); + // RepositorySet should call loadMetadata, getMetadata when all promises resolved, then metadataComplete when done so we can GC the loaded json and whatnot then as needed + } + + /** + * TODO v3 should make this private once we can drop PHP 5.3 support + * + * @param string $name package name (must be lowercased already) + * @private + */ + public function isVersionAcceptable($constraint, $name, $versionData, array $acceptableStabilities = null, array $stabilityFlags = null) + { + $versions = array($versionData['version_normalized']); + + if ($alias = $this->loader->getBranchAlias($versionData)) { + $versions[] = $alias; } + foreach ($versions as $version) { + if (null !== $acceptableStabilities && null !== $stabilityFlags && !StabilityFilter::isPackageAcceptable($acceptableStabilities, $stabilityFlags, array($name), VersionParser::parseStability($version))) { + continue; + } + + if ($constraint && !CompilingMatcher::match($constraint, Constraint::OP_EQ, $version)) { + continue; + } + + return true; + } + + return false; + } + + private function getPackagesJsonUrl() + { $jsonUrlParts = parse_url($this->url); if (isset($jsonUrlParts['path']) && false !== strpos($jsonUrlParts['path'], '.json')) { - $jsonUrl = $this->url; - } else { - $jsonUrl = $this->url . '/packages.json'; + return $this->url; + } + + return $this->url . '/packages.json'; + } + + protected function loadRootServerFile() + { + if (null !== $this->rootData) { + return $this->rootData; + } + + if (!extension_loaded('openssl') && strpos($this->url, 'https') === 0) { + throw new \RuntimeException('You must enable the openssl extension in your php.ini to load information from '.$this->url); } - $data = $this->fetchFile($jsonUrl, 'packages.json'); + $data = $this->fetchFile($this->getPackagesJsonUrl(), 'packages.json'); if (!empty($data['notify-batch'])) { $this->notifyUrl = $this->canonicalizeUrl($data['notify-batch']); @@ -539,6 +912,46 @@ protected function loadRootServerFile() $this->hasPartialPackages = !empty($data['packages']) && is_array($data['packages']); } + // Horrible hack to workaround https://github.com/composer/composer/issues/9297 and bad mirrors, should be disabled if possible once they fix things + if (!empty($data['metadata-url']) && !empty($data['list']) && $data['metadata-url'] === '/p/%package%.json' && $data['list'] === 'https://packagist.org/packages/list.json') { + $this->io->writeError('Composer 2 repository support for '.$this->url.' has been disabled due to what seems like a misconfiguration. If this is a packagist.org mirror we recommend removing it as Composer 2 handles network operations much faster and should work fine without.'); + unset($data['metadata-url']); + } + + // metadata-url indicates V2 repo protocol so it takes over from all the V1 types + // V2 only has lazyProviders and possibly partial packages, but no ability to process anything else, + // V2 also supports async loading + if (!empty($data['metadata-url'])) { + $this->lazyProvidersUrl = $this->canonicalizeUrl($data['metadata-url']); + $this->providersUrl = null; + $this->hasProviders = false; + $this->hasPartialPackages = !empty($data['packages']) && is_array($data['packages']); + $this->allowSslDowngrade = false; + + // provides a list of package names that are available in this repo + // this disables lazy-provider behavior in the sense that if a list is available we assume it is finite and won't search for other packages in that repo + // while if no list is there lazyProvidersUrl is used when looking for any package name to see if the repo knows it + if (!empty($data['available-packages'])) { + $availPackages = array_map('strtolower', $data['available-packages']); + $this->availablePackages = array_combine($availPackages, $availPackages); + $this->hasAvailablePackageList = true; + } + + // Provides a list of package name patterns (using * wildcards to match any substring, e.g. "vendor/*") that are available in this repo + // Disables lazy-provider behavior as with available-packages, but may allow much more compact expression of packages covered by this repository. + // Over-specifying covered packages is safe, but may result in increased traffic to your repository. + if (!empty($data['available-package-patterns'])) { + $this->availablePackagePatterns = array_map(function ($pattern) { + return BasePackage::packageNameToRegexp($pattern); + }, $data['available-package-patterns']); + $this->hasAvailablePackageList = true; + } + + // Remove legacy keys as most repos need to be compatible with Composer v1 + // as well but we are not interested in the old format anymore at this point + unset($data['providers-url'], $data['providers'], $data['providers-includes']); + } + if ($this->allowSslDowngrade) { $this->url = str_replace('https://', 'http://', $this->url); $this->baseUrl = str_replace('https://', 'http://', $this->baseUrl); @@ -549,25 +962,22 @@ protected function loadRootServerFile() $this->hasProviders = true; } + if (!empty($data['list'])) { + $this->listUrl = $this->canonicalizeUrl($data['list']); + } + if (!empty($data['providers']) || !empty($data['providers-includes'])) { $this->hasProviders = true; } - // force values for packagist - if (preg_match('{^https?://repo\.packagist\.org/?$}i', $this->url) && !empty($this->repoConfig['force-lazy-providers'])) { - $this->url = 'https://repo.packagist.org'; - $this->baseUrl = 'https://repo.packagist.org'; - $this->lazyProvidersUrl = $this->canonicalizeUrl('https://repo.packagist.org/p/%package%.json'); - $this->providersUrl = null; - } elseif (!empty($this->repoConfig['force-lazy-providers'])) { - $this->lazyProvidersUrl = $this->canonicalizeUrl('/p/%package%.json'); - $this->providersUrl = null; + if (!empty($data['providers-api'])) { + $this->providersApiUrl = $this->canonicalizeUrl($data['providers-api']); } return $this->rootData = $data; } - protected function canonicalizeUrl($url) + private function canonicalizeUrl($url) { if ('/' === $url[0]) { if (preg_match('{^[^:]++://[^/]*+}', $this->url, $matches)) { @@ -580,14 +990,23 @@ protected function canonicalizeUrl($url) return $url; } - protected function loadDataFromServer() + private function loadDataFromServer() { $data = $this->loadRootServerFile(); return $this->loadIncludes($data); } - protected function loadProviderListings($data) + private function hasPartialPackages() + { + if ($this->hasPartialPackages && null === $this->partialPackagesByName) { + $this->initializePartialPackages(); + } + + return $this->hasPartialPackages; + } + + private function loadProviderListings($data) { if (isset($data['providers'])) { if (!is_array($this->providerListing)) { @@ -612,7 +1031,7 @@ protected function loadProviderListings($data) } } - protected function loadIncludes($data) + private function loadIncludes($data) { $packages = array(); @@ -649,23 +1068,38 @@ protected function loadIncludes($data) return $packages; } - protected function createPackage(array $data, $class = 'Composer\Package\CompletePackage') + /** + * TODO v3 should make this private once we can drop PHP 5.3 support + * + * @private + * @return list + */ + public function createPackages(array $packages, $source = null) { + if (!$packages) { + return array(); + } + try { - if (!isset($data['notification-url'])) { - $data['notification-url'] = $this->notifyUrl; + foreach ($packages as &$data) { + if (!isset($data['notification-url'])) { + $data['notification-url'] = $this->notifyUrl; + } } - $package = $this->loader->load($data, $class); - if (isset($this->sourceMirrors[$package->getSourceType()])) { - $package->setSourceMirrors($this->sourceMirrors[$package->getSourceType()]); + $packageInstances = $this->loader->loadPackages($packages); + + foreach ($packageInstances as $package) { + if (isset($this->sourceMirrors[$package->getSourceType()])) { + $package->setSourceMirrors($this->sourceMirrors[$package->getSourceType()]); + } + $package->setDistMirrors($this->distMirrors); + $this->configurePackageTransportOptions($package); } - $package->setDistMirrors($this->distMirrors); - $this->configurePackageTransportOptions($package); - return $package; + return $packageInstances; } catch (\Exception $e) { - throw new \RuntimeException('Could not load package '.(isset($data['name']) ? $data['name'] : json_encode($data)).' in '.$this->url.': ['.get_class($e).'] '.$e->getMessage(), 0, $e); + throw new \RuntimeException('Could not load packages '.(isset($packages[0]['name']) ? $packages[0]['name'] : json_encode($packages)).' in '.$this->getRepoName().($source ? ' from '.$source : '').': ['.get_class($e).'] '.$e->getMessage(), 0, $e); } } @@ -677,22 +1111,24 @@ protected function fetchFile($filename, $cacheKey = null, $sha256 = null, $store } // url-encode $ signs in URLs as bad proxies choke on them - if (($pos = strpos($filename, '$')) && preg_match('{^https?://.*}i', $filename)) { + if (($pos = strpos($filename, '$')) && preg_match('{^https?://}i', $filename)) { $filename = substr($filename, 0, $pos) . '%24' . substr($filename, $pos + 1); } $retries = 3; while ($retries--) { try { - $preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->rfs, $filename); + $options = $this->options; if ($this->eventDispatcher) { + $preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->httpDownloader, $filename, 'metadata', array('repository' => $this)); + $preFileDownloadEvent->setTransportOptions($this->options); $this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent); + $filename = $preFileDownloadEvent->getProcessedUrl(); + $options = $preFileDownloadEvent->getTransportOptions(); } - $origin = RemoteFilesystem::getOrigin($filename); - $rfs = $preFileDownloadEvent->getRemoteFilesystem(); - - $json = $rfs->getContents($origin, $filename, false); + $response = $this->httpDownloader->get($filename, $options); + $json = $response->getBody(); if ($sha256 && $sha256 !== hash('sha256', $json)) { // undo downgrade before trying again if http seems to be hijacked or modifying content somehow if ($this->allowSslDowngrade) { @@ -711,12 +1147,17 @@ protected function fetchFile($filename, $cacheKey = null, $sha256 = null, $store throw new RepositorySecurityException('The contents of '.$filename.' do not match its signature. This could indicate a man-in-the-middle attack or e.g. antivirus software corrupting files. Try running composer again and report this if you think it is a mistake.'); } - $data = JsonFile::parseJson($json, $filename); - RemoteFilesystem::outputWarnings($this->io, $this->url, $data); + if ($this->eventDispatcher) { + $postFileDownloadEvent = new PostFileDownloadEvent(PluginEvents::POST_FILE_DOWNLOAD, null, $sha256, $filename, 'metadata', array('response' => $response, 'repository' => $this)); + $this->eventDispatcher->dispatch($postFileDownloadEvent->getName(), $postFileDownloadEvent); + } + + $data = $response->decodeJson(); + HttpDownloader::outputWarnings($this->io, $this->url, $data); - if ($cacheKey) { + if ($cacheKey && !$this->cache->isReadOnly()) { if ($storeLastModifiedTime) { - $lastModifiedDate = $rfs->findHeaderValue($rfs->getLastHeaders(), 'last-modified'); + $lastModifiedDate = $response->getHeader('last-modified'); if ($lastModifiedDate) { $data['last-modified'] = $lastModifiedDate; $json = json_encode($data); @@ -725,12 +1166,26 @@ protected function fetchFile($filename, $cacheKey = null, $sha256 = null, $store $this->cache->write($cacheKey, $json); } + $response->collect(); + break; } catch (\Exception $e) { + if ($e instanceof \LogicException) { + throw $e; + } + if ($e instanceof TransportException && $e->getStatusCode() === 404) { throw $e; } + // try to detect offline state (if dns resolution fails it is pretty likely to keep failing) and avoid retrying in that case + if ($e instanceof TransportException && $e->getStatusCode() === null) { + $responseInfo = $e->getResponseInfo(); + if (isset($responseInfo['namelookup_time']) && $responseInfo['namelookup_time'] == 0) { + $retries = 0; + } + } + if ($retries) { usleep(100000); continue; @@ -742,8 +1197,7 @@ protected function fetchFile($filename, $cacheKey = null, $sha256 = null, $store if ($cacheKey && ($contents = $this->cache->read($cacheKey))) { if (!$this->degradedMode) { - $this->io->writeError(''.$e->getMessage().''); - $this->io->writeError(''.$this->url.' could not be fully loaded, package information was loaded from the local cache and may be out of date'); + $this->io->writeError(''.$this->url.' could not be fully loaded ('.$e->getMessage().'), package information was loaded from the local cache and may be out of date'); } $this->degradedMode = true; $data = JsonFile::parseJson($contents, $this->cache->getRoot().$cacheKey); @@ -755,39 +1209,61 @@ protected function fetchFile($filename, $cacheKey = null, $sha256 = null, $store } } + if (!isset($data)) { + throw new \LogicException("ComposerRepository: Undefined \$data. Please report at https://github.com/composer/composer/issues/new."); + } + return $data; } - protected function fetchFileIfLastModified($filename, $cacheKey, $lastModifiedTime) + private function fetchFileIfLastModified($filename, $cacheKey, $lastModifiedTime) { $retries = 3; while ($retries--) { try { - $preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->rfs, $filename); + $options = $this->options; if ($this->eventDispatcher) { + $preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->httpDownloader, $filename, 'metadata', array('repository' => $this)); + $preFileDownloadEvent->setTransportOptions($this->options); $this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent); + $filename = $preFileDownloadEvent->getProcessedUrl(); + $options = $preFileDownloadEvent->getTransportOptions(); } - $origin = RemoteFilesystem::getOrigin($filename); - $rfs = $preFileDownloadEvent->getRemoteFilesystem(); - $options = array('http' => array('header' => array('If-Modified-Since: '.$lastModifiedTime))); - $json = $rfs->getContents($origin, $filename, false, $options); - if ($json === '' && $rfs->findStatusCode($rfs->getLastHeaders()) === 304) { + if (isset($options['http']['header'])) { + $options['http']['header'] = (array) $options['http']['header']; + } + $options['http']['header'][] = 'If-Modified-Since: '.$lastModifiedTime; + $response = $this->httpDownloader->get($filename, $options); + $json = $response->getBody(); + if ($json === '' && $response->getStatusCode() === 304) { return true; } - $data = JsonFile::parseJson($json, $filename); - RemoteFilesystem::outputWarnings($this->io, $this->url, $data); + if ($this->eventDispatcher) { + $postFileDownloadEvent = new PostFileDownloadEvent(PluginEvents::POST_FILE_DOWNLOAD, null, null, $filename, 'metadata', array('response' => $response, 'repository' => $this)); + $this->eventDispatcher->dispatch($postFileDownloadEvent->getName(), $postFileDownloadEvent); + } + + $data = $response->decodeJson(); + HttpDownloader::outputWarnings($this->io, $this->url, $data); - $lastModifiedDate = $rfs->findHeaderValue($rfs->getLastHeaders(), 'last-modified'); + $lastModifiedDate = $response->getHeader('last-modified'); + $response->collect(); if ($lastModifiedDate) { $data['last-modified'] = $lastModifiedDate; $json = json_encode($data); } - $this->cache->write($cacheKey, $json); + if (!$this->cache->isReadOnly()) { + $this->cache->write($cacheKey, $json); + } return $data; } catch (\Exception $e) { + if ($e instanceof \LogicException) { + throw $e; + } + if ($e instanceof TransportException && $e->getStatusCode() === 404) { throw $e; } @@ -798,8 +1274,7 @@ protected function fetchFileIfLastModified($filename, $cacheKey, $lastModifiedTi } if (!$this->degradedMode) { - $this->io->writeError(''.$e->getMessage().''); - $this->io->writeError(''.$this->url.' could not be fully loaded, package information was loaded from the local cache and may be out of date'); + $this->io->writeError(''.$this->url.' could not be fully loaded ('.$e->getMessage().'), package information was loaded from the local cache and may be out of date'); } $this->degradedMode = true; @@ -808,6 +1283,127 @@ protected function fetchFileIfLastModified($filename, $cacheKey, $lastModifiedTi } } + private function asyncFetchFile($filename, $cacheKey, $lastModifiedTime = null) + { + $retries = 3; + + if (isset($this->packagesNotFoundCache[$filename])) { + return \React\Promise\resolve(array('packages' => array())); + } + + if (isset($this->freshMetadataUrls[$filename]) && $lastModifiedTime) { + // make it look like we got a 304 response + return \React\Promise\resolve(true); + } + + $httpDownloader = $this->httpDownloader; + $options = $this->options; + if ($this->eventDispatcher) { + $preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->httpDownloader, $filename, 'metadata', array('repository' => $this)); + $preFileDownloadEvent->setTransportOptions($this->options); + $this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent); + $filename = $preFileDownloadEvent->getProcessedUrl(); + $options = $preFileDownloadEvent->getTransportOptions(); + } + + if ($lastModifiedTime) { + if (isset($options['http']['header'])) { + $options['http']['header'] = (array) $options['http']['header']; + } + $options['http']['header'][] = 'If-Modified-Since: '.$lastModifiedTime; + } + + $io = $this->io; + $url = $this->url; + $cache = $this->cache; + $degradedMode = &$this->degradedMode; + $eventDispatcher = $this->eventDispatcher; + $repo = $this; + + $accept = function ($response) use ($io, $url, $filename, $cache, $cacheKey, $eventDispatcher, $repo) { + // package not found is acceptable for a v2 protocol repository + if ($response->getStatusCode() === 404) { + $repo->packagesNotFoundCache[$filename] = true; + + return array('packages' => array()); + } + + $json = $response->getBody(); + if ($json === '' && $response->getStatusCode() === 304) { + $repo->freshMetadataUrls[$filename] = true; + + return true; + } + + if ($eventDispatcher) { + $postFileDownloadEvent = new PostFileDownloadEvent(PluginEvents::POST_FILE_DOWNLOAD, null, null, $filename, 'metadata', array('response' => $response, 'repository' => $repo)); + $eventDispatcher->dispatch($postFileDownloadEvent->getName(), $postFileDownloadEvent); + } + + $data = $response->decodeJson(); + HttpDownloader::outputWarnings($io, $url, $data); + + $lastModifiedDate = $response->getHeader('last-modified'); + $response->collect(); + if ($lastModifiedDate) { + $data['last-modified'] = $lastModifiedDate; + $json = JsonFile::encode($data, JsonFile::JSON_UNESCAPED_SLASHES | JsonFile::JSON_UNESCAPED_UNICODE); + } + if (!$cache->isReadOnly()) { + $cache->write($cacheKey, $json); + } + $repo->freshMetadataUrls[$filename] = true; + + return $data; + }; + + $reject = function ($e) use (&$retries, $httpDownloader, $filename, $options, &$reject, $accept, $io, $url, &$degradedMode, $repo, $lastModifiedTime) { + if ($e instanceof TransportException && $e->getStatusCode() === 404) { + $repo->packagesNotFoundCache[$filename] = true; + + return false; + } + + // special error code returned when network is being artificially disabled + if ($e instanceof TransportException && $e->getStatusCode() === 499) { + $retries = 0; + } + + // try to detect offline state (if dns resolution fails it is pretty likely to keep failing) and avoid retrying in that case + if ($e instanceof TransportException && $e->getStatusCode() === null) { + $responseInfo = $e->getResponseInfo(); + if (isset($responseInfo['namelookup_time']) && $responseInfo['namelookup_time'] == 0) { + $retries = 0; + } + } + + if (--$retries > 0) { + usleep(100000); + + return $httpDownloader->add($filename, $options)->then($accept, $reject); + } + + if (!$degradedMode) { + $io->writeError(''.$url.' could not be fully loaded ('.$e->getMessage().'), package information was loaded from the local cache and may be out of date'); + } + $degradedMode = true; + + // if the file is in the cache, we fake a 304 Not Modified to allow the process to continue + if ($lastModifiedTime) { + return $accept(new Response(array('url' => $url), 304, array(), '')); + } + + // special error code returned when network is being artificially disabled + if ($e instanceof TransportException && $e->getStatusCode() === 499) { + return $accept(new Response(array('url' => $url), 404, array(), '')); + } + + throw $e; + }; + + return $httpDownloader->add($filename, $options)->then($accept, $reject); + } + /** * This initializes the packages key of a partial packages.json that contain some packages inlined + a providers-lazy-url * @@ -819,23 +1415,39 @@ private function initializePartialPackages() $this->partialPackagesByName = array(); foreach ($rootData['packages'] as $package => $versions) { - $package = strtolower($package); foreach ($versions as $version) { - $this->partialPackagesByName[$package][] = $version; - if (!empty($version['provide']) && is_array($version['provide'])) { - foreach ($version['provide'] as $provided => $providedVersion) { - $this->partialPackagesByName[strtolower($provided)][] = $version; - } - } - if (!empty($version['replace']) && is_array($version['replace'])) { - foreach ($version['replace'] as $provided => $providedVersion) { - $this->partialPackagesByName[strtolower($provided)][] = $version; - } - } + $this->partialPackagesByName[strtolower($version['name'])][] = $version; } } // wipe rootData as it is fully consumed at this point and this saves some memory $this->rootData = true; } + + /** + * Checks if the package name is present in this lazy providers repo + * + * @param string $name + * @return bool true if the package name is present in availablePackages or matched by availablePackagePatterns + */ + protected function lazyProvidersRepoContains($name) + { + if (!$this->hasAvailablePackageList) { + throw new \LogicException('lazyProvidersRepoContains should not be called unless hasAvailablePackageList is true'); + } + + if (is_array($this->availablePackages) && isset($this->availablePackages[$name])) { + return true; + } + + if (is_array($this->availablePackagePatterns)) { + foreach ($this->availablePackagePatterns as $providerRegex) { + if (preg_match($providerRegex, $name)) { + return true; + } + } + } + + return false; + } } diff --git a/app/vendor/composer/composer/src/Composer/Repository/CompositeRepository.php b/app/vendor/composer/composer/src/Composer/Repository/CompositeRepository.php index ce57504f0..8426364a5 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/CompositeRepository.php +++ b/app/vendor/composer/composer/src/Composer/Repository/CompositeRepository.php @@ -19,17 +19,17 @@ * * @author Beau Simensen */ -class CompositeRepository extends BaseRepository +class CompositeRepository implements RepositoryInterface { /** * List of repositories - * @var array + * @var RepositoryInterface[] */ private $repositories; /** * Constructor - * @param array $repositories + * @param RepositoryInterface[] $repositories */ public function __construct(array $repositories) { @@ -39,6 +39,13 @@ public function __construct(array $repositories) } } + public function getRepoName() + { + return 'composite repo ('.implode(', ', array_map(function ($repo) { + return $repo->getRepoName(); + }, $this->repositories)).')'; + } + /** * Returns all the wrapped repositories * @@ -94,6 +101,26 @@ public function findPackages($name, $constraint = null) return $packages ? call_user_func_array('array_merge', $packages) : array(); } + /** + * {@inheritDoc} + */ + public function loadPackages(array $packageMap, array $acceptableStabilities, array $stabilityFlags, array $alreadyLoaded = array()) + { + $packages = array(); + $namesFound = array(); + foreach ($this->repositories as $repository) { + /* @var $repository RepositoryInterface */ + $result = $repository->loadPackages($packageMap, $acceptableStabilities, $stabilityFlags, $alreadyLoaded); + $packages[] = $result['packages']; + $namesFound[] = $result['namesFound']; + } + + return array( + 'packages' => $packages ? call_user_func_array('array_merge', $packages) : array(), + 'namesFound' => $namesFound ? array_unique(call_user_func_array('array_merge', $namesFound)) : array(), + ); + } + /** * {@inheritdoc} */ @@ -125,17 +152,33 @@ public function getPackages() /** * {@inheritdoc} */ - public function removePackage(PackageInterface $package) + public function getProviders($packageName) { + $results = array(); foreach ($this->repositories as $repository) { /* @var $repository RepositoryInterface */ - $repository->removePackage($package); + $results[] = $repository->getProviders($packageName); + } + + return $results ? call_user_func_array('array_merge', $results) : array(); + } + + /** + * {@inheritdoc} + */ + public function removePackage(PackageInterface $package) + { + foreach ($this->repositories as $repository) { + if ($repository instanceof WritableRepositoryInterface) { + $repository->removePackage($package); + } } } /** * {@inheritdoc} */ + #[\ReturnTypeWillChange] public function count() { $total = 0; diff --git a/app/vendor/composer/composer/src/Composer/Repository/FilesystemRepository.php b/app/vendor/composer/composer/src/Composer/Repository/FilesystemRepository.php index 204aa095d..debdb7a89 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/FilesystemRepository.php +++ b/app/vendor/composer/composer/src/Composer/Repository/FilesystemRepository.php @@ -14,7 +14,11 @@ use Composer\Json\JsonFile; use Composer\Package\Loader\ArrayLoader; +use Composer\Package\RootPackageInterface; +use Composer\Package\AliasPackage; use Composer\Package\Dumper\ArrayDumper; +use Composer\Installer\InstallationManager; +use Composer\Util\Filesystem; /** * Filesystem repository. @@ -24,17 +28,32 @@ */ class FilesystemRepository extends WritableArrayRepository { - private $file; + /** @var JsonFile */ + protected $file; + /** @var bool */ + private $dumpVersions; + /** @var ?RootPackageInterface */ + private $rootPackage; + /** @var Filesystem */ + private $filesystem; /** * Initializes filesystem repository. * - * @param JsonFile $repositoryFile repository json file + * @param JsonFile $repositoryFile repository json file + * @param bool $dumpVersions + * @param ?RootPackageInterface $rootPackage Must be provided if $dumpVersions is true */ - public function __construct(JsonFile $repositoryFile) + public function __construct(JsonFile $repositoryFile, $dumpVersions = false, RootPackageInterface $rootPackage = null, Filesystem $filesystem = null) { parent::__construct(); $this->file = $repositoryFile; + $this->dumpVersions = $dumpVersions; + $this->rootPackage = $rootPackage; + $this->filesystem = $filesystem ?: new Filesystem; + if ($dumpVersions && !$rootPackage) { + throw new \InvalidArgumentException('Expected a root package instance if $dumpVersions is true'); + } } /** @@ -49,11 +68,15 @@ protected function initialize() } try { - $packages = $this->file->read(); + $data = $this->file->read(); + if (isset($data['packages'])) { + $packages = $data['packages']; + } else { + $packages = $data; + } - // forward compatibility for composer v2 installed.json - if (isset($packages['packages'])) { - $packages = $packages['packages']; + if (isset($data['dev-package-names'])) { + $this->setDevPackageNames($data['dev-package-names']); } if (!is_array($packages)) { @@ -79,19 +102,200 @@ public function reload() /** * Writes writable repository. */ - public function write() + public function write($devMode, InstallationManager $installationManager) { - $data = array(); + $data = array('packages' => array(), 'dev' => $devMode, 'dev-package-names' => array()); $dumper = new ArrayDumper(); + // make sure the directory is created so we can realpath it + // as realpath() does some additional normalizations with network paths that normalizePath does not + // and we need to find shortest path correctly + $repoDir = dirname($this->file->getPath()); + $this->filesystem->ensureDirectoryExists($repoDir); + + $repoDir = $this->filesystem->normalizePath(realpath($repoDir)); + $installPaths = array(); + foreach ($this->getCanonicalPackages() as $package) { - $data[] = $dumper->dump($package); + $pkgArray = $dumper->dump($package); + $path = $installationManager->getInstallPath($package); + $installPath = null; + if ('' !== $path && null !== $path) { + $normalizedPath = $this->filesystem->normalizePath($this->filesystem->isAbsolutePath($path) ? $path : getcwd() . '/' . $path); + $installPath = $this->filesystem->findShortestPath($repoDir, $normalizedPath, true); + } + $installPaths[$package->getName()] = $installPath; + + $pkgArray['install-path'] = $installPath; + $data['packages'][] = $pkgArray; + + // only write to the files the names which are really installed, as we receive the full list + // of dev package names before they get installed during composer install + if (in_array($package->getName(), $this->devPackageNames, true)) { + $data['dev-package-names'][] = $package->getName(); + } } - usort($data, function ($a, $b) { + sort($data['dev-package-names']); + usort($data['packages'], function ($a, $b) { return strcmp($a['name'], $b['name']); }); $this->file->write($data); + + if ($this->dumpVersions) { + $versions = $this->generateInstalledVersions($installationManager, $installPaths, $devMode, $repoDir); + + $this->filesystem->filePutContentsIfModified($repoDir.'/installed.php', 'dumpToPhpCode($versions) . ';'."\n"); + $installedVersionsClass = file_get_contents(__DIR__.'/../InstalledVersions.php'); + $this->filesystem->filePutContentsIfModified($repoDir.'/InstalledVersions.php', $installedVersionsClass); + + \Composer\InstalledVersions::reload($versions); + } + } + + private function dumpToPhpCode(array $array = array(), $level = 0) + { + $lines = "array(\n"; + $level++; + + foreach ($array as $key => $value) { + $lines .= str_repeat(' ', $level); + $lines .= is_int($key) ? $key . ' => ' : '\'' . $key . '\' => '; + + if (is_array($value)) { + if (!empty($value)) { + $lines .= $this->dumpToPhpCode($value, $level); + } else { + $lines .= "array(),\n"; + } + } elseif ($key === 'install_path' && is_string($value)) { + if ($this->filesystem->isAbsolutePath($value)) { + $lines .= var_export($value, true) . ",\n"; + } else { + $lines .= "__DIR__ . " . var_export('/' . $value, true) . ",\n"; + } + } else { + $lines .= var_export($value, true) . ",\n"; + } + } + + $lines .= str_repeat(' ', $level - 1) . ')' . ($level - 1 == 0 ? '' : ",\n"); + + return $lines; + } + + /** + * @return ?array + */ + private function generateInstalledVersions(InstallationManager $installationManager, array $installPaths, $devMode, $repoDir) + { + if (!$this->dumpVersions) { + return null; + } + + $devPackages = array_flip($this->devPackageNames); + $versions = array('versions' => array()); + $packages = $this->getPackages(); + $packages[] = $rootPackage = $this->rootPackage; + while ($rootPackage instanceof AliasPackage) { + $rootPackage = $rootPackage->getAliasOf(); + $packages[] = $rootPackage; + } + + // add real installed packages + foreach ($packages as $package) { + if ($package instanceof AliasPackage) { + continue; + } + + $reference = null; + if ($package->getInstallationSource()) { + $reference = $package->getInstallationSource() === 'source' ? $package->getSourceReference() : $package->getDistReference(); + } + if (null === $reference) { + $reference = ($package->getSourceReference() ?: $package->getDistReference()) ?: null; + } + + if ($package instanceof RootPackageInterface) { + $to = $this->filesystem->normalizePath(realpath(getcwd())); + $installPath = $this->filesystem->findShortestPath($repoDir, $to, true); + } else { + $installPath = $installPaths[$package->getName()]; + } + + $versions['versions'][$package->getName()] = array( + 'pretty_version' => $package->getPrettyVersion(), + 'version' => $package->getVersion(), + 'type' => $package->getType(), + 'install_path' => $installPath, + 'aliases' => array(), + 'reference' => $reference, + 'dev_requirement' => isset($devPackages[$package->getName()]), + ); + if ($package instanceof RootPackageInterface) { + $versions['root'] = $versions['versions'][$package->getName()]; + unset($versions['root']['dev_requirement']); + $versions['root']['name'] = $package->getName(); + $versions['root']['dev'] = $devMode; + } + } + + // add provided/replaced packages + foreach ($packages as $package) { + $isDevPackage = isset($devPackages[$package->getName()]); + foreach ($package->getReplaces() as $replace) { + // exclude platform replaces as when they are really there we can not check for their presence + if (PlatformRepository::isPlatformPackage($replace->getTarget())) { + continue; + } + if (!isset($versions['versions'][$replace->getTarget()]['dev_requirement'])) { + $versions['versions'][$replace->getTarget()]['dev_requirement'] = $isDevPackage; + } elseif (!$isDevPackage) { + $versions['versions'][$replace->getTarget()]['dev_requirement'] = false; + } + $replaced = $replace->getPrettyConstraint(); + if ($replaced === 'self.version') { + $replaced = $package->getPrettyVersion(); + } + if (!isset($versions['versions'][$replace->getTarget()]['replaced']) || !in_array($replaced, $versions['versions'][$replace->getTarget()]['replaced'], true)) { + $versions['versions'][$replace->getTarget()]['replaced'][] = $replaced; + } + } + foreach ($package->getProvides() as $provide) { + // exclude platform provides as when they are really there we can not check for their presence + if (PlatformRepository::isPlatformPackage($provide->getTarget())) { + continue; + } + if (!isset($versions['versions'][$provide->getTarget()]['dev_requirement'])) { + $versions['versions'][$provide->getTarget()]['dev_requirement'] = $isDevPackage; + } elseif (!$isDevPackage) { + $versions['versions'][$provide->getTarget()]['dev_requirement'] = false; + } + $provided = $provide->getPrettyConstraint(); + if ($provided === 'self.version') { + $provided = $package->getPrettyVersion(); + } + if (!isset($versions['versions'][$provide->getTarget()]['provided']) || !in_array($provided, $versions['versions'][$provide->getTarget()]['provided'], true)) { + $versions['versions'][$provide->getTarget()]['provided'][] = $provided; + } + } + } + + // add aliases + foreach ($packages as $package) { + if (!$package instanceof AliasPackage) { + continue; + } + $versions['versions'][$package->getName()]['aliases'][] = $package->getPrettyVersion(); + if ($package instanceof RootPackageInterface) { + $versions['root']['aliases'][] = $package->getPrettyVersion(); + } + } + + ksort($versions['versions']); + ksort($versions); + + return $versions; } } diff --git a/app/vendor/composer/composer/src/Composer/Repository/FilterRepository.php b/app/vendor/composer/composer/src/Composer/Repository/FilterRepository.php new file mode 100644 index 000000000..8ce7be05f --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Repository/FilterRepository.php @@ -0,0 +1,206 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\Package\PackageInterface; +use Composer\Package\BasePackage; + +/** + * Filters which packages are seen as canonical on this repo by loadPackages + * + * @author Jordi Boggiano + */ +class FilterRepository implements RepositoryInterface +{ + /** @var ?string */ + private $only = null; + /** @var ?string */ + private $exclude = null; + /** @var bool */ + private $canonical = true; + /** @var RepositoryInterface */ + private $repo; + + public function __construct(RepositoryInterface $repo, array $options) + { + if (isset($options['only'])) { + if (!is_array($options['only'])) { + throw new \InvalidArgumentException('"only" key for repository '.$repo->getRepoName().' should be an array'); + } + $this->only = '{^(?:'.implode('|', array_map(function ($val) { + return BasePackage::packageNameToRegexp($val, '%s'); + }, $options['only'])) .')$}iD'; + } + if (isset($options['exclude'])) { + if (!is_array($options['exclude'])) { + throw new \InvalidArgumentException('"exclude" key for repository '.$repo->getRepoName().' should be an array'); + } + $this->exclude = '{^(?:'.implode('|', array_map(function ($val) { + return BasePackage::packageNameToRegexp($val, '%s'); + }, $options['exclude'])) .')$}iD'; + } + if ($this->exclude && $this->only) { + throw new \InvalidArgumentException('Only one of "only" and "exclude" can be specified for repository '.$repo->getRepoName()); + } + if (isset($options['canonical'])) { + if (!is_bool($options['canonical'])) { + throw new \InvalidArgumentException('"canonical" key for repository '.$repo->getRepoName().' should be a boolean'); + } + $this->canonical = $options['canonical']; + } + + $this->repo = $repo; + } + + public function getRepoName() + { + return $this->repo->getRepoName(); + } + + /** + * Returns the wrapped repositories + * + * @return RepositoryInterface + */ + public function getRepository() + { + return $this->repo; + } + + /** + * {@inheritdoc} + */ + public function hasPackage(PackageInterface $package) + { + return $this->repo->hasPackage($package); + } + + /** + * {@inheritdoc} + */ + public function findPackage($name, $constraint) + { + if (!$this->isAllowed($name)) { + return null; + } + + return $this->repo->findPackage($name, $constraint); + } + + /** + * {@inheritdoc} + */ + public function findPackages($name, $constraint = null) + { + if (!$this->isAllowed($name)) { + return array(); + } + + return $this->repo->findPackages($name, $constraint); + } + + /** + * {@inheritDoc} + */ + public function loadPackages(array $packageMap, array $acceptableStabilities, array $stabilityFlags, array $alreadyLoaded = array()) + { + foreach ($packageMap as $name => $constraint) { + if (!$this->isAllowed($name)) { + unset($packageMap[$name]); + } + } + + if (!$packageMap) { + return array('namesFound' => array(), 'packages' => array()); + } + + $result = $this->repo->loadPackages($packageMap, $acceptableStabilities, $stabilityFlags, $alreadyLoaded); + if (!$this->canonical) { + $result['namesFound'] = array(); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function search($query, $mode = 0, $type = null) + { + $result = array(); + + foreach ($this->repo->search($query, $mode, $type) as $package) { + if ($this->isAllowed($package['name'])) { + $result[] = $package; + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function getPackages() + { + $result = array(); + foreach ($this->repo->getPackages() as $package) { + if ($this->isAllowed($package->getName())) { + $result[] = $package; + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function getProviders($packageName) + { + $result = array(); + foreach ($this->repo->getProviders($packageName) as $name => $provider) { + if ($this->isAllowed($provider['name'])) { + $result[$name] = $provider; + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + #[\ReturnTypeWillChange] + public function count() + { + if ($this->repo->count() > 0) { + return count($this->getPackages()); + } + + return 0; + } + + private function isAllowed($name) + { + if (!$this->only && !$this->exclude) { + return true; + } + + if ($this->only) { + return (bool) preg_match($this->only, $name); + } + + return !preg_match($this->exclude, $name); + } +} diff --git a/app/vendor/composer/composer/src/Composer/Repository/InstalledArrayRepository.php b/app/vendor/composer/composer/src/Composer/Repository/InstalledArrayRepository.php index c801d49ea..e8a5f8194 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/InstalledArrayRepository.php +++ b/app/vendor/composer/composer/src/Composer/Repository/InstalledArrayRepository.php @@ -15,10 +15,24 @@ /** * Installed array repository. * - * This is used for serving the RootPackage inside an in-memory InstalledRepository + * This is used as an in-memory InstalledRepository mostly for testing purposes * * @author Jordi Boggiano */ class InstalledArrayRepository extends WritableArrayRepository implements InstalledRepositoryInterface { + public function getRepoName() + { + return 'installed '.parent::getRepoName(); + } + + /** + * {@inheritDoc} + */ + public function isFresh() + { + // this is not a completely correct implementation but there is no way to + // distinguish an empty repo and a newly created one given this is all in-memory + return $this->count() === 0; + } } diff --git a/app/vendor/composer/composer/src/Composer/Repository/InstalledFilesystemRepository.php b/app/vendor/composer/composer/src/Composer/Repository/InstalledFilesystemRepository.php index 1ff8a0a06..f526f31c2 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/InstalledFilesystemRepository.php +++ b/app/vendor/composer/composer/src/Composer/Repository/InstalledFilesystemRepository.php @@ -19,4 +19,16 @@ */ class InstalledFilesystemRepository extends FilesystemRepository implements InstalledRepositoryInterface { + public function getRepoName() + { + return 'installed '.parent::getRepoName(); + } + + /** + * {@inheritDoc} + */ + public function isFresh() + { + return !$this->file->exists(); + } } diff --git a/app/vendor/composer/composer/src/Composer/Repository/InstalledRepository.php b/app/vendor/composer/composer/src/Composer/Repository/InstalledRepository.php new file mode 100644 index 000000000..13c5fddbb --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Repository/InstalledRepository.php @@ -0,0 +1,266 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\Package\Version\VersionParser; +use Composer\Semver\Constraint\ConstraintInterface; +use Composer\Semver\Constraint\Constraint; +use Composer\Semver\Constraint\MatchAllConstraint; +use Composer\Package\RootPackageInterface; +use Composer\Package\Link; + +/** + * Installed repository is a composite of all installed repo types. + * + * The main use case is tagging a repo as an "installed" repository, and offering a way to get providers/replacers easily. + * + * Installed repos are LockArrayRepository, InstalledRepositoryInterface, RootPackageRepository and PlatformRepository + * + * @author Jordi Boggiano + */ +class InstalledRepository extends CompositeRepository +{ + public function findPackagesWithReplacersAndProviders($name, $constraint = null) + { + $name = strtolower($name); + + if (null !== $constraint && !$constraint instanceof ConstraintInterface) { + $versionParser = new VersionParser(); + $constraint = $versionParser->parseConstraints($constraint); + } + + $matches = array(); + foreach ($this->getRepositories() as $repo) { + foreach ($repo->getPackages() as $candidate) { + if ($name === $candidate->getName()) { + if (null === $constraint || $constraint->matches(new Constraint('==', $candidate->getVersion()))) { + $matches[] = $candidate; + } + continue; + } + + foreach (array_merge($candidate->getProvides(), $candidate->getReplaces()) as $link) { + if ( + $name === $link->getTarget() + && ($constraint === null || $link->getConstraint() === null || $constraint->matches($link->getConstraint())) + ) { + $matches[] = $candidate; + continue 2; + } + } + } + } + + return $matches; + } + + /** + * Returns a list of links causing the requested needle packages to be installed, as an associative array with the + * dependent's name as key, and an array containing in order the PackageInterface and Link describing the relationship + * as values. If recursive lookup was requested a third value is returned containing an identically formed array up + * to the root package. That third value will be false in case a circular recursion was detected. + * + * @param string|string[] $needle The package name(s) to inspect. + * @param ConstraintInterface|null $constraint Optional constraint to filter by. + * @param bool $invert Whether to invert matches to discover reasons for the package *NOT* to be installed. + * @param bool $recurse Whether to recursively expand the requirement tree up to the root package. + * @param string[] $packagesFound Used internally when recurring + * @return array An associative array of arrays as described above. + */ + public function getDependents($needle, $constraint = null, $invert = false, $recurse = true, $packagesFound = null) + { + $needles = array_map('strtolower', (array) $needle); + $results = array(); + + // initialize the array with the needles before any recursion occurs + if (null === $packagesFound) { + $packagesFound = $needles; + } + + // locate root package for use below + $rootPackage = null; + foreach ($this->getPackages() as $package) { + if ($package instanceof RootPackageInterface) { + $rootPackage = $package; + break; + } + } + + // Loop over all currently installed packages. + foreach ($this->getPackages() as $package) { + $links = $package->getRequires(); + + // each loop needs its own "tree" as we want to show the complete dependent set of every needle + // without warning all the time about finding circular deps + $packagesInTree = $packagesFound; + + // Replacements are considered valid reasons for a package to be installed during forward resolution + if (!$invert) { + $links += $package->getReplaces(); + + // On forward search, check if any replaced package was required and add the replaced + // packages to the list of needles. Contrary to the cross-reference link check below, + // replaced packages are the target of links. + foreach ($package->getReplaces() as $link) { + foreach ($needles as $needle) { + if ($link->getSource() === $needle) { + if ($constraint === null || ($link->getConstraint()->matches($constraint) === true)) { + // already displayed this node's dependencies, cutting short + if (in_array($link->getTarget(), $packagesInTree)) { + $results[] = array($package, $link, false); + continue; + } + $packagesInTree[] = $link->getTarget(); + $dependents = $recurse ? $this->getDependents($link->getTarget(), null, false, true, $packagesInTree) : array(); + $results[] = array($package, $link, $dependents); + $needles[] = $link->getTarget(); + } + } + } + } + } + + // Require-dev is only relevant for the root package + if ($package instanceof RootPackageInterface) { + $links += $package->getDevRequires(); + } + + // Cross-reference all discovered links to the needles + foreach ($links as $link) { + foreach ($needles as $needle) { + if ($link->getTarget() === $needle) { + if ($constraint === null || ($link->getConstraint()->matches($constraint) === !$invert)) { + // already displayed this node's dependencies, cutting short + if (in_array($link->getSource(), $packagesInTree)) { + $results[] = array($package, $link, false); + continue; + } + $packagesInTree[] = $link->getSource(); + $dependents = $recurse ? $this->getDependents($link->getSource(), null, false, true, $packagesInTree) : array(); + $results[] = array($package, $link, $dependents); + } + } + } + } + + // When inverting, we need to check for conflicts of the needles against installed packages + if ($invert && in_array($package->getName(), $needles)) { + foreach ($package->getConflicts() as $link) { + foreach ($this->findPackages($link->getTarget()) as $pkg) { + $version = new Constraint('=', $pkg->getVersion()); + if ($link->getConstraint()->matches($version) === $invert) { + $results[] = array($package, $link, false); + } + } + } + } + + // List conflicts against X as they may explain why the current version was selected, or explain why it is rejected if the conflict matched when inverting + foreach ($package->getConflicts() as $link) { + if (in_array($link->getTarget(), $needles)) { + foreach ($this->findPackages($link->getTarget()) as $pkg) { + $version = new Constraint('=', $pkg->getVersion()); + if ($link->getConstraint()->matches($version) === $invert) { + $results[] = array($package, $link, false); + } + } + } + } + + // When inverting, we need to check for conflicts of the needles' requirements against installed packages + if ($invert && $constraint && in_array($package->getName(), $needles) && $constraint->matches(new Constraint('=', $package->getVersion()))) { + foreach ($package->getRequires() as $link) { + if (PlatformRepository::isPlatformPackage($link->getTarget())) { + if ($this->findPackage($link->getTarget(), $link->getConstraint())) { + continue; + } + + $platformPkg = $this->findPackage($link->getTarget(), '*'); + $description = $platformPkg ? 'but '.$platformPkg->getPrettyVersion().' is installed' : 'but it is missing'; + $results[] = array($package, new Link($package->getName(), $link->getTarget(), new MatchAllConstraint, Link::TYPE_REQUIRE, $link->getPrettyConstraint().' '.$description), false); + + continue; + } + + foreach ($this->getPackages() as $pkg) { + if (!in_array($link->getTarget(), $pkg->getNames())) { + continue; + } + + $version = new Constraint('=', $pkg->getVersion()); + + if ($link->getTarget() !== $pkg->getName()) { + foreach (array_merge($pkg->getReplaces(), $pkg->getProvides()) as $prov) { + if ($link->getTarget() === $prov->getTarget()) { + $version = $prov->getConstraint(); + break; + } + } + } + + if (!$link->getConstraint()->matches($version)) { + // if we have a root package (we should but can not guarantee..) we show + // the root requires as well to perhaps allow to find an issue there + if ($rootPackage) { + foreach (array_merge($rootPackage->getRequires(), $rootPackage->getDevRequires()) as $rootReq) { + if (in_array($rootReq->getTarget(), $pkg->getNames()) && !$rootReq->getConstraint()->matches($link->getConstraint())) { + $results[] = array($package, $link, false); + $results[] = array($rootPackage, $rootReq, false); + continue 3; + } + } + + $results[] = array($package, $link, false); + $results[] = array($rootPackage, new Link($rootPackage->getName(), $link->getTarget(), new MatchAllConstraint, Link::TYPE_DOES_NOT_REQUIRE, 'but ' . $pkg->getPrettyVersion() . ' is installed'), false); + } else { + // no root so let's just print whatever we found + $results[] = array($package, $link, false); + } + } + + continue 2; + } + } + } + } + + ksort($results); + + return $results; + } + + public function getRepoName() + { + return 'installed repo ('.implode(', ', array_map(function ($repo) { + return $repo->getRepoName(); + }, $this->getRepositories())).')'; + } + + /** + * Add a repository. + * @param RepositoryInterface $repository + */ + public function addRepository(RepositoryInterface $repository) + { + if ( + $repository instanceof LockArrayRepository + || $repository instanceof InstalledRepositoryInterface + || $repository instanceof RootPackageRepository + || $repository instanceof PlatformRepository + ) { + return parent::addRepository($repository); + } + + throw new \LogicException('An InstalledRepository can not contain a repository of type '.get_class($repository).' ('.$repository->getRepoName().')'); + } +} diff --git a/app/vendor/composer/composer/src/Composer/Repository/InstalledRepositoryInterface.php b/app/vendor/composer/composer/src/Composer/Repository/InstalledRepositoryInterface.php index 19b095b2a..b5d8a264e 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/InstalledRepositoryInterface.php +++ b/app/vendor/composer/composer/src/Composer/Repository/InstalledRepositoryInterface.php @@ -21,4 +21,8 @@ */ interface InstalledRepositoryInterface extends WritableRepositoryInterface { + /** + * @return bool true if packages were never installed in this repository + */ + public function isFresh(); } diff --git a/app/vendor/composer/composer/src/Composer/Repository/LockArrayRepository.php b/app/vendor/composer/composer/src/Composer/Repository/LockArrayRepository.php new file mode 100644 index 000000000..7e3b92b03 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Repository/LockArrayRepository.php @@ -0,0 +1,28 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +/** + * Lock array repository. + * + * Regular array repository, only uses a different type to identify the lock file as the source of info + * + * @author Nils Adermann + */ +class LockArrayRepository extends ArrayRepository +{ + public function getRepoName() + { + return 'lock repo'; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Repository/PackageRepository.php b/app/vendor/composer/composer/src/Composer/Repository/PackageRepository.php index 52b9a0f6b..c5576cf7e 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/PackageRepository.php +++ b/app/vendor/composer/composer/src/Composer/Repository/PackageRepository.php @@ -22,6 +22,7 @@ */ class PackageRepository extends ArrayRepository { + /** @var mixed[] */ private $config; /** @@ -58,4 +59,9 @@ protected function initialize() $this->addPackage($package); } } + + public function getRepoName() + { + return preg_replace('{^array }', 'package ', parent::getRepoName()); + } } diff --git a/app/vendor/composer/composer/src/Composer/Repository/PathRepository.php b/app/vendor/composer/composer/src/Composer/Repository/PathRepository.php index b6601f883..7232e0368 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/PathRepository.php +++ b/app/vendor/composer/composer/src/Composer/Repository/PathRepository.php @@ -21,6 +21,7 @@ use Composer\Util\Platform; use Composer\Util\ProcessExecutor; use Composer\Util\Filesystem; +use Composer\Util\Url; use Composer\Util\Git as GitUtil; /** @@ -76,7 +77,8 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn private $url; /** - * @var array + * @var mixed[] + * @phpstan-var array{url: string, options?: array{symlink?: bool, relative?: bool, versions?: array}} */ private $repoConfig; @@ -86,7 +88,7 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn private $process; /** - * @var array + * @var array{symlink?: bool, relative?: bool, versions?: array} */ private $options; @@ -117,6 +119,11 @@ public function __construct(array $repoConfig, IOInterface $io, Config $config) parent::__construct(); } + public function getRepoName() + { + return 'path repo ('.Url::sanitize($this->repoConfig['url']).')'; + } + public function getRepoConfig() { return $this->repoConfig; diff --git a/app/vendor/composer/composer/src/Composer/Repository/Pear/BaseChannelReader.php b/app/vendor/composer/composer/src/Composer/Repository/Pear/BaseChannelReader.php deleted file mode 100644 index 9b26eb9db..000000000 --- a/app/vendor/composer/composer/src/Composer/Repository/Pear/BaseChannelReader.php +++ /dev/null @@ -1,82 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Repository\Pear; - -use Composer\Util\RemoteFilesystem; - -/** - * Base PEAR Channel reader. - * - * Provides xml namespaces and red - * - * @author Alexey Prilipko - */ -abstract class BaseChannelReader -{ - /** - * PEAR REST Interface namespaces - */ - const CHANNEL_NS = 'http://pear.php.net/channel-1.0'; - const ALL_CATEGORIES_NS = 'http://pear.php.net/dtd/rest.allcategories'; - const CATEGORY_PACKAGES_INFO_NS = 'http://pear.php.net/dtd/rest.categorypackageinfo'; - const ALL_PACKAGES_NS = 'http://pear.php.net/dtd/rest.allpackages'; - const ALL_RELEASES_NS = 'http://pear.php.net/dtd/rest.allreleases'; - const PACKAGE_INFO_NS = 'http://pear.php.net/dtd/rest.package'; - - /** @var RemoteFilesystem */ - private $rfs; - - protected function __construct(RemoteFilesystem $rfs) - { - $this->rfs = $rfs; - } - - /** - * Read content from remote filesystem. - * - * @param string $origin server - * @param string $path relative path to content - * @throws \UnexpectedValueException - * @return \SimpleXMLElement - */ - protected function requestContent($origin, $path) - { - $url = rtrim($origin, '/') . '/' . ltrim($path, '/'); - $content = $this->rfs->getContents($origin, $url, false); - if (!$content) { - throw new \UnexpectedValueException('The PEAR channel at ' . $url . ' did not respond.'); - } - - return str_replace('http://pear.php.net/rest/', 'https://pear.php.net/rest/', $content); - } - - /** - * Read xml content from remote filesystem - * - * @param string $origin server - * @param string $path relative path to content - * @throws \UnexpectedValueException - * @return \SimpleXMLElement - */ - protected function requestXml($origin, $path) - { - // http://components.ez.no/p/packages.xml is malformed. to read it we must ignore parsing errors. - $xml = simplexml_load_string($this->requestContent($origin, $path), "SimpleXMLElement", LIBXML_NOERROR); - - if (false === $xml) { - throw new \UnexpectedValueException(sprintf('The PEAR channel at ' . $origin . ' is broken. (Invalid XML at file `%s`)', $path)); - } - - return $xml; - } -} diff --git a/app/vendor/composer/composer/src/Composer/Repository/Pear/ChannelInfo.php b/app/vendor/composer/composer/src/Composer/Repository/Pear/ChannelInfo.php deleted file mode 100644 index 69e33b887..000000000 --- a/app/vendor/composer/composer/src/Composer/Repository/Pear/ChannelInfo.php +++ /dev/null @@ -1,67 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Repository\Pear; - -/** - * PEAR channel info - * - * @author Alexey Prilipko - */ -class ChannelInfo -{ - private $name; - private $alias; - private $packages; - - /** - * @param string $name - * @param string $alias - * @param PackageInfo[] $packages - */ - public function __construct($name, $alias, array $packages) - { - $this->name = $name; - $this->alias = $alias; - $this->packages = $packages; - } - - /** - * Name of the channel - * - * @return string - */ - public function getName() - { - return $this->name; - } - - /** - * Alias of the channel - * - * @return string - */ - public function getAlias() - { - return $this->alias; - } - - /** - * List of channel packages - * - * @return PackageInfo[] - */ - public function getPackages() - { - return $this->packages; - } -} diff --git a/app/vendor/composer/composer/src/Composer/Repository/Pear/ChannelReader.php b/app/vendor/composer/composer/src/Composer/Repository/Pear/ChannelReader.php deleted file mode 100644 index 73cc9152e..000000000 --- a/app/vendor/composer/composer/src/Composer/Repository/Pear/ChannelReader.php +++ /dev/null @@ -1,101 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Repository\Pear; - -use Composer\Util\RemoteFilesystem; - -/** - * PEAR Channel package reader. - * - * Reads channel packages info from and builds Package's - * - * @author Alexey Prilipko - */ -class ChannelReader extends BaseChannelReader -{ - /** @var array of ('xpath test' => 'rest implementation') */ - private $readerMap; - - public function __construct(RemoteFilesystem $rfs) - { - parent::__construct($rfs); - - $rest10reader = new ChannelRest10Reader($rfs); - $rest11reader = new ChannelRest11Reader($rfs); - - $this->readerMap = array( - 'REST1.3' => $rest11reader, - 'REST1.2' => $rest11reader, - 'REST1.1' => $rest11reader, - 'REST1.0' => $rest10reader, - ); - } - - /** - * Reads PEAR channel through REST interface and builds list of packages - * - * @param string $url PEAR Channel url - * @throws \UnexpectedValueException - * @return ChannelInfo - */ - public function read($url) - { - $xml = $this->requestXml($url, "/channel.xml"); - - $channelName = (string) $xml->name; - $channelAlias = (string) $xml->suggestedalias; - - $supportedVersions = array_keys($this->readerMap); - $selectedRestVersion = $this->selectRestVersion($xml, $supportedVersions); - if (!$selectedRestVersion) { - throw new \UnexpectedValueException(sprintf('PEAR repository %s does not supports any of %s protocols.', $url, implode(', ', $supportedVersions))); - } - - $reader = $this->readerMap[$selectedRestVersion['version']]; - $packageDefinitions = $reader->read($selectedRestVersion['baseUrl']); - - return new ChannelInfo($channelName, $channelAlias, $packageDefinitions); - } - - /** - * Reads channel supported REST interfaces and selects one of them - * - * @param \SimpleXMLElement $channelXml - * @param string[] $supportedVersions supported PEAR REST protocols - * @return array|null hash with selected version and baseUrl - */ - private function selectRestVersion($channelXml, $supportedVersions) - { - $channelXml->registerXPathNamespace('ns', self::CHANNEL_NS); - - foreach ($supportedVersions as $version) { - $xpathTest = "ns:servers/ns:*/ns:rest/ns:baseurl[@type='{$version}']"; - $testResult = $channelXml->xpath($xpathTest); - - foreach ($testResult as $result) { - // Choose first https:// option. - $result = (string) $result; - if (preg_match('{^https://}i', $result)) { - return array('version' => $version, 'baseUrl' => $result); - } - } - - // Fallback to non-https if it does not exist. - if (count($testResult) > 0) { - return array('version' => $version, 'baseUrl' => (string) $testResult[0]); - } - } - - return null; - } -} diff --git a/app/vendor/composer/composer/src/Composer/Repository/Pear/ChannelRest10Reader.php b/app/vendor/composer/composer/src/Composer/Repository/Pear/ChannelRest10Reader.php deleted file mode 100644 index 489914d5d..000000000 --- a/app/vendor/composer/composer/src/Composer/Repository/Pear/ChannelRest10Reader.php +++ /dev/null @@ -1,164 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Repository\Pear; - -use Composer\Downloader\TransportException; - -/** - * Read PEAR packages using REST 1.0 interface - * - * At version 1.0 package descriptions read from: - * {baseUrl}/p/packages.xml - * {baseUrl}/p/{package}/info.xml - * {baseUrl}/p/{package}/allreleases.xml - * {baseUrl}/p/{package}/deps.{version}.txt - * - * @author Alexey Prilipko - */ -class ChannelRest10Reader extends BaseChannelReader -{ - private $dependencyReader; - - public function __construct($rfs) - { - parent::__construct($rfs); - - $this->dependencyReader = new PackageDependencyParser(); - } - - /** - * Reads package descriptions using PEAR Rest 1.0 interface - * - * @param string $baseUrl base Url interface - * - * @return PackageInfo[] - */ - public function read($baseUrl) - { - return $this->readPackages($baseUrl); - } - - /** - * Read list of packages from - * {baseUrl}/p/packages.xml - * - * @param string $baseUrl - * @return PackageInfo[] - */ - private function readPackages($baseUrl) - { - $result = array(); - - $xmlPath = '/p/packages.xml'; - $xml = $this->requestXml($baseUrl, $xmlPath); - $xml->registerXPathNamespace('ns', self::ALL_PACKAGES_NS); - foreach ($xml->xpath('ns:p') as $node) { - $packageName = (string) $node; - $packageInfo = $this->readPackage($baseUrl, $packageName); - $result[] = $packageInfo; - } - - return $result; - } - - /** - * Read package info from - * {baseUrl}/p/{package}/info.xml - * - * @param string $baseUrl - * @param string $packageName - * @return PackageInfo - */ - private function readPackage($baseUrl, $packageName) - { - $xmlPath = '/p/' . strtolower($packageName) . '/info.xml'; - $xml = $this->requestXml($baseUrl, $xmlPath); - $xml->registerXPathNamespace('ns', self::PACKAGE_INFO_NS); - - $channelName = (string) $xml->c; - $packageName = (string) $xml->n; - $license = (string) $xml->l; - $shortDescription = (string) $xml->s; - $description = (string) $xml->d; - - return new PackageInfo( - $channelName, - $packageName, - $license, - $shortDescription, - $description, - $this->readPackageReleases($baseUrl, $packageName) - ); - } - - /** - * Read package releases from - * {baseUrl}/p/{package}/allreleases.xml - * - * @param string $baseUrl - * @param string $packageName - * @throws \Composer\Downloader\TransportException|\Exception - * @return ReleaseInfo[] hash array with keys as version numbers - */ - private function readPackageReleases($baseUrl, $packageName) - { - $result = array(); - - try { - $xmlPath = '/r/' . strtolower($packageName) . '/allreleases.xml'; - $xml = $this->requestXml($baseUrl, $xmlPath); - $xml->registerXPathNamespace('ns', self::ALL_RELEASES_NS); - foreach ($xml->xpath('ns:r') as $node) { - $releaseVersion = (string) $node->v; - $releaseStability = (string) $node->s; - - try { - $result[$releaseVersion] = new ReleaseInfo( - $releaseStability, - $this->readPackageReleaseDependencies($baseUrl, $packageName, $releaseVersion) - ); - } catch (TransportException $exception) { - if ($exception->getCode() != 404) { - throw $exception; - } - } - } - } catch (TransportException $exception) { - if ($exception->getCode() != 404) { - throw $exception; - } - } - - return $result; - } - - /** - * Read package dependencies from - * {baseUrl}/p/{package}/deps.{version}.txt - * - * @param string $baseUrl - * @param string $packageName - * @param string $version - * @return DependencyInfo[] - */ - private function readPackageReleaseDependencies($baseUrl, $packageName, $version) - { - $dependencyReader = new PackageDependencyParser(); - - $depthPath = '/r/' . strtolower($packageName) . '/deps.' . $version . '.txt'; - $content = $this->requestContent($baseUrl, $depthPath); - $dependencyArray = unserialize($content); - - return $dependencyReader->buildDependencyInfo($dependencyArray); - } -} diff --git a/app/vendor/composer/composer/src/Composer/Repository/Pear/ChannelRest11Reader.php b/app/vendor/composer/composer/src/Composer/Repository/Pear/ChannelRest11Reader.php deleted file mode 100644 index f9e05f5be..000000000 --- a/app/vendor/composer/composer/src/Composer/Repository/Pear/ChannelRest11Reader.php +++ /dev/null @@ -1,139 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Repository\Pear; - -/** - * Read PEAR packages using REST 1.1 interface - * - * At version 1.1 package descriptions read from: - * {baseUrl}/c/categories.xml - * {baseUrl}/c/{category}/packagesinfo.xml - * - * @author Alexey Prilipko - */ -class ChannelRest11Reader extends BaseChannelReader -{ - private $dependencyReader; - - public function __construct($rfs) - { - parent::__construct($rfs); - - $this->dependencyReader = new PackageDependencyParser(); - } - - /** - * Reads package descriptions using PEAR Rest 1.1 interface - * - * @param string $baseUrl base Url interface - * - * @return PackageInfo[] - */ - public function read($baseUrl) - { - return $this->readChannelPackages($baseUrl); - } - - /** - * Read list of channel categories from - * {baseUrl}/c/categories.xml - * - * @param string $baseUrl - * @return PackageInfo[] - */ - private function readChannelPackages($baseUrl) - { - $result = array(); - - $xml = $this->requestXml($baseUrl, "/c/categories.xml"); - $xml->registerXPathNamespace('ns', self::ALL_CATEGORIES_NS); - foreach ($xml->xpath('ns:c') as $node) { - $categoryName = (string) $node; - $categoryPackages = $this->readCategoryPackages($baseUrl, $categoryName); - $result = array_merge($result, $categoryPackages); - } - - return $result; - } - - /** - * Read packages from - * {baseUrl}/c/{category}/packagesinfo.xml - * - * @param string $baseUrl - * @param string $categoryName - * @return PackageInfo[] - */ - private function readCategoryPackages($baseUrl, $categoryName) - { - $result = array(); - - $categoryPath = '/c/'.urlencode($categoryName).'/packagesinfo.xml'; - $xml = $this->requestXml($baseUrl, $categoryPath); - $xml->registerXPathNamespace('ns', self::CATEGORY_PACKAGES_INFO_NS); - foreach ($xml->xpath('ns:pi') as $node) { - $packageInfo = $this->parsePackage($node); - $result[] = $packageInfo; - } - - return $result; - } - - /** - * Parses package node. - * - * @param \SimpleXMLElement $packageInfo xml element describing package - * @return PackageInfo - */ - private function parsePackage($packageInfo) - { - $packageInfo->registerXPathNamespace('ns', self::CATEGORY_PACKAGES_INFO_NS); - $channelName = (string) $packageInfo->p->c; - $packageName = (string) $packageInfo->p->n; - $license = (string) $packageInfo->p->l; - $shortDescription = (string) $packageInfo->p->s; - $description = (string) $packageInfo->p->d; - - $dependencies = array(); - foreach ($packageInfo->xpath('ns:deps') as $node) { - $dependencyVersion = (string) $node->v; - $dependencyArray = unserialize((string) $node->d); - - $dependencyInfo = $this->dependencyReader->buildDependencyInfo($dependencyArray); - - $dependencies[$dependencyVersion] = $dependencyInfo; - } - - $releases = array(); - $releasesInfo = $packageInfo->xpath('ns:a/ns:r'); - if ($releasesInfo) { - foreach ($releasesInfo as $node) { - $releaseVersion = (string) $node->v; - $releaseStability = (string) $node->s; - $releases[$releaseVersion] = new ReleaseInfo( - $releaseStability, - isset($dependencies[$releaseVersion]) ? $dependencies[$releaseVersion] : new DependencyInfo(array(), array()) - ); - } - } - - return new PackageInfo( - $channelName, - $packageName, - $license, - $shortDescription, - $description, - $releases - ); - } -} diff --git a/app/vendor/composer/composer/src/Composer/Repository/Pear/DependencyConstraint.php b/app/vendor/composer/composer/src/Composer/Repository/Pear/DependencyConstraint.php deleted file mode 100644 index 13a790026..000000000 --- a/app/vendor/composer/composer/src/Composer/Repository/Pear/DependencyConstraint.php +++ /dev/null @@ -1,60 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Repository\Pear; - -/** - * PEAR package release dependency info - * - * @author Alexey Prilipko - */ -class DependencyConstraint -{ - private $type; - private $constraint; - private $channelName; - private $packageName; - - /** - * @param string $type - * @param string $constraint - * @param string $channelName - * @param string $packageName - */ - public function __construct($type, $constraint, $channelName, $packageName) - { - $this->type = $type; - $this->constraint = $constraint; - $this->channelName = $channelName; - $this->packageName = $packageName; - } - - public function getChannelName() - { - return $this->channelName; - } - - public function getConstraint() - { - return $this->constraint; - } - - public function getPackageName() - { - return $this->packageName; - } - - public function getType() - { - return $this->type; - } -} diff --git a/app/vendor/composer/composer/src/Composer/Repository/Pear/DependencyInfo.php b/app/vendor/composer/composer/src/Composer/Repository/Pear/DependencyInfo.php deleted file mode 100644 index c6b266e37..000000000 --- a/app/vendor/composer/composer/src/Composer/Repository/Pear/DependencyInfo.php +++ /dev/null @@ -1,50 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Repository\Pear; - -/** - * PEAR package release dependency info - * - * @author Alexey Prilipko - */ -class DependencyInfo -{ - private $requires; - private $optionals; - - /** - * @param DependencyConstraint[] $requires list of requires/conflicts/replaces - * @param array $optionals [groupName => DependencyConstraint[]] list of optional groups - */ - public function __construct($requires, $optionals) - { - $this->requires = $requires; - $this->optionals = $optionals; - } - - /** - * @return DependencyConstraint[] list of requires/conflicts/replaces - */ - public function getRequires() - { - return $this->requires; - } - - /** - * @return array [groupName => DependencyConstraint[]] list of optional groups - */ - public function getOptionals() - { - return $this->optionals; - } -} diff --git a/app/vendor/composer/composer/src/Composer/Repository/Pear/PackageDependencyParser.php b/app/vendor/composer/composer/src/Composer/Repository/Pear/PackageDependencyParser.php deleted file mode 100644 index 24f8fb9f9..000000000 --- a/app/vendor/composer/composer/src/Composer/Repository/Pear/PackageDependencyParser.php +++ /dev/null @@ -1,317 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Repository\Pear; - -/** - * Read PEAR packages using REST 1.0 interface - * - * @author Alexey Prilipko - */ -class PackageDependencyParser -{ - /** - * Builds dependency information. It detects used package.xml format. - * - * @param array $depArray - * @return DependencyInfo - */ - public function buildDependencyInfo($depArray) - { - if (!is_array($depArray)) { - return new DependencyInfo(array(), array()); - } - if (!$this->isHash($depArray)) { - return new DependencyInfo($this->buildDependency10Info($depArray), array()); - } - - return $this->buildDependency20Info($depArray); - } - - /** - * Builds dependency information from package.xml 1.0 format - * - * https://pear.php.net/manual/en/guide.developers.package2.dependencies.php - * - * package.xml 1.0 format consists of array of - * { type="php|os|sapi|ext|pkg" rel="has|not|eq|ge|gt|le|lt" optional="yes" - * channel="channelName" name="extName|packageName" } - * - * @param array $depArray Dependency data in package.xml 1.0 format - * @return DependencyConstraint[] - */ - private function buildDependency10Info($depArray) - { - static $dep10toOperatorMap = array('has' => '==', 'eq' => '==', 'ge' => '>=', 'gt' => '>', 'le' => '<=', 'lt' => '<', 'not' => '!='); - - $result = array(); - - foreach ($depArray as $depItem) { - if (empty($depItem['rel']) || !array_key_exists($depItem['rel'], $dep10toOperatorMap)) { - // 'unknown rel type:' . $depItem['rel']; - continue; - } - - $depType = !empty($depItem['optional']) && 'yes' == $depItem['optional'] - ? 'optional' - : 'required'; - $depType = 'not' == $depItem['rel'] - ? 'conflicts' - : $depType; - - $depVersion = !empty($depItem['version']) ? $this->parseVersion($depItem['version']) : '*'; - - // has & not are special operators that does not requires version - $depVersionConstraint = ('has' == $depItem['rel'] || 'not' == $depItem['rel']) && '*' == $depVersion - ? '*' - : $dep10toOperatorMap[$depItem['rel']] . $depVersion; - - switch ($depItem['type']) { - case 'php': - $depChannelName = 'php'; - $depPackageName = ''; - break; - case 'pkg': - $depChannelName = !empty($depItem['channel']) ? $depItem['channel'] : 'pear.php.net'; - $depPackageName = $depItem['name']; - break; - case 'ext': - $depChannelName = 'ext'; - $depPackageName = $depItem['name']; - break; - case 'os': - case 'sapi': - $depChannelName = ''; - $depPackageName = ''; - break; - default: - $depChannelName = ''; - $depPackageName = ''; - break; - } - - if ('' != $depChannelName) { - $result[] = new DependencyConstraint( - $depType, - $depVersionConstraint, - $depChannelName, - $depPackageName - ); - } - } - - return $result; - } - - /** - * Builds dependency information from package.xml 2.0 format - * - * @param array $depArray Dependency data in package.xml 1.0 format - * @return DependencyInfo - */ - private function buildDependency20Info($depArray) - { - $result = array(); - $optionals = array(); - $defaultOptionals = array(); - foreach ($depArray as $depType => $depTypeGroup) { - if (!is_array($depTypeGroup)) { - continue; - } - if ('required' == $depType || 'optional' == $depType) { - foreach ($depTypeGroup as $depItemType => $depItem) { - switch ($depItemType) { - case 'php': - $result[] = new DependencyConstraint( - $depType, - $this->parse20VersionConstraint($depItem), - 'php', - '' - ); - break; - case 'package': - $deps = $this->buildDepPackageConstraints($depItem, $depType); - $result = array_merge($result, $deps); - break; - case 'extension': - $deps = $this->buildDepExtensionConstraints($depItem, $depType); - $result = array_merge($result, $deps); - break; - case 'subpackage': - $deps = $this->buildDepPackageConstraints($depItem, 'replaces'); - $defaultOptionals += $deps; - break; - case 'os': - case 'pearinstaller': - break; - default: - break; - } - } - } elseif ('group' == $depType) { - if ($this->isHash($depTypeGroup)) { - $depTypeGroup = array($depTypeGroup); - } - - foreach ($depTypeGroup as $depItem) { - $groupName = $depItem['attribs']['name']; - if (!isset($optionals[$groupName])) { - $optionals[$groupName] = array(); - } - - if (isset($depItem['subpackage'])) { - $optionals[$groupName] += $this->buildDepPackageConstraints($depItem['subpackage'], 'replaces'); - } else { - $result += $this->buildDepPackageConstraints($depItem['package'], 'optional'); - } - } - } - } - - if (count($defaultOptionals) > 0) { - $optionals['*'] = $defaultOptionals; - } - - return new DependencyInfo($result, $optionals); - } - - /** - * Builds dependency constraint of 'extension' type - * - * @param array $depItem dependency constraint or array of dependency constraints - * @param string $depType target type of building constraint. - * @return DependencyConstraint[] - */ - private function buildDepExtensionConstraints($depItem, $depType) - { - if ($this->isHash($depItem)) { - $depItem = array($depItem); - } - - $result = array(); - foreach ($depItem as $subDepItem) { - $depChannelName = 'ext'; - $depPackageName = $subDepItem['name']; - $depVersionConstraint = $this->parse20VersionConstraint($subDepItem); - - $result[] = new DependencyConstraint( - $depType, - $depVersionConstraint, - $depChannelName, - $depPackageName - ); - } - - return $result; - } - - /** - * Builds dependency constraint of 'package' type - * - * @param array $depItem dependency constraint or array of dependency constraints - * @param string $depType target type of building constraint. - * @return DependencyConstraint[] - */ - private function buildDepPackageConstraints($depItem, $depType) - { - if ($this->isHash($depItem)) { - $depItem = array($depItem); - } - - $result = array(); - foreach ($depItem as $subDepItem) { - if (!array_key_exists('channel', $subDepItem)) { - $subDepItem['channel'] = $subDepItem['uri']; - } - $depChannelName = $subDepItem['channel']; - $depPackageName = $subDepItem['name']; - $depVersionConstraint = $this->parse20VersionConstraint($subDepItem); - if (isset($subDepItem['conflicts'])) { - $depType = 'conflicts'; - } - - $result[] = new DependencyConstraint( - $depType, - $depVersionConstraint, - $depChannelName, - $depPackageName - ); - } - - return $result; - } - - /** - * Parses version constraint - * - * @param array $data array containing several 'min', 'max', 'has', 'exclude' and other keys. - * @return string - */ - private function parse20VersionConstraint(array $data) - { - static $dep20toOperatorMap = array('has' => '==', 'min' => '>=', 'max' => '<=', 'exclude' => '!='); - - $versions = array(); - $values = array_intersect_key($data, $dep20toOperatorMap); - if (0 == count($values)) { - return '*'; - } - if (isset($values['min']) && isset($values['exclude']) && $data['min'] == $data['exclude']) { - $versions[] = '>' . $this->parseVersion($values['min']); - } elseif (isset($values['max']) && isset($values['exclude']) && $data['max'] == $data['exclude']) { - $versions[] = '<' . $this->parseVersion($values['max']); - } else { - foreach ($values as $op => $version) { - if ('exclude' == $op && is_array($version)) { - foreach ($version as $versionPart) { - $versions[] = $dep20toOperatorMap[$op] . $this->parseVersion($versionPart); - } - } else { - $versions[] = $dep20toOperatorMap[$op] . $this->parseVersion($version); - } - } - } - - return implode(',', $versions); - } - - /** - * Softened version parser - * - * @param string $version - * @return null|string - */ - private function parseVersion($version) - { - if (preg_match('{^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?}i', $version, $matches)) { - $version = $matches[1] - .(!empty($matches[2]) ? $matches[2] : '.0') - .(!empty($matches[3]) ? $matches[3] : '.0') - .(!empty($matches[4]) ? $matches[4] : '.0'); - - return $version; - } - - return null; - } - - /** - * Test if array is associative or hash type - * - * @param array $array - * @return bool - */ - private function isHash(array $array) - { - return !array_key_exists(1, $array) && !array_key_exists(0, $array); - } -} diff --git a/app/vendor/composer/composer/src/Composer/Repository/Pear/PackageInfo.php b/app/vendor/composer/composer/src/Composer/Repository/Pear/PackageInfo.php deleted file mode 100644 index 3b2eb6d90..000000000 --- a/app/vendor/composer/composer/src/Composer/Repository/Pear/PackageInfo.php +++ /dev/null @@ -1,94 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Repository\Pear; - -/** - * PEAR Package info - * - * @author Alexey Prilipko - */ -class PackageInfo -{ - private $channelName; - private $packageName; - private $license; - private $shortDescription; - private $description; - private $releases; - - /** - * @param string $channelName - * @param string $packageName - * @param string $license - * @param string $shortDescription - * @param string $description - * @param ReleaseInfo[] $releases associative array maps release version to release info - */ - public function __construct($channelName, $packageName, $license, $shortDescription, $description, $releases) - { - $this->channelName = $channelName; - $this->packageName = $packageName; - $this->license = $license; - $this->shortDescription = $shortDescription; - $this->description = $description; - $this->releases = $releases; - } - - /** - * @return string the package channel name - */ - public function getChannelName() - { - return $this->channelName; - } - - /** - * @return string the package name - */ - public function getPackageName() - { - return $this->packageName; - } - - /** - * @return string the package description - */ - public function getDescription() - { - return $this->description; - } - - /** - * @return string the package short description - */ - public function getShortDescription() - { - return $this->shortDescription; - } - - /** - * @return string the package license - */ - public function getLicense() - { - return $this->license; - } - - /** - * @return ReleaseInfo[] - */ - public function getReleases() - { - return $this->releases; - } -} diff --git a/app/vendor/composer/composer/src/Composer/Repository/Pear/ReleaseInfo.php b/app/vendor/composer/composer/src/Composer/Repository/Pear/ReleaseInfo.php deleted file mode 100644 index 39d6e1ed6..000000000 --- a/app/vendor/composer/composer/src/Composer/Repository/Pear/ReleaseInfo.php +++ /dev/null @@ -1,50 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Repository\Pear; - -/** - * PEAR package release info - * - * @author Alexey Prilipko - */ -class ReleaseInfo -{ - private $stability; - private $dependencyInfo; - - /** - * @param string $stability - * @param DependencyInfo $dependencyInfo - */ - public function __construct($stability, $dependencyInfo) - { - $this->stability = $stability; - $this->dependencyInfo = $dependencyInfo; - } - - /** - * @return DependencyInfo release dependencies - */ - public function getDependencyInfo() - { - return $this->dependencyInfo; - } - - /** - * @return string release stability - */ - public function getStability() - { - return $this->stability; - } -} diff --git a/app/vendor/composer/composer/src/Composer/Repository/PearRepository.php b/app/vendor/composer/composer/src/Composer/Repository/PearRepository.php index c4f0b83e7..0998f302d 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/PearRepository.php +++ b/app/vendor/composer/composer/src/Composer/Repository/PearRepository.php @@ -12,19 +12,6 @@ namespace Composer\Repository; -use Composer\IO\IOInterface; -use Composer\Semver\VersionParser as SemverVersionParser; -use Composer\Package\Version\VersionParser; -use Composer\Repository\Pear\ChannelReader; -use Composer\Package\CompletePackage; -use Composer\Repository\Pear\ChannelInfo; -use Composer\EventDispatcher\EventDispatcher; -use Composer\Package\Link; -use Composer\Semver\Constraint\Constraint; -use Composer\Util\RemoteFilesystem; -use Composer\Config; -use Composer\Factory; - /** * Builds list of package from PEAR channel. * @@ -33,166 +20,13 @@ * * @author Benjamin Eberlei * @author Jordi Boggiano + * @deprecated + * @private */ -class PearRepository extends ArrayRepository implements ConfigurableRepositoryInterface +class PearRepository extends ArrayRepository { - private $url; - private $io; - private $rfs; - private $versionParser; - private $repoConfig; - - /** @var string vendor makes additional alias for each channel as {prefix}/{packagename}. It allows smoother - * package transition to composer-like repositories. - */ - private $vendorAlias; - - public function __construct(array $repoConfig, IOInterface $io, Config $config, EventDispatcher $dispatcher = null, RemoteFilesystem $rfs = null) - { - parent::__construct(); - if (!preg_match('{^https?://}', $repoConfig['url'])) { - $repoConfig['url'] = 'http://'.$repoConfig['url']; - } - - $urlBits = parse_url($repoConfig['url']); - if (empty($urlBits['scheme']) || empty($urlBits['host'])) { - throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$repoConfig['url']); - } - - $this->url = rtrim($repoConfig['url'], '/'); - $this->io = $io; - $this->rfs = $rfs ?: Factory::createRemoteFilesystem($this->io, $config); - $this->vendorAlias = isset($repoConfig['vendor-alias']) ? $repoConfig['vendor-alias'] : null; - $this->versionParser = new VersionParser(); - $this->repoConfig = $repoConfig; - } - - public function getRepoConfig() - { - return $this->repoConfig; - } - - protected function initialize() - { - parent::initialize(); - - $this->io->writeError('Initializing PEAR repository '.$this->url); - - $reader = new ChannelReader($this->rfs); - try { - $channelInfo = $reader->read($this->url); - } catch (\Exception $e) { - $this->io->writeError('PEAR repository from '.$this->url.' could not be loaded. '.$e->getMessage().''); - - return; - } - $packages = $this->buildComposerPackages($channelInfo, $this->versionParser); - foreach ($packages as $package) { - $this->addPackage($package); - } - } - - /** - * Builds CompletePackages from PEAR package definition data. - * - * @param ChannelInfo $channelInfo - * @param SemverVersionParser $versionParser - * @return CompletePackage - */ - private function buildComposerPackages(ChannelInfo $channelInfo, SemverVersionParser $versionParser) + public function __construct() { - $result = array(); - foreach ($channelInfo->getPackages() as $packageDefinition) { - foreach ($packageDefinition->getReleases() as $version => $releaseInfo) { - try { - $normalizedVersion = $versionParser->normalize($version); - } catch (\UnexpectedValueException $e) { - $this->io->writeError('Could not load '.$packageDefinition->getPackageName().' '.$version.': '.$e->getMessage(), true, IOInterface::VERBOSE); - continue; - } - - $composerPackageName = $this->buildComposerPackageName($packageDefinition->getChannelName(), $packageDefinition->getPackageName()); - - // distribution url must be read from /r/{packageName}/{version}.xml::/r/g:text() - // but this location is 'de-facto' standard - $urlBits = parse_url($this->url); - $scheme = (isset($urlBits['scheme']) && 'https' === $urlBits['scheme'] && extension_loaded('openssl')) ? 'https' : 'http'; - $distUrl = "{$scheme}://{$packageDefinition->getChannelName()}/get/{$packageDefinition->getPackageName()}-{$version}.tgz"; - - $requires = array(); - $suggests = array(); - $conflicts = array(); - $replaces = array(); - - // alias package only when its channel matches repository channel, - // cause we've know only repository channel alias - if ($channelInfo->getName() == $packageDefinition->getChannelName()) { - $composerPackageAlias = $this->buildComposerPackageName($channelInfo->getAlias(), $packageDefinition->getPackageName()); - $aliasConstraint = new Constraint('==', $normalizedVersion); - $replaces[] = new Link($composerPackageName, $composerPackageAlias, $aliasConstraint, 'replaces', (string) $aliasConstraint); - } - - // alias package with user-specified prefix. it makes private pear channels looks like composer's. - if (!empty($this->vendorAlias) - && ($this->vendorAlias != 'pear-'.$channelInfo->getAlias() || $channelInfo->getName() != $packageDefinition->getChannelName()) - ) { - $composerPackageAlias = "{$this->vendorAlias}/{$packageDefinition->getPackageName()}"; - $aliasConstraint = new Constraint('==', $normalizedVersion); - $replaces[] = new Link($composerPackageName, $composerPackageAlias, $aliasConstraint, 'replaces', (string) $aliasConstraint); - } - - foreach ($releaseInfo->getDependencyInfo()->getRequires() as $dependencyConstraint) { - $dependencyPackageName = $this->buildComposerPackageName($dependencyConstraint->getChannelName(), $dependencyConstraint->getPackageName()); - $constraint = $versionParser->parseConstraints($dependencyConstraint->getConstraint()); - $link = new Link($composerPackageName, $dependencyPackageName, $constraint, $dependencyConstraint->getType(), $dependencyConstraint->getConstraint()); - switch ($dependencyConstraint->getType()) { - case 'required': - $requires[] = $link; - break; - case 'conflicts': - $conflicts[] = $link; - break; - case 'replaces': - $replaces[] = $link; - break; - } - } - - foreach ($releaseInfo->getDependencyInfo()->getOptionals() as $group => $dependencyConstraints) { - foreach ($dependencyConstraints as $dependencyConstraint) { - $dependencyPackageName = $this->buildComposerPackageName($dependencyConstraint->getChannelName(), $dependencyConstraint->getPackageName()); - $suggests[$group.'-'.$dependencyPackageName] = $dependencyConstraint->getConstraint(); - } - } - - $package = new CompletePackage($composerPackageName, $normalizedVersion, $version); - $package->setType('pear-library'); - $package->setDescription($packageDefinition->getDescription()); - $package->setLicense(array($packageDefinition->getLicense())); - $package->setDistType('file'); - $package->setDistUrl($distUrl); - $package->setAutoload(array('classmap' => array(''))); - $package->setIncludePaths(array('/')); - $package->setRequires($requires); - $package->setConflicts($conflicts); - $package->setSuggests($suggests); - $package->setReplaces($replaces); - $result[] = $package; - } - } - - return $result; - } - - private function buildComposerPackageName($channelName, $packageName) - { - if ('php' === $channelName) { - return "php"; - } - if ('ext' === $channelName) { - return "ext-{$packageName}"; - } - - return "pear-{$channelName}/{$packageName}"; + throw new \InvalidArgumentException('The PEAR repository has been removed from Composer 2.x'); } } diff --git a/app/vendor/composer/composer/src/Composer/Repository/PlatformRepository.php b/app/vendor/composer/composer/src/Composer/Repository/PlatformRepository.php index ecd4ca256..5cd1ae27c 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/PlatformRepository.php +++ b/app/vendor/composer/composer/src/Composer/Repository/PlatformRepository.php @@ -14,14 +14,17 @@ use Composer\Composer; use Composer\Package\CompletePackage; +use Composer\Package\CompletePackageInterface; +use Composer\Package\Link; use Composer\Package\PackageInterface; use Composer\Package\Version\VersionParser; +use Composer\Platform\HhvmDetector; +use Composer\Platform\Runtime; +use Composer\Platform\Version; use Composer\Plugin\PluginInterface; -use Composer\Util\ProcessExecutor; +use Composer\Semver\Constraint\Constraint; use Composer\Util\Silencer; -use Composer\Util\Platform; use Composer\XdebugHandler\XdebugHandler; -use Symfony\Component\Process\ExecutableFinder; /** * @author Jordi Boggiano @@ -30,28 +33,45 @@ class PlatformRepository extends ArrayRepository { const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer-(?:plugin|runtime)-api)$}iD'; + /** + * @var ?string + */ + private static $lastSeenPlatformPhp = null; + + /** + * @var VersionParser + */ private $versionParser; /** * Defines overrides so that the platform can be mocked * - * Should be an array of package name => version number mappings + * Keyed by package name (lowercased) * - * @var array + * @var array */ private $overrides = array(); - private $process; + /** @var Runtime */ + private $runtime; + /** @var HhvmDetector */ + private $hhvmDetector; - public function __construct(array $packages = array(), array $overrides = array(), ProcessExecutor $process = null) + public function __construct(array $packages = array(), array $overrides = array(), Runtime $runtime = null, HhvmDetector $hhvmDetector = null) { - $this->process = $process === null ? (new ProcessExecutor()) : $process; + $this->runtime = $runtime ?: new Runtime(); + $this->hhvmDetector = $hhvmDetector ?: new HhvmDetector(); foreach ($overrides as $name => $version) { $this->overrides[strtolower($name)] = array('name' => $name, 'version' => $version); } parent::__construct($packages); } + public function getRepoName() + { + return 'platform repo'; + } + protected function initialize() { parent::initialize(); @@ -62,7 +82,7 @@ protected function initialize() // Later we might even replace the extensions instead. foreach ($this->overrides as $override) { // Check that it's a platform package. - if (!preg_match(self::PLATFORM_PACKAGE_REGEX, $override['name'])) { + if (!self::isPlatformPackage($override['name'])) { throw new \InvalidArgumentException('Invalid platform package name in config.platform: '.$override['name']); } @@ -82,10 +102,10 @@ protected function initialize() $this->addPackage($composerRuntimeApi); try { - $prettyVersion = PHP_VERSION; + $prettyVersion = $this->runtime->getConstant('PHP_VERSION'); $version = $this->versionParser->normalize($prettyVersion); } catch (\UnexpectedValueException $e) { - $prettyVersion = preg_replace('#^([^~+-]+).*$#', '$1', PHP_VERSION); + $prettyVersion = preg_replace('#^([^~+-]+).*$#', '$1', $this->runtime->getConstant('PHP_VERSION')); $version = $this->versionParser->normalize($prettyVersion); } @@ -93,19 +113,19 @@ protected function initialize() $php->setDescription('The PHP interpreter'); $this->addPackage($php); - if (PHP_DEBUG) { + if ($this->runtime->getConstant('PHP_DEBUG')) { $phpdebug = new CompletePackage('php-debug', $version, $prettyVersion); $phpdebug->setDescription('The PHP interpreter, with debugging symbols'); $this->addPackage($phpdebug); } - if (defined('PHP_ZTS') && PHP_ZTS) { + if ($this->runtime->hasConstant('PHP_ZTS') && $this->runtime->getConstant('PHP_ZTS')) { $phpzts = new CompletePackage('php-zts', $version, $prettyVersion); $phpzts->setDescription('The PHP interpreter, with Zend Thread Safety'); $this->addPackage($phpzts); } - if (PHP_INT_SIZE === 8) { + if ($this->runtime->getConstant('PHP_INT_SIZE') === 8) { $php64 = new CompletePackage('php-64bit', $version, $prettyVersion); $php64->setDescription('The PHP interpreter, 64bit'); $this->addPackage($php64); @@ -113,13 +133,13 @@ protected function initialize() // The AF_INET6 constant is only defined if ext-sockets is available but // IPv6 support might still be available. - if (defined('AF_INET6') || Silencer::call('inet_pton', '::') !== false) { + if ($this->runtime->hasConstant('AF_INET6') || Silencer::call(array($this->runtime, 'invoke'), 'inet_pton', array('::')) !== false) { $phpIpv6 = new CompletePackage('php-ipv6', $version, $prettyVersion); $phpIpv6->setDescription('The PHP interpreter, with IPv6 support'); $this->addPackage($phpIpv6); } - $loadedExtensions = get_loaded_extensions(); + $loadedExtensions = $this->runtime->getExtensions(); // Extensions scanning foreach ($loadedExtensions as $name) { @@ -127,9 +147,7 @@ protected function initialize() continue; } - $reflExt = new \ReflectionExtension($name); - $prettyVersion = $reflExt->getVersion(); - $this->addExtension($name, $prettyVersion); + $this->addExtension($name, $this->runtime->getExtensionVersion($name)); } // Check for Xdebug in a restarted process @@ -141,119 +159,318 @@ protected function initialize() // Doing it this way to know that functions or constants exist before // relying on them. foreach ($loadedExtensions as $name) { - $prettyVersion = null; - $description = 'The '.$name.' PHP library'; switch ($name) { + case 'amqp': + $info = $this->runtime->getExtensionInfo($name); + + // librabbitmq version => 0.9.0 + if (preg_match('/^librabbitmq version => (?.+)$/im', $info, $librabbitmqMatches)) { + $this->addLibrary($name.'-librabbitmq', $librabbitmqMatches['version'], 'AMQP librabbitmq version'); + } + + // AMQP protocol version => 0-9-1 + if (preg_match('/^AMQP protocol version => (?.+)$/im', $info, $protocolMatches)) { + $this->addLibrary($name.'-protocol', str_replace('-', '.', $protocolMatches['version']), 'AMQP protocol version'); + } + break; + + case 'bz2': + $info = $this->runtime->getExtensionInfo($name); + + // BZip2 Version => 1.0.6, 6-Sept-2010 + if (preg_match('/^BZip2 Version => (?.*),/im', $info, $matches)) { + $this->addLibrary($name, $matches['version']); + } + break; + case 'curl': - $curlVersion = curl_version(); - $prettyVersion = $curlVersion['version']; + $curlVersion = $this->runtime->invoke('curl_version'); + $this->addLibrary($name, $curlVersion['version']); + + $info = $this->runtime->getExtensionInfo($name); + + // SSL Version => OpenSSL/1.0.1t + if (preg_match('{^SSL Version => (?[^/]+)/(?.+)$}im', $info, $sslMatches)) { + $library = strtolower($sslMatches['library']); + if ($library === 'openssl') { + $parsedVersion = Version::parseOpenssl($sslMatches['version'], $isFips); + $this->addLibrary($name.'-openssl'.($isFips ? '-fips' : ''), $parsedVersion, 'curl OpenSSL version ('.$parsedVersion.')', array(), $isFips ? array('curl-openssl') : array()); + } else { + $this->addLibrary($name.'-'.$library, $sslMatches['version'], 'curl '.$library.' version ('.$sslMatches['version'].')', array('curl-openssl')); + } + } + + // libSSH Version => libssh2/1.4.3 + if (preg_match('{^libSSH Version => (?[^/]+)/(?.+?)(?:/.*)?$}im', $info, $sshMatches)) { + $this->addLibrary($name.'-'.strtolower($sshMatches['library']), $sshMatches['version'], 'curl '.$sshMatches['library'].' version'); + } + + // ZLib Version => 1.2.8 + if (preg_match('{^ZLib Version => (?.+)$}im', $info, $zlibMatches)) { + $this->addLibrary($name.'-zlib', $zlibMatches['version'], 'curl zlib version'); + } + break; + + case 'date': + $info = $this->runtime->getExtensionInfo($name); + + // timelib version => 2018.03 + if (preg_match('/^timelib version => (?.+)$/im', $info, $timelibMatches)) { + $this->addLibrary($name.'-timelib', $timelibMatches['version'], 'date timelib version'); + } + + // Timezone Database => internal + if (preg_match('/^Timezone Database => (?internal|external)$/im', $info, $zoneinfoSourceMatches)) { + $external = $zoneinfoSourceMatches['source'] === 'external'; + if (preg_match('/^"Olson" Timezone Database Version => (?.+?)(\.system)?$/im', $info, $zoneinfoMatches)) { + // If the timezonedb is provided by ext/timezonedb, register that version as a replacement + if ($external && in_array('timezonedb', $loadedExtensions, true)) { + $this->addLibrary('timezonedb-zoneinfo', $zoneinfoMatches['version'], 'zoneinfo ("Olson") database for date (replaced by timezonedb)', array($name.'-zoneinfo')); + } else { + $this->addLibrary($name.'-zoneinfo', $zoneinfoMatches['version'], 'zoneinfo ("Olson") database for date'); + } + } + } + break; + + case 'fileinfo': + $info = $this->runtime->getExtensionInfo($name); + + // libmagic => 537 + if (preg_match('/^libmagic => (?.+)$/im', $info, $magicMatches)) { + $this->addLibrary($name.'-libmagic', $magicMatches['version'], 'fileinfo libmagic version'); + } + break; + + case 'gd': + $this->addLibrary($name, $this->runtime->getConstant('GD_VERSION')); + + $info = $this->runtime->getExtensionInfo($name); + + if (preg_match('/^libJPEG Version => (?.+?)(?: compatible)?$/im', $info, $libjpegMatches)) { + $this->addLibrary($name.'-libjpeg', Version::parseLibjpeg($libjpegMatches['version']), 'libjpeg version for gd'); + } + + if (preg_match('/^libPNG Version => (?.+)$/im', $info, $libpngMatches)) { + $this->addLibrary($name.'-libpng', $libpngMatches['version'], 'libpng version for gd'); + } + + if (preg_match('/^FreeType Version => (?.+)$/im', $info, $freetypeMatches)) { + $this->addLibrary($name.'-freetype', $freetypeMatches['version'], 'freetype version for gd'); + } + + if (preg_match('/^libXpm Version => (?\d+)$/im', $info, $libxpmMatches)) { + $this->addLibrary($name.'-libxpm', Version::convertLibxpmVersionId($libxpmMatches['versionId']), 'libxpm version for gd'); + } + + break; + + case 'gmp': + $this->addLibrary($name, $this->runtime->getConstant('GMP_VERSION')); break; case 'iconv': - $prettyVersion = ICONV_VERSION; + $this->addLibrary($name, $this->runtime->getConstant('ICONV_VERSION')); break; case 'intl': - $name = 'ICU'; - if (defined('INTL_ICU_VERSION')) { - $prettyVersion = INTL_ICU_VERSION; - } else { - $reflector = new \ReflectionExtension('intl'); + $info = $this->runtime->getExtensionInfo($name); + + $description = 'The ICU unicode and globalization support library'; + // Truthy check is for testing only so we can make the condition fail + if ($this->runtime->hasConstant('INTL_ICU_VERSION')) { + $this->addLibrary('icu', $this->runtime->getConstant('INTL_ICU_VERSION'), $description); + } elseif (preg_match('/^ICU version => (?.+)$/im', $info, $matches)) { + $this->addLibrary('icu', $matches['version'], $description); + } - ob_start(); - $reflector->info(); - $output = ob_get_clean(); + // ICU TZData version => 2019c + if (preg_match('/^ICU TZData version => (?.*)$/im', $info, $zoneinfoMatches)) { + $this->addLibrary('icu-zoneinfo', Version::parseZoneinfoVersion($zoneinfoMatches['version']), 'zoneinfo ("Olson") database for icu'); + } - preg_match('/^ICU version => (.*)$/m', $output, $matches); - $prettyVersion = $matches[1]; + // Add a separate version for the CLDR library version + if ($this->runtime->hasClass('ResourceBundle')) { + $cldrVersion = $this->runtime->invoke(array('ResourceBundle', 'create'), array('root', 'ICUDATA', false))->get('Version'); + $this->addLibrary('icu-cldr', $cldrVersion, 'ICU CLDR project version'); } + if ($this->runtime->hasClass('IntlChar')) { + $this->addLibrary('icu-unicode', implode('.', array_slice($this->runtime->invoke(array('IntlChar', 'getUnicodeVersion')), 0, 3)), 'ICU unicode version'); + } break; case 'imagick': - $imagick = new \Imagick(); - $imageMagickVersion = $imagick->getVersion(); + $imageMagickVersion = $this->runtime->construct('Imagick')->getVersion(); // 6.x: ImageMagick 6.2.9 08/24/06 Q16 http://www.imagemagick.org // 7.x: ImageMagick 7.0.8-34 Q16 x86_64 2019-03-23 https://imagemagick.org - preg_match('/^ImageMagick ([\d.]+)(?:-(\d+))?/', $imageMagickVersion['versionString'], $matches); - if (isset($matches[2])) { - $prettyVersion = "{$matches[1]}.{$matches[2]}"; - } else { - $prettyVersion = $matches[1]; + preg_match('/^ImageMagick (?[\d.]+)(?:-(?\d+))?/', $imageMagickVersion['versionString'], $matches); + $version = $matches['version']; + if (isset($matches['patch'])) { + $version .= '.'.$matches['patch']; + } + + $this->addLibrary($name.'-imagemagick', $version, null, array('imagick')); + break; + + case 'ldap': + $info = $this->runtime->getExtensionInfo($name); + + if (preg_match('/^Vendor Version => (?\d+)$/im', $info, $matches) && preg_match('/^Vendor Name => (?.+)$/im', $info, $vendorMatches)) { + $this->addLibrary($name.'-'.strtolower($vendorMatches['vendor']), Version::convertOpenldapVersionId($matches['versionId']), $vendorMatches['vendor'].' version of ldap'); } break; case 'libxml': - $prettyVersion = LIBXML_DOTTED_VERSION; + // ext/dom, ext/simplexml, ext/xmlreader and ext/xmlwriter use the same libxml as the ext/libxml + $libxmlProvides = array_map(function ($extension) { + return $extension . '-libxml'; + }, array_intersect($loadedExtensions, array('dom', 'simplexml', 'xml', 'xmlreader', 'xmlwriter'))); + $this->addLibrary($name, $this->runtime->getConstant('LIBXML_DOTTED_VERSION'), 'libxml library version', array(), $libxmlProvides); + break; - case 'openssl': - $prettyVersion = preg_replace_callback('{^(?:OpenSSL|LibreSSL)?\s*([0-9.]+)([a-z]*).*}i', function ($match) { - if (empty($match[2])) { - return $match[1]; - } + case 'mbstring': + $info = $this->runtime->getExtensionInfo($name); - // OpenSSL versions add another letter when they reach Z. - // e.g. OpenSSL 0.9.8zh 3 Dec 2015 + // libmbfl version => 1.3.2 + if (preg_match('/^libmbfl version => (?.+)$/im', $info, $libmbflMatches)) { + $this->addLibrary($name.'-libmbfl', $libmbflMatches['version'], 'mbstring libmbfl version'); + } - if (!preg_match('{^z*[a-z]$}', $match[2])) { - // 0.9.8abc is garbage - return 0; - } + if ($this->runtime->hasConstant('MB_ONIGURUMA_VERSION')) { + $this->addLibrary($name.'-oniguruma', $this->runtime->getConstant('MB_ONIGURUMA_VERSION'), 'mbstring oniguruma version'); + + // Multibyte regex (oniguruma) version => 5.9.5 + // oniguruma version => 6.9.0 + } elseif (preg_match('/^(?:oniguruma|Multibyte regex \(oniguruma\)) version => (?.+)$/im', $info, $onigurumaMatches)) { + $this->addLibrary($name.'-oniguruma', $onigurumaMatches['version'], 'mbstring oniguruma version'); + } + + break; - $len = strlen($match[2]); - $patchVersion = ($len - 1) * 26; // All Z - $patchVersion += ord($match[2][$len - 1]) - 96; + case 'memcached': + $info = $this->runtime->getExtensionInfo($name); - return $match[1].'.'.$patchVersion; - }, OPENSSL_VERSION_TEXT); + // libmemcached version => 1.0.18 + if (preg_match('/^libmemcached version => (?.+)$/im', $info, $matches)) { + $this->addLibrary($name.'-libmemcached', $matches['version'], 'libmemcached version'); + } + break; - $description = OPENSSL_VERSION_TEXT; + case 'openssl': + // OpenSSL 1.1.1g 21 Apr 2020 + if (preg_match('{^(?:OpenSSL|LibreSSL)?\s*(?\S+)}i', $this->runtime->getConstant('OPENSSL_VERSION_TEXT'), $matches)) { + $parsedVersion = Version::parseOpenssl($matches['version'], $isFips); + $this->addLibrary($name.($isFips ? '-fips' : ''), $parsedVersion, $this->runtime->getConstant('OPENSSL_VERSION_TEXT'), array(), $isFips ? array($name) : array()); + } break; case 'pcre': - $prettyVersion = preg_replace('{^(\S+).*}', '$1', PCRE_VERSION); + $this->addLibrary($name, preg_replace('{^(\S+).*}', '$1', $this->runtime->getConstant('PCRE_VERSION'))); + + $info = $this->runtime->getExtensionInfo($name); + + // PCRE Unicode Version => 12.1.0 + if (preg_match('/^PCRE Unicode Version => (?.+)$/im', $info, $pcreUnicodeMatches)) { + $this->addLibrary($name.'-unicode', $pcreUnicodeMatches['version'], 'PCRE Unicode version support'); + } + + break; + + case 'mysqlnd': + case 'pdo_mysql': + $info = $this->runtime->getExtensionInfo($name); + + if (preg_match('/^(?:Client API version|Version) => mysqlnd (?.+?) /mi', $info, $matches)) { + $this->addLibrary($name.'-mysqlnd', $matches['version'], 'mysqlnd library version for '.$name); + } + break; + + case 'mongodb': + $info = $this->runtime->getExtensionInfo($name); + + if (preg_match('/^libmongoc bundled version => (?.+)$/im', $info, $libmongocMatches)) { + $this->addLibrary($name.'-libmongoc', $libmongocMatches['version'], 'libmongoc version of mongodb'); + } + + if (preg_match('/^libbson bundled version => (?.+)$/im', $info, $libbsonMatches)) { + $this->addLibrary($name.'-libbson', $libbsonMatches['version'], 'libbson version of mongodb'); + } + break; + + case 'pgsql': + case 'pdo_pgsql': + $info = $this->runtime->getExtensionInfo($name); + + if (preg_match('/^PostgreSQL\(libpq\) Version => (?.*)$/im', $info, $matches)) { + $this->addLibrary($name.'-libpq', $matches['version'], 'libpq for '.$name); + } + break; + + case 'libsodium': + case 'sodium': + if ($this->runtime->hasConstant('SODIUM_LIBRARY_VERSION')) { + $this->addLibrary('libsodium', $this->runtime->getConstant('SODIUM_LIBRARY_VERSION')); + } + break; + + case 'sqlite3': + case 'pdo_sqlite': + $info = $this->runtime->getExtensionInfo($name); + + if (preg_match('/^SQLite Library => (?.+)$/im', $info, $matches)) { + $this->addLibrary($name.'-sqlite', $matches['version']); + } break; - case 'uuid': - $prettyVersion = phpversion('uuid'); + case 'ssh2': + $info = $this->runtime->getExtensionInfo($name); + + if (preg_match('/^libssh2 version => (?.+)$/im', $info, $matches)) { + $this->addLibrary($name.'-libssh2', $matches['version']); + } break; case 'xsl': - $prettyVersion = LIBXSLT_DOTTED_VERSION; + $this->addLibrary('libxslt', $this->runtime->getConstant('LIBXSLT_DOTTED_VERSION'), null, array('xsl')); + + $info = $this->runtime->getExtensionInfo('xsl'); + if (preg_match('/^libxslt compiled against libxml Version => (?.+)$/im', $info, $matches)) { + $this->addLibrary('libxslt-libxml', $matches['version'], 'libxml version libxslt is compiled against'); + } break; - default: - // None handled extensions have no special cases, skip - continue 2; - } + case 'yaml': + $info = $this->runtime->getExtensionInfo('yaml'); - try { - $version = $this->versionParser->normalize($prettyVersion); - } catch (\UnexpectedValueException $e) { - continue; - } + if (preg_match('/^LibYAML Version => (?.+)$/im', $info, $matches)) { + $this->addLibrary($name.'-libyaml', $matches['version'], 'libyaml version of yaml'); + } + break; - $lib = new CompletePackage('lib-'.$name, $version, $prettyVersion); - $lib->setDescription($description); - $this->addPackage($lib); - } + case 'zip': + if ($this->runtime->hasConstant('LIBZIP_VERSION', 'ZipArchive')) { + $this->addLibrary($name.'-libzip', $this->runtime->getConstant('LIBZIP_VERSION', 'ZipArchive'), null, array('zip')); + } + break; + + case 'zlib': + if ($this->runtime->hasConstant('ZLIB_VERSION')) { + $this->addLibrary($name, $this->runtime->getConstant('ZLIB_VERSION')); + + // Linked Version => 1.2.8 + } elseif (preg_match('/^Linked Version => (?.+)$/im', $this->runtime->getExtensionInfo($name), $matches)) { + $this->addLibrary($name, $matches['version']); + } + break; - $hhvmVersion = defined('HHVM_VERSION') ? HHVM_VERSION : null; - if ($hhvmVersion === null && !Platform::isWindows()) { - $finder = new ExecutableFinder(); - $hhvm = $finder->find('hhvm'); - if ($hhvm !== null) { - $exitCode = $this->process->execute( - ProcessExecutor::escape($hhvm). - ' --php -d hhvm.jit=0 -r "echo HHVM_VERSION;" 2>/dev/null', - $hhvmVersion - ); - if ($exitCode !== 0) { - $hhvmVersion = null; - } + default: + break; } } + + $hhvmVersion = $this->hhvmDetector->getVersion(); if ($hhvmVersion) { try { $prettyVersion = $hhvmVersion; @@ -282,7 +499,9 @@ public function addPackage(PackageInterface $package) } else { $actualText = 'actual: '.$package->getPrettyVersion(); } - $overrider->setDescription($overrider->getDescription().' ('.$actualText.')'); + if ($overrider instanceof CompletePackageInterface) { + $overrider->setDescription($overrider->getDescription().', '.$actualText); + } return; } @@ -295,7 +514,7 @@ public function addPackage(PackageInterface $package) } else { $actualText = 'actual: '.$package->getPrettyVersion(); } - $overrider->setDescription($overrider->getDescription().' ('.$actualText.')'); + $overrider->setDescription($overrider->getDescription().', '.$actualText); return; } @@ -303,6 +522,9 @@ public function addPackage(PackageInterface $package) parent::addPackage($package); } + /** + * @return CompletePackage + */ private function addOverriddenPackage(array $override, $name = null) { $version = $this->versionParser->normalize($override['version']); @@ -311,6 +533,10 @@ private function addOverriddenPackage(array $override, $name = null) $package->setExtra(array('config.platform' => true)); parent::addPackage($package); + if ($package->getName() === 'php') { + self::$lastSeenPlatformPhp = implode('.', array_slice(explode('.', $package->getVersion()), 0, 3)); + } + return $package; } @@ -339,11 +565,84 @@ private function addExtension($name, $prettyVersion) $packageName = $this->buildPackageName($name); $ext = new CompletePackage($packageName, $version, $prettyVersion); $ext->setDescription('The '.$name.' PHP extension'.$extraDescription); + + if ($name === 'uuid') { + $ext->setReplaces(array( + 'lib-uuid' => new Link('ext-uuid', 'lib-uuid', new Constraint('=', $version), Link::TYPE_REPLACE, $ext->getPrettyVersion()), + )); + } + $this->addPackage($ext); } + /** + * @param string $name + * @return string + */ private function buildPackageName($name) { - return 'ext-' . str_replace(' ', '-', $name); + return 'ext-' . str_replace(' ', '-', strtolower($name)); + } + + /** + * @param string $name + * @param string $prettyVersion + * @param string|null $description + * @param string[] $replaces + * @param string[] $provides + */ + private function addLibrary($name, $prettyVersion, $description = null, array $replaces = array(), array $provides = array()) + { + try { + $version = $this->versionParser->normalize($prettyVersion); + } catch (\UnexpectedValueException $e) { + return; + } + + if ($description === null) { + $description = 'The '.$name.' library'; + } + + $lib = new CompletePackage('lib-'.$name, $version, $prettyVersion); + $lib->setDescription($description); + + $links = function ($alias) use ($name, $version, $lib) { + return new Link('lib-'.$name, 'lib-'.$alias, new Constraint('=', $version), Link::TYPE_REPLACE, $lib->getPrettyVersion()); + }; + $lib->setReplaces(array_map($links, $replaces)); + $lib->setProvides(array_map($links, $provides)); + + $this->addPackage($lib); + } + + /** + * Check if a package name is a platform package. + * + * @param string $name + * @return bool + */ + public static function isPlatformPackage($name) + { + static $cache = array(); + + if (isset($cache[$name])) { + return $cache[$name]; + } + + return $cache[$name] = (bool) preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name); + } + + /** + * Returns the last seen config.platform.php version if defined + * + * This is a best effort attempt for internal purposes, retrieve the real + * packages from a PlatformRepository instance if you need a version guaranteed to + * be correct. + * + * @internal + */ + public static function getPlatformPhpVersion() + { + return self::$lastSeenPlatformPhp; } } diff --git a/app/vendor/composer/composer/src/Composer/Repository/RepositoryFactory.php b/app/vendor/composer/composer/src/Composer/Repository/RepositoryFactory.php index cf2c79dec..4eeaa14aa 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/RepositoryFactory.php +++ b/app/vendor/composer/composer/src/Composer/Repository/RepositoryFactory.php @@ -16,7 +16,8 @@ use Composer\IO\IOInterface; use Composer\Config; use Composer\EventDispatcher\EventDispatcher; -use Composer\Util\RemoteFilesystem; +use Composer\Util\HttpDownloader; +use Composer\Util\ProcessExecutor; use Composer\Json\JsonFile; /** @@ -36,7 +37,7 @@ public static function configFromString(IOInterface $io, Config $config, $reposi if (0 === strpos($repository, 'http')) { $repoConfig = array('type' => 'composer', 'url' => $repository); } elseif ("json" === pathinfo($repository, PATHINFO_EXTENSION)) { - $json = new JsonFile($repository, Factory::createRemoteFilesystem($io, $config)); + $json = new JsonFile($repository, Factory::createHttpDownloader($io, $config)); $data = $json->read(); if (!empty($data['packages']) || !empty($data['includes']) || !empty($data['provider-includes'])) { $repoConfig = array('type' => 'composer', 'url' => 'file://' . strtr(realpath($repository), '\\', '/')); @@ -45,7 +46,7 @@ public static function configFromString(IOInterface $io, Config $config, $reposi } else { throw new \InvalidArgumentException("Invalid repository URL ($repository) given. This file does not contain a valid composer repository."); } - } elseif ('{' === substr($repository, 0, 1)) { + } elseif (strpos($repository, '{') === 0) { // assume it is a json object that makes a repo config $repoConfig = JsonFile::parseJson($repository); } else { @@ -78,7 +79,7 @@ public static function fromString(IOInterface $io, Config $config, $repository, public static function createRepo(IOInterface $io, Config $config, array $repoConfig, RepositoryManager $rm = null) { if (!$rm) { - $rm = static::manager($io, $config, null, Factory::createRemoteFilesystem($io, $config)); + $rm = static::manager($io, $config, Factory::createHttpDownloader($io, $config)); } $repos = static::createRepos($rm, array($repoConfig)); @@ -103,7 +104,7 @@ public static function defaultRepos(IOInterface $io = null, Config $config = nul if (!$io) { throw new \InvalidArgumentException('This function requires either an IOInterface or a RepositoryManager'); } - $rm = static::manager($io, $config, null, Factory::createRemoteFilesystem($io, $config)); + $rm = static::manager($io, $config, Factory::createHttpDownloader($io, $config)); } return static::createRepos($rm, $config->getRepositories()); @@ -113,12 +114,12 @@ public static function defaultRepos(IOInterface $io = null, Config $config = nul * @param IOInterface $io * @param Config $config * @param EventDispatcher $eventDispatcher - * @param RemoteFilesystem $rfs + * @param HttpDownloader $httpDownloader * @return RepositoryManager */ - public static function manager(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, RemoteFilesystem $rfs = null) + public static function manager(IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $eventDispatcher = null, ProcessExecutor $process = null) { - $rm = new RepositoryManager($io, $config, $eventDispatcher, $rfs); + $rm = new RepositoryManager($io, $config, $httpDownloader, $eventDispatcher, $process); $rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository'); $rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository'); $rm->setRepositoryClass('package', 'Composer\Repository\PackageRepository'); diff --git a/app/vendor/composer/composer/src/Composer/Repository/RepositoryInterface.php b/app/vendor/composer/composer/src/Composer/Repository/RepositoryInterface.php index 9a2aaf3b5..3b0760e0e 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/RepositoryInterface.php +++ b/app/vendor/composer/composer/src/Composer/Repository/RepositoryInterface.php @@ -13,6 +13,8 @@ namespace Composer\Repository; use Composer\Package\PackageInterface; +use Composer\Package\BasePackage; +use Composer\Semver\Constraint\ConstraintInterface; /** * Repository interface. @@ -38,20 +40,22 @@ public function hasPackage(PackageInterface $package); /** * Searches for the first match of a package by name and version. * - * @param string $name package name - * @param string|\Composer\Semver\Constraint\ConstraintInterface $constraint package version or version constraint to match against + * @param string $name package name + * @param string|ConstraintInterface $constraint package version or version constraint to match against * * @return PackageInterface|null + * @phpstan-return (BasePackage&PackageInterface)|null */ public function findPackage($name, $constraint); /** * Searches for all packages matching a name and optionally a version. * - * @param string $name package name - * @param string|\Composer\Semver\Constraint\ConstraintInterface $constraint package version or version constraint to match against + * @param string $name package name + * @param string|ConstraintInterface $constraint package version or version constraint to match against * * @return PackageInterface[] + * @phpstan-return array */ public function findPackages($name, $constraint = null); @@ -59,16 +63,58 @@ public function findPackages($name, $constraint = null); * Returns list of registered packages. * * @return PackageInterface[] + * @phpstan-return array */ public function getPackages(); + /** + * Returns list of registered packages with the supplied name + * + * - The packages returned are the packages found which match the constraints, acceptable stability and stability flags provided + * - The namesFound returned are names which should be considered as canonically found in this repository, that should not be looked up in any further lower priority repositories + * + * @param ConstraintInterface[] $packageNameMap package names pointing to constraints + * @param array $acceptableStabilities array of stability => BasePackage::STABILITY_* value + * @param array $stabilityFlags an array of package name => BasePackage::STABILITY_* value + * @param array> $alreadyLoaded an array of package name => package version => package + * + * @return array + * + * @phpstan-param array $packageNameMap + * @phpstan-return array{namesFound: string[], packages: array} + */ + public function loadPackages(array $packageNameMap, array $acceptableStabilities, array $stabilityFlags, array $alreadyLoaded = array()); + /** * Searches the repository for packages containing the query * * @param string $query search query * @param int $mode a set of SEARCH_* constants to search on, implementations should do a best effort only + * @param string $type The type of package to search for. Defaults to all types of packages + * + * @return array[] an array of array('name' => '...', 'description' => '...'|null) + * @phpstan-return list + */ + public function search($query, $mode = 0, $type = null); + + /** + * Returns a list of packages providing a given package name + * + * Packages which have the same name as $packageName should not be returned, only those that have a "provide" on it. + * + * @param string $packageName package name which must be provided + * + * @return array[] an array with the provider name as key and value of array('name' => '...', 'description' => '...', 'type' => '...') + * @phpstan-return array + */ + public function getProviders($packageName); + + /** + * Returns a name representing this repository to the user + * + * This is best effort and definitely can not always be very precise * - * @return array[] an array of array('name' => '...', 'description' => '...') + * @return string */ - public function search($query, $mode = 0); + public function getRepoName(); } diff --git a/app/vendor/composer/composer/src/Composer/Repository/RepositoryManager.php b/app/vendor/composer/composer/src/Composer/Repository/RepositoryManager.php index a724f644f..12123ce7b 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/RepositoryManager.php +++ b/app/vendor/composer/composer/src/Composer/Repository/RepositoryManager.php @@ -16,7 +16,8 @@ use Composer\Config; use Composer\EventDispatcher\EventDispatcher; use Composer\Package\PackageInterface; -use Composer\Util\RemoteFilesystem; +use Composer\Util\HttpDownloader; +use Composer\Util\ProcessExecutor; /** * Repositories manager. @@ -27,24 +28,34 @@ */ class RepositoryManager { + /** @var InstalledRepositoryInterface */ private $localRepository; + /** @var list */ private $repositories = array(); + /** @var array */ private $repositoryClasses = array(); + /** @var IOInterface */ private $io; + /** @var Config */ private $config; + /** @var HttpDownloader */ + private $httpDownloader; + /** @var ?EventDispatcher */ private $eventDispatcher; - private $rfs; + /** @var ProcessExecutor */ + private $process; - public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, RemoteFilesystem $rfs = null) + public function __construct(IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $eventDispatcher = null, ProcessExecutor $process = null) { $this->io = $io; $this->config = $config; + $this->httpDownloader = $httpDownloader; $this->eventDispatcher = $eventDispatcher; - $this->rfs = $rfs; + $this->process = $process ?: new ProcessExecutor($io); } /** - * Searches for a package by it's name and version in managed repositories. + * Searches for a package by its name and version in managed repositories. * * @param string $name package name * @param string|\Composer\Semver\Constraint\ConstraintInterface $constraint package version or version constraint to match against @@ -125,25 +136,18 @@ public function createRepository($type, $config, $name = null) $class = $this->repositoryClasses[$type]; - $reflMethod = new \ReflectionMethod($class, '__construct'); - $params = $reflMethod->getParameters(); - if (isset($params[4])) { - $paramType = null; - if (\PHP_VERSION_ID >= 70000) { - $reflectionType = $params[4]->getType(); - if ($reflectionType) { - $paramType = $reflectionType instanceof \ReflectionNamedType ? $reflectionType->getName() : (string)$reflectionType; - } - } else { - $paramType = $params[4]->getClass() ? $params[4]->getClass()->getName() : null; - } + if (isset($config['only']) || isset($config['exclude']) || isset($config['canonical'])) { + $filterConfig = $config; + unset($config['only'], $config['exclude'], $config['canonical']); + } - if ($paramType === 'Composer\Util\RemoteFilesystem') { - return new $class($config, $this->io, $this->config, $this->eventDispatcher, $this->rfs); - } + $repository = new $class($config, $this->io, $this->config, $this->httpDownloader, $this->eventDispatcher, $this->process); + + if (isset($filterConfig)) { + $repository = new FilterRepository($repository, $filterConfig); } - return new $class($config, $this->io, $this->config, $this->eventDispatcher); + return $repository; } /** @@ -170,9 +174,9 @@ public function getRepositories() /** * Sets local repository for the project. * - * @param WritableRepositoryInterface $repository repository instance + * @param InstalledRepositoryInterface $repository repository instance */ - public function setLocalRepository(WritableRepositoryInterface $repository) + public function setLocalRepository(InstalledRepositoryInterface $repository) { $this->localRepository = $repository; } @@ -180,7 +184,7 @@ public function setLocalRepository(WritableRepositoryInterface $repository) /** * Returns local repository for the project. * - * @return WritableRepositoryInterface + * @return InstalledRepositoryInterface */ public function getLocalRepository() { diff --git a/app/vendor/composer/composer/src/Composer/Repository/RepositorySet.php b/app/vendor/composer/composer/src/Composer/Repository/RepositorySet.php new file mode 100644 index 000000000..d87a334b3 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Repository/RepositorySet.php @@ -0,0 +1,329 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\DependencyResolver\Pool; +use Composer\DependencyResolver\PoolBuilder; +use Composer\DependencyResolver\Request; +use Composer\EventDispatcher\EventDispatcher; +use Composer\IO\IOInterface; +use Composer\IO\NullIO; +use Composer\Package\BasePackage; +use Composer\Package\AliasPackage; +use Composer\Package\CompleteAliasPackage; +use Composer\Package\CompletePackage; +use Composer\Package\CompletePackageInterface; +use Composer\Semver\Constraint\ConstraintInterface; +use Composer\Package\Version\StabilityFilter; + +/** + * @author Nils Adermann + */ +class RepositorySet +{ + /** + * Packages are returned even though their stability does not match the required stability + */ + const ALLOW_UNACCEPTABLE_STABILITIES = 1; + /** + * Packages will be looked up in all repositories, even after they have been found in a higher prio one + */ + const ALLOW_SHADOWED_REPOSITORIES = 2; + + /** + * @var array[] + * @phpstan-var array> + */ + private $rootAliases; + + /** + * @var string[] + * @phpstan-var array + */ + private $rootReferences; + + /** @var RepositoryInterface[] */ + private $repositories = array(); + + /** + * @var int[] array of stability => BasePackage::STABILITY_* value + * @phpstan-var array + */ + private $acceptableStabilities; + + /** + * @var int[] array of package name => BasePackage::STABILITY_* value + * @phpstan-var array + */ + private $stabilityFlags; + + /** + * @var ConstraintInterface[] + * @phpstan-var array + */ + private $rootRequires; + + /** @var bool */ + private $locked = false; + /** @var bool */ + private $allowInstalledRepositories = false; + + /** + * In most cases if you are looking to use this class as a way to find packages from repositories + * passing minimumStability is all you need to worry about. The rest is for advanced pool creation including + * aliases, pinned references and other special cases. + * + * @param string $minimumStability + * @param int[] $stabilityFlags an array of package name => BasePackage::STABILITY_* value + * @phpstan-param array $stabilityFlags + * @param array[] $rootAliases + * @phpstan-param list $rootAliases + * @param string[] $rootReferences an array of package name => source reference + * @phpstan-param array $rootReferences + * @param ConstraintInterface[] $rootRequires an array of package name => constraint from the root package + * @phpstan-param array $rootRequires + */ + public function __construct($minimumStability = 'stable', array $stabilityFlags = array(), array $rootAliases = array(), array $rootReferences = array(), array $rootRequires = array()) + { + $this->rootAliases = self::getRootAliasesPerPackage($rootAliases); + $this->rootReferences = $rootReferences; + + $this->acceptableStabilities = array(); + foreach (BasePackage::$stabilities as $stability => $value) { + if ($value <= BasePackage::$stabilities[$minimumStability]) { + $this->acceptableStabilities[$stability] = $value; + } + } + $this->stabilityFlags = $stabilityFlags; + $this->rootRequires = $rootRequires; + foreach ($rootRequires as $name => $constraint) { + if (PlatformRepository::isPlatformPackage($name)) { + unset($this->rootRequires[$name]); + } + } + } + + public function allowInstalledRepositories($allow = true) + { + $this->allowInstalledRepositories = $allow; + } + + /** + * @return ConstraintInterface[] an array of package name => constraint from the root package, platform requirements excluded + * @phpstan-return array + */ + public function getRootRequires() + { + return $this->rootRequires; + } + + /** + * Adds a repository to this repository set + * + * The first repos added have a higher priority. As soon as a package is found in any + * repository the search for that package ends, and following repos will not be consulted. + * + * @param RepositoryInterface $repo A package repository + */ + public function addRepository(RepositoryInterface $repo) + { + if ($this->locked) { + throw new \RuntimeException("Pool has already been created from this repository set, it cannot be modified anymore."); + } + + if ($repo instanceof CompositeRepository) { + $repos = $repo->getRepositories(); + } else { + $repos = array($repo); + } + + foreach ($repos as $repo) { + $this->repositories[] = $repo; + } + } + + /** + * Find packages providing or matching a name and optionally meeting a constraint in all repositories + * + * Returned in the order of repositories, matching priority + * + * @param string $name + * @param ConstraintInterface|null $constraint + * @param int $flags any of the ALLOW_* constants from this class to tweak what is returned + * @return array + */ + public function findPackages($name, ConstraintInterface $constraint = null, $flags = 0) + { + $ignoreStability = ($flags & self::ALLOW_UNACCEPTABLE_STABILITIES) !== 0; + $loadFromAllRepos = ($flags & self::ALLOW_SHADOWED_REPOSITORIES) !== 0; + + $packages = array(); + if ($loadFromAllRepos) { + foreach ($this->repositories as $repository) { + $packages[] = $repository->findPackages($name, $constraint) ?: array(); + } + } else { + foreach ($this->repositories as $repository) { + $result = $repository->loadPackages(array($name => $constraint), $ignoreStability ? BasePackage::$stabilities : $this->acceptableStabilities, $ignoreStability ? array() : $this->stabilityFlags); + + $packages[] = $result['packages']; + foreach ($result['namesFound'] as $nameFound) { + // avoid loading the same package again from other repositories once it has been found + if ($name === $nameFound) { + break 2; + } + } + } + } + + $candidates = $packages ? call_user_func_array('array_merge', $packages) : array(); + + // when using loadPackages above (!$loadFromAllRepos) the repos already filter for stability so no need to do it again + if ($ignoreStability || !$loadFromAllRepos) { + return $candidates; + } + + $result = array(); + foreach ($candidates as $candidate) { + if ($this->isPackageAcceptable($candidate->getNames(), $candidate->getStability())) { + $result[] = $candidate; + } + } + + return $result; + } + + /** + * @param string $packageName + * + * @return array[] an array with the provider name as key and value of array('name' => '...', 'description' => '...', 'type' => '...') + * @phpstan-return array + */ + public function getProviders($packageName) + { + $providers = array(); + foreach ($this->repositories as $repository) { + if ($repoProviders = $repository->getProviders($packageName)) { + $providers = array_merge($providers, $repoProviders); + } + } + + return $providers; + } + + /** + * Check for each given package name whether it would be accepted by this RepositorySet in the given $stability + * + * @param string[] $names + * @param string $stability one of 'stable', 'RC', 'beta', 'alpha' or 'dev' + * @return bool + */ + public function isPackageAcceptable($names, $stability) + { + return StabilityFilter::isPackageAcceptable($this->acceptableStabilities, $this->stabilityFlags, $names, $stability); + } + + /** + * Create a pool for dependency resolution from the packages in this repository set. + * + * @return Pool + */ + public function createPool(Request $request, IOInterface $io, EventDispatcher $eventDispatcher = null) + { + $poolBuilder = new PoolBuilder($this->acceptableStabilities, $this->stabilityFlags, $this->rootAliases, $this->rootReferences, $io, $eventDispatcher); + + foreach ($this->repositories as $repo) { + if (($repo instanceof InstalledRepositoryInterface || $repo instanceof InstalledRepository) && !$this->allowInstalledRepositories) { + throw new \LogicException('The pool can not accept packages from an installed repository'); + } + } + + $this->locked = true; + + return $poolBuilder->buildPool($this->repositories, $request); + } + + /** + * Create a pool for dependency resolution from the packages in this repository set. + * + * @return Pool + */ + public function createPoolWithAllPackages() + { + foreach ($this->repositories as $repo) { + if (($repo instanceof InstalledRepositoryInterface || $repo instanceof InstalledRepository) && !$this->allowInstalledRepositories) { + throw new \LogicException('The pool can not accept packages from an installed repository'); + } + } + + $this->locked = true; + + $packages = array(); + foreach ($this->repositories as $repository) { + foreach ($repository->getPackages() as $package) { + $packages[] = $package; + + if (isset($this->rootAliases[$package->getName()][$package->getVersion()])) { + $alias = $this->rootAliases[$package->getName()][$package->getVersion()]; + while ($package instanceof AliasPackage) { + $package = $package->getAliasOf(); + } + if ($package instanceof CompletePackage) { + $aliasPackage = new CompleteAliasPackage($package, $alias['alias_normalized'], $alias['alias']); + } else { + $aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']); + } + $aliasPackage->setRootPackageAlias(true); + $packages[] = $aliasPackage; + } + } + } + + return new Pool($packages); + } + + // TODO unify this with above in some simpler version without "request"? + public function createPoolForPackage($packageName, LockArrayRepository $lockedRepo = null) + { + return $this->createPoolForPackages(array($packageName), $lockedRepo); + } + + public function createPoolForPackages($packageNames, LockArrayRepository $lockedRepo = null) + { + $request = new Request($lockedRepo); + + foreach ($packageNames as $packageName) { + if (PlatformRepository::isPlatformPackage($packageName)) { + throw new \LogicException('createPoolForPackage(s) can not be used for platform packages, as they are never loaded by the PoolBuilder which expects them to be fixed. Use createPoolWithAllPackages or pass in a proper request with the platform packages you need fixed in it.'); + } + + $request->requireName($packageName); + } + + return $this->createPool($request, new NullIO()); + } + + private static function getRootAliasesPerPackage(array $aliases) + { + $normalizedAliases = array(); + + foreach ($aliases as $alias) { + $normalizedAliases[$alias['package']][$alias['version']] = array( + 'alias' => $alias['alias'], + 'alias_normalized' => $alias['alias_normalized'], + ); + } + + return $normalizedAliases; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Repository/RootPackageRepository.php b/app/vendor/composer/composer/src/Composer/Repository/RootPackageRepository.php new file mode 100644 index 000000000..9652282f7 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Repository/RootPackageRepository.php @@ -0,0 +1,35 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\Package\RootPackageInterface; + +/** + * Root package repository. + * + * This is used for serving the RootPackage inside an in-memory InstalledRepository + * + * @author Jordi Boggiano + */ +class RootPackageRepository extends ArrayRepository +{ + public function __construct(RootPackageInterface $package) + { + parent::__construct(array($package)); + } + + public function getRepoName() + { + return 'root package repo'; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Repository/Vcs/BitbucketDriver.php b/app/vendor/composer/composer/src/Composer/Repository/Vcs/BitbucketDriver.php index 08f119527..8bd47a7a2 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/Vcs/BitbucketDriver.php +++ b/app/vendor/composer/composer/src/Composer/Repository/Vcs/BitbucketDriver.php @@ -16,28 +16,37 @@ use Composer\Downloader\TransportException; use Composer\Json\JsonFile; use Composer\Util\Bitbucket; +use Composer\Util\Http\Response; abstract class BitbucketDriver extends VcsDriver { - /** @var Cache */ - protected $cache; + /** @var string */ protected $owner; + /** @var string */ protected $repository; - protected $hasIssues; + /** @var bool */ + protected $hasIssues = false; + /** @var ?string */ protected $rootIdentifier; + /** @var array Map of tag name to identifier */ protected $tags; + /** @var array Map of branch name to identifier */ protected $branches; - protected $infoCache = array(); + /** @var string */ protected $branchesUrl = ''; + /** @var string */ protected $tagsUrl = ''; + /** @var string */ protected $homeUrl = ''; + /** @var string */ protected $website = ''; + /** @var string */ protected $cloneHttpsUrl = ''; /** - * @var VcsDriver + * @var ?VcsDriver */ - protected $fallbackDriver; + protected $fallbackDriver = null; /** @var string|null if set either git or hg */ protected $vcsType; @@ -59,6 +68,7 @@ public function initialize() $this->repository, )) ); + $this->cache->setReadOnly($this->config->get('cache-read-only')); } /** @@ -78,6 +88,7 @@ public function getUrl() * sets some parameters which are used in other methods * * @return bool + * @phpstan-impure */ protected function getRepoData() { @@ -87,12 +98,12 @@ protected function getRepoData() $this->repository, http_build_query( array('fields' => '-project,-owner'), - null, + '', '&' ) ); - $repoData = JsonFile::parseJson($this->getContentsWithOAuthCredentials($resource, true), $resource); + $repoData = $this->fetchWithOAuthCredentials($resource, true)->decodeJson(); if ($this->fallbackDriver) { return false; } @@ -145,7 +156,7 @@ public function getComposerInformation($identifier) $hash = $branches[$label]; } - if (! isset($hash)) { + if (!isset($hash)) { $composer['support']['source'] = sprintf( 'https://%s/%s/%s/src', $this->originUrl, @@ -206,7 +217,7 @@ public function getFileContent($file, $identifier) $file ); - return $this->getContentsWithOAuthCredentials($resource); + return $this->fetchWithOAuthCredentials($resource)->getBody(); } /** @@ -231,7 +242,7 @@ public function getChangeDate($identifier) $this->repository, $identifier ); - $commit = JsonFile::parseJson($this->getContentsWithOAuthCredentials($resource), $resource); + $commit = $this->fetchWithOAuthCredentials($resource)->decodeJson(); return new \DateTime($commit['date']); } @@ -287,13 +298,13 @@ public function getTags() 'fields' => 'values.name,values.target.hash,next', 'sort' => '-target.date', ), - null, + '', '&' ) ); $hasNext = true; while ($hasNext) { - $tagsData = JsonFile::parseJson($this->getContentsWithOAuthCredentials($resource), $resource); + $tagsData = $this->fetchWithOAuthCredentials($resource)->decodeJson(); foreach ($tagsData['values'] as $data) { $this->tags[$data['name']] = $data['target']['hash']; } @@ -331,13 +342,13 @@ public function getBranches() 'fields' => 'values.name,values.target.hash,values.heads,next', 'sort' => '-target.date', ), - null, + '', '&' ) ); $hasNext = true; while ($hasNext) { - $branchData = JsonFile::parseJson($this->getContentsWithOAuthCredentials($resource), $resource); + $branchData = $this->fetchWithOAuthCredentials($resource)->decodeJson(); foreach ($branchData['values'] as $data) { // skip headless branches which seem to be deleted branches that bitbucket nevertheless returns in the API if ($this->vcsType === 'hg' && empty($data['heads'])) { @@ -363,14 +374,16 @@ public function getBranches() * @param string $url The URL of content * @param bool $fetchingRepoData * - * @return mixed The result + * @return Response The result + * + * @phpstan-impure */ - protected function getContentsWithOAuthCredentials($url, $fetchingRepoData = false) + protected function fetchWithOAuthCredentials($url, $fetchingRepoData = false) { try { return parent::getContents($url); } catch (TransportException $e) { - $bitbucketUtil = new Bitbucket($this->io, $this->config, $this->process, $this->remoteFilesystem); + $bitbucketUtil = new Bitbucket($this->io, $this->config, $this->process, $this->httpDownloader); if (403 === $e->getCode() || (401 === $e->getCode() && strpos($e->getMessage(), 'Could not authenticate against') === 0)) { if (!$this->io->hasAuthentication($this->originUrl) @@ -380,7 +393,9 @@ protected function getContentsWithOAuthCredentials($url, $fetchingRepoData = fal } if (!$this->io->isInteractive() && $fetchingRepoData) { - return $this->attemptCloneFallback(); + if ($this->attemptCloneFallback()) { + return new Response(array('url' => 'dummy'), 200, array(), 'null'); + } } } @@ -395,10 +410,15 @@ protected function getContentsWithOAuthCredentials($url, $fetchingRepoData = fal */ abstract protected function generateSshUrl(); + /** + * @phpstan-impure + */ protected function attemptCloneFallback() { try { $this->setupFallbackDriver($this->generateSshUrl()); + + return true; } catch (\RuntimeException $e) { $this->fallbackDriver = null; @@ -442,7 +462,7 @@ protected function getMainBranchData() $this->repository ); - $data = JsonFile::parseJson($this->getContentsWithOAuthCredentials($resource), $resource); + $data = $this->fetchWithOAuthCredentials($resource)->decodeJson(); if (isset($data['mainbranch'])) { return $data['mainbranch']; } diff --git a/app/vendor/composer/composer/src/Composer/Repository/Vcs/FossilDriver.php b/app/vendor/composer/composer/src/Composer/Repository/Vcs/FossilDriver.php index 8839deb5e..c4c468f40 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/Vcs/FossilDriver.php +++ b/app/vendor/composer/composer/src/Composer/Repository/Vcs/FossilDriver.php @@ -23,12 +23,16 @@ */ class FossilDriver extends VcsDriver { + /** @var array Map of tag name to identifier */ protected $tags; + /** @var array Map of branch name to identifier */ protected $branches; - protected $rootIdentifier; - protected $repoFile; + /** @var ?string */ + protected $rootIdentifier = null; + /** @var ?string */ + protected $repoFile = null; + /** @var string */ protected $checkoutDir; - protected $infoCache = array(); /** * {@inheritDoc} @@ -166,7 +170,7 @@ public function getFileContent($file, $identifier) public function getChangeDate($identifier) { $this->process->execute('fossil finfo -b -n 1 composer.json', $output, $this->checkoutDir); - list($ckout, $date, $message) = explode(' ', trim($output), 3); + list(, $date) = explode(' ', trim($output), 3); return new \DateTime($date, new \DateTimeZone('UTC')); } @@ -197,7 +201,6 @@ public function getBranches() { if (null === $this->branches) { $branches = array(); - $bookmarks = array(); $this->process->execute('fossil branch list', $output, $this->checkoutDir); foreach ($this->process->splitLines($output) as $branch) { diff --git a/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitBitbucketDriver.php index 82d934b5b..ab59c23e1 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -30,7 +30,11 @@ public function getRootIdentifier() } if (null === $this->rootIdentifier) { - if (! $this->getRepoData()) { + if (!$this->getRepoData()) { + if (!$this->fallbackDriver) { + throw new \LogicException('A fallback driver should be setup if getRepoData returns false'); + } + return $this->fallbackDriver->getRootIdentifier(); } @@ -75,8 +79,8 @@ protected function setupFallbackDriver($url) array('url' => $url), $this->io, $this->config, - $this->process, - $this->remoteFilesystem + $this->httpDownloader, + $this->process ); $this->fallbackDriver->initialize(); } diff --git a/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitDriver.php b/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitDriver.php index 9e3fa23f0..4fef3f702 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitDriver.php +++ b/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitDriver.php @@ -14,6 +14,7 @@ use Composer\Util\ProcessExecutor; use Composer\Util\Filesystem; +use Composer\Util\Url; use Composer\Util\Git as GitUtil; use Composer\IO\IOInterface; use Composer\Cache; @@ -24,12 +25,14 @@ */ class GitDriver extends VcsDriver { - protected $cache; + /** @var array Map of tag name to identifier */ protected $tags; + /** @var array Map of branch name to identifier */ protected $branches; + /** @var string */ protected $rootIdentifier; + /** @var string */ protected $repoDir; - protected $infoCache = array(); /** * {@inheritDoc} @@ -65,6 +68,9 @@ public function initialize() $gitUtil = new GitUtil($this->io, $this->config, $this->process, $fs); if (!$gitUtil->syncMirror($this->url, $this->repoDir)) { + if (!is_dir($this->repoDir)) { + throw new \RuntimeException('Failed to clone '.$this->url.' to read package information from it'); + } $this->io->writeError('Failed to update '.$this->url.', package information from this repository may be outdated'); } @@ -74,7 +80,8 @@ public function initialize() $this->getTags(); $this->getBranches(); - $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $cacheUrl)); + $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', Url::sanitize($cacheUrl))); + $this->cache->setReadOnly($this->config->get('cache-read-only')); } /** diff --git a/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitHubDriver.php b/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitHubDriver.php index 26904b12f..af0ef59da 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitHubDriver.php @@ -18,31 +18,40 @@ use Composer\Cache; use Composer\IO\IOInterface; use Composer\Util\GitHub; +use Composer\Util\Http\Response; /** * @author Jordi Boggiano */ class GitHubDriver extends VcsDriver { - protected $cache; + /** @var string */ protected $owner; + /** @var string */ protected $repository; + /** @var array Map of tag name to identifier */ protected $tags; + /** @var array Map of branch name to identifier */ protected $branches; + /** @var string */ protected $rootIdentifier; + /** @var mixed[] */ protected $repoData; - protected $hasIssues; - protected $infoCache = array(); + /** @var bool */ + protected $hasIssues = false; + /** @var bool */ protected $isPrivate = false; + /** @var bool */ private $isArchived = false; + /** @var array|false|null */ private $fundingInfo; /** * Git Driver * - * @var GitDriver + * @var ?GitDriver */ - protected $gitDriver; + protected $gitDriver = null; /** * {@inheritDoc} @@ -57,8 +66,9 @@ public function initialize() $this->originUrl = 'github.com'; } $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository); + $this->cache->setReadOnly($this->config->get('cache-read-only')); - if ( $this->config->get('use-github-api') === false || (isset($this->repoConfig['no-api']) && $this->repoConfig['no-api'] ) ){ + if ($this->config->get('use-github-api') === false || (isset($this->repoConfig['no-api']) && $this->repoConfig['no-api'])) { $this->setupGitDriver($this->url); return; @@ -150,7 +160,7 @@ public function getComposerInformation($identifier) if (!isset($this->infoCache[$identifier])) { if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) { - $composer = JsonFile::parseJson($res); + $composer = JsonFile::parseJson($res); } else { $composer = $this->getBaseComposerInformation($identifier); @@ -193,12 +203,10 @@ private function getFundingInfo() } foreach (array($this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/contents/.github/FUNDING.yml', $this->getApiUrl() . '/repos/'.$this->owner.'/.github/contents/FUNDING.yml') as $file) { - try { - $result = $this->remoteFilesystem->getContents($this->originUrl, $file, false, array( + $response = $this->httpDownloader->get($file, array( 'retry-auth-failure' => false, - )); - $response = json_decode($result, true); + ))->decodeJson(); } catch (TransportException $e) { continue; } @@ -276,7 +284,7 @@ public function getFileContent($file, $identifier) } $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/contents/' . $file . '?ref='.urlencode($identifier); - $resource = JsonFile::parseJson($this->getContents($resource)); + $resource = $this->getContents($resource)->decodeJson(); if (empty($resource['content']) || $resource['encoding'] !== 'base64' || !($content = base64_decode($resource['content']))) { throw new \RuntimeException('Could not retrieve ' . $file . ' for '.$identifier); } @@ -294,7 +302,7 @@ public function getChangeDate($identifier) } $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/commits/'.urlencode($identifier); - $commit = JsonFile::parseJson($this->getContents($resource), $resource); + $commit = $this->getContents($resource)->decodeJson(); return new \DateTime($commit['commit']['committer']['date']); } @@ -312,12 +320,13 @@ public function getTags() $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/tags?per_page=100'; do { - $tagsData = JsonFile::parseJson($this->getContents($resource), $resource); + $response = $this->getContents($resource); + $tagsData = $response->decodeJson(); foreach ($tagsData as $tag) { $this->tags[$tag['name']] = $tag['commit']['sha']; } - $resource = $this->getNextPage(); + $resource = $this->getNextPage($response); } while ($resource); } @@ -337,7 +346,8 @@ public function getBranches() $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/git/refs/heads?per_page=100'; do { - $branchData = JsonFile::parseJson($this->getContents($resource), $resource); + $response = $this->getContents($resource); + $branchData = $response->decodeJson(); foreach ($branchData as $branch) { $name = substr($branch['ref'], 11); if ($name !== 'gh-pages') { @@ -345,7 +355,7 @@ public function getBranches() } } - $resource = $this->getNextPage(); + $resource = $this->getNextPage($response); } while ($resource); } @@ -409,7 +419,7 @@ protected function getContents($url, $fetchingRepoData = false) try { return parent::getContents($url); } catch (TransportException $e) { - $gitHubUtil = new GitHub($this->io, $this->config, $this->process, $this->remoteFilesystem); + $gitHubUtil = new GitHub($this->io, $this->config, $this->process, $this->httpDownloader); switch ($e->getCode()) { case 401: @@ -424,16 +434,18 @@ protected function getContents($url, $fetchingRepoData = false) } if (!$this->io->isInteractive()) { - return $this->attemptCloneFallback(); + if ($this->attemptCloneFallback()) { + return new Response(array('url' => 'dummy'), 200, array(), 'null'); + } } $scopesIssued = array(); $scopesNeeded = array(); if ($headers = $e->getHeaders()) { - if ($scopes = $this->remoteFilesystem->findHeaderValue($headers, 'X-OAuth-Scopes')) { + if ($scopes = Response::findHeaderValue($headers, 'X-OAuth-Scopes')) { $scopesIssued = explode(' ', $scopes); } - if ($scopes = $this->remoteFilesystem->findHeaderValue($headers, 'X-Accepted-OAuth-Scopes')) { + if ($scopes = Response::findHeaderValue($headers, 'X-Accepted-OAuth-Scopes')) { $scopesNeeded = explode(' ', $scopes); } } @@ -452,7 +464,9 @@ protected function getContents($url, $fetchingRepoData = false) } if (!$this->io->isInteractive() && $fetchingRepoData) { - return $this->attemptCloneFallback(); + if ($this->attemptCloneFallback()) { + return new Response(array('url' => 'dummy'), 200, array(), 'null'); + } } $rateLimited = $gitHubUtil->isRateLimited($e->getHeaders()); @@ -498,7 +512,15 @@ protected function fetchRootIdentifier() $repoDataUrl = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository; - $this->repoData = JsonFile::parseJson($this->getContents($repoDataUrl, true), $repoDataUrl); + try { + $this->repoData = $this->getContents($repoDataUrl, true)->decodeJson(); + } catch (TransportException $e) { + if ($e->getCode() === 499) { + $this->attemptCloneFallback(); + } else { + throw $e; + } + } if (null === $this->repoData && null !== $this->gitDriver) { return; } @@ -529,7 +551,7 @@ protected function attemptCloneFallback() // are not interactive) then we fallback to GitDriver. $this->setupGitDriver($this->generateSshUrl()); - return; + return true; } catch (\RuntimeException $e) { $this->gitDriver = null; @@ -544,23 +566,24 @@ protected function setupGitDriver($url) array('url' => $url), $this->io, $this->config, - $this->process, - $this->remoteFilesystem + $this->httpDownloader, + $this->process ); $this->gitDriver->initialize(); } - protected function getNextPage() + protected function getNextPage(Response $response) { - $headers = $this->remoteFilesystem->getLastHeaders(); - foreach ($headers as $header) { - if (preg_match('{^link:\s*(.+?)\s*$}i', $header, $match)) { - $links = explode(',', $match[1]); - foreach ($links as $link) { - if (preg_match('{<(.+?)>; *rel="next"}', $link, $match)) { - return $match[1]; - } - } + $header = $response->getHeader('link'); + + if (!$header) { + return; + } + + $links = explode(',', $header); + foreach ($links as $link) { + if (preg_match('{<(.+?)>; *rel="next"}', $link, $match)) { + return $match[1]; } } } diff --git a/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitLabDriver.php b/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitLabDriver.php index bb5c2121e..8e9d9059d 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitLabDriver.php +++ b/app/vendor/composer/composer/src/Composer/Repository/Vcs/GitLabDriver.php @@ -17,8 +17,9 @@ use Composer\IO\IOInterface; use Composer\Json\JsonFile; use Composer\Downloader\TransportException; -use Composer\Util\RemoteFilesystem; +use Composer\Util\HttpDownloader; use Composer\Util\GitLab; +use Composer\Util\Http\Response; /** * Driver for GitLab API, use the Git driver for local checkouts. @@ -28,36 +29,45 @@ */ class GitLabDriver extends VcsDriver { + /** + * @var string + * @phpstan-var 'https'|'http' + */ private $scheme; + /** @var string */ private $namespace; + /** @var string */ private $repository; /** - * @var array Project data returned by GitLab API + * @var mixed[] Project data returned by GitLab API */ private $project; /** - * @var array Keeps commits returned by GitLab API + * @var array Keeps commits returned by GitLab API */ private $commits = array(); - /** - * @var array List of tag => reference - */ + /** @var array Map of tag name to identifier */ private $tags; - /** - * @var array List of branch => reference - */ + /** @var array Map of branch name to identifier */ private $branches; /** * Git Driver * - * @var GitDriver + * @var ?GitDriver */ - protected $gitDriver; + protected $gitDriver = null; + + /** + * Protocol to force use of for repository URLs. + * + * @var string One of ssh, http + */ + protected $protocol; /** * Defaults to true unless we can make sure it is public @@ -94,7 +104,15 @@ public function initialize() ? $match['scheme'] : (isset($this->repoConfig['secure-http']) && $this->repoConfig['secure-http'] === false ? 'http' : 'https') ; - $this->originUrl = $this->determineOrigin($configuredDomains, $guessedDomain, $urlParts, $match['port']); + $this->originUrl = self::determineOrigin($configuredDomains, $guessedDomain, $urlParts, $match['port']); + + if ($protocol = $this->config->get('gitlab-protocol')) { + // https treated as a synonym for http. + if (!in_array($protocol, array('git', 'http', 'https'))) { + throw new \RuntimeException('gitlab-protocol must be one of git, http.'); + } + $this->protocol = $protocol === 'git' ? 'ssh' : 'http'; + } if (false !== strpos($this->originUrl, ':') || false !== strpos($this->originUrl, '/')) { $this->hasNonstandardOrigin = true; @@ -104,19 +122,20 @@ public function initialize() $this->repository = preg_replace('#(\.git)$#', '', $match['repo']); $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->namespace.'/'.$this->repository); + $this->cache->setReadOnly($this->config->get('cache-read-only')); $this->fetchProject(); } /** - * Updates the RemoteFilesystem instance. + * Updates the HttpDownloader instance. * Mainly useful for tests. * * @internal */ - public function setRemoteFilesystem(RemoteFilesystem $remoteFilesystem) + public function setHttpDownloader(HttpDownloader $httpDownloader) { - $this->remoteFilesystem = $remoteFilesystem; + $this->httpDownloader = $httpDownloader; } /** @@ -130,7 +149,7 @@ public function getComposerInformation($identifier) if (!isset($this->infoCache[$identifier])) { if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) { - $composer = JsonFile::parseJson($res); + $composer = JsonFile::parseJson($res); } else { $composer = $this->getBaseComposerInformation($identifier); @@ -175,7 +194,7 @@ public function getFileContent($file, $identifier) $resource = $this->getApiUrl().'/repository/files/'.$this->urlEncodeAll($file).'/raw?ref='.$identifier; try { - $content = $this->getContents($resource); + $content = $this->getContents($resource)->getBody(); } catch (TransportException $e) { if ($e->getCode() !== 404) { throw $e; @@ -208,6 +227,10 @@ public function getChangeDate($identifier) */ public function getRepositoryUrl() { + if ($this->protocol) { + return $this->project["{$this->protocol}_url_to_repo"]; + } + return $this->isPrivate ? $this->project['ssh_url_to_repo'] : $this->project['http_url_to_repo']; } @@ -329,7 +352,8 @@ protected function getReferences($type) $references = array(); do { - $data = JsonFile::parseJson($this->getContents($resource), $resource); + $response = $this->getContents($resource); + $data = $response->decodeJson(); foreach ($data as $datum) { $references[$datum['name']] = $datum['commit']['id']; @@ -340,7 +364,7 @@ protected function getReferences($type) } if (count($data) >= $perPage) { - $resource = $this->getNextPage(); + $resource = $this->getNextPage($response); } else { $resource = false; } @@ -353,30 +377,30 @@ protected function fetchProject() { // we need to fetch the default branch from the api $resource = $this->getApiUrl(); - $this->project = JsonFile::parseJson($this->getContents($resource, true), $resource); + $this->project = $this->getContents($resource, true)->decodeJson(); if (isset($this->project['visibility'])) { $this->isPrivate = $this->project['visibility'] !== 'public'; } else { - // client is not authendicated, therefore repository has to be public + // client is not authenticated, therefore repository has to be public $this->isPrivate = false; } } protected function attemptCloneFallback() { - try { - if ($this->isPrivate === false) { - $url = $this->generatePublicUrl(); - } else { - $url = $this->generateSshUrl(); - } + if ($this->isPrivate === false) { + $url = $this->generatePublicUrl(); + } else { + $url = $this->generateSshUrl(); + } + try { // If this repository may be private and we // cannot ask for authentication credentials (because we // are not interactive) then we fallback to GitDriver. $this->setupGitDriver($url); - return; + return true; } catch (\RuntimeException $e) { $this->gitDriver = null; @@ -410,8 +434,8 @@ protected function setupGitDriver($url) array('url' => $url), $this->io, $this->config, - $this->process, - $this->remoteFilesystem + $this->httpDownloader, + $this->process ); $this->gitDriver->initialize(); } @@ -422,10 +446,10 @@ protected function setupGitDriver($url) protected function getContents($url, $fetchingRepoData = false) { try { - $res = parent::getContents($url); + $response = parent::getContents($url); if ($fetchingRepoData) { - $json = JsonFile::parseJson($res, $url); + $json = $response->decodeJson(); // Accessing the API with a token with Guest (10) access will return // more data than unauthenticated access but no default_branch data @@ -460,9 +484,9 @@ protected function getContents($url, $fetchingRepoData = false) } } - return $res; + return $response; } catch (TransportException $e) { - $gitLabUtil = new GitLab($this->io, $this->config, $this->process, $this->remoteFilesystem); + $gitLabUtil = new GitLab($this->io, $this->config, $this->process, $this->httpDownloader); switch ($e->getCode()) { case 401: @@ -477,7 +501,9 @@ protected function getContents($url, $fetchingRepoData = false) } if (!$this->io->isInteractive()) { - return $this->attemptCloneFallback(); + if ($this->attemptCloneFallback()) { + return new Response(array('url' => 'dummy'), 200, array(), 'null'); + } } $this->io->writeError('Failed to download ' . $this->namespace . '/' . $this->repository . ':' . $e->getMessage() . ''); $gitLabUtil->authorizeOAuthInteractively($this->scheme, $this->originUrl, 'Your credentials are required to fetch private repository metadata ('.$this->url.')'); @@ -490,7 +516,9 @@ protected function getContents($url, $fetchingRepoData = false) } if (!$this->io->isInteractive() && $fetchingRepoData) { - return $this->attemptCloneFallback(); + if ($this->attemptCloneFallback()) { + return new Response(array('url' => 'dummy'), 200, array(), 'null'); + } } throw $e; @@ -530,17 +558,14 @@ public static function supports(IOInterface $io, Config $config, $url, $deep = f return true; } - private function getNextPage() + protected function getNextPage(Response $response) { - $headers = $this->remoteFilesystem->getLastHeaders(); - foreach ($headers as $header) { - if (preg_match('{^link:\s*(.+?)\s*$}i', $header, $match)) { - $links = explode(',', $match[1]); - foreach ($links as $link) { - if (preg_match('{<(.+?)>; *rel="next"}', $link, $match)) { - return $match[1]; - } - } + $header = $response->getHeader('link'); + + $links = explode(',', $header); + foreach ($links as $link) { + if (preg_match('{<(.+?)>; *rel="next"}', $link, $match)) { + return $match[1]; } } } @@ -559,6 +584,7 @@ private static function determineOrigin(array $configuredDomains, $guessedDomain if ($portNumber) { return $guessedDomain.':'.$portNumber; } + return $guessedDomain; } diff --git a/app/vendor/composer/composer/src/Composer/Repository/Vcs/HgBitbucketDriver.php b/app/vendor/composer/composer/src/Composer/Repository/Vcs/HgBitbucketDriver.php index 1cf630da9..71505cf52 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/Vcs/HgBitbucketDriver.php +++ b/app/vendor/composer/composer/src/Composer/Repository/Vcs/HgBitbucketDriver.php @@ -30,7 +30,11 @@ public function getRootIdentifier() } if (null === $this->rootIdentifier) { - if (! $this->getRepoData()) { + if (!$this->getRepoData()) { + if (!$this->fallbackDriver) { + throw new \LogicException('A fallback driver should be setup if getRepoData returns false'); + } + return $this->fallbackDriver->getRootIdentifier(); } @@ -75,8 +79,8 @@ protected function setupFallbackDriver($url) array('url' => $url), $this->io, $this->config, - $this->process, - $this->remoteFilesystem + $this->httpDownloader, + $this->process ); $this->fallbackDriver->initialize(); } diff --git a/app/vendor/composer/composer/src/Composer/Repository/Vcs/HgDriver.php b/app/vendor/composer/composer/src/Composer/Repository/Vcs/HgDriver.php index e739d4cda..56e35ea30 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/Vcs/HgDriver.php +++ b/app/vendor/composer/composer/src/Composer/Repository/Vcs/HgDriver.php @@ -24,11 +24,14 @@ */ class HgDriver extends VcsDriver { + /** @var array Map of tag name to identifier */ protected $tags; + /** @var array Map of branch name to identifier */ protected $branches; + /** @var string */ protected $rootIdentifier; + /** @var string */ protected $repoDir; - protected $infoCache = array(); /** * {@inheritDoc} @@ -126,7 +129,7 @@ public function getFileContent($file, $identifier) $this->process->execute($resource, $content, $this->repoDir); if (!trim($content)) { - return; + return null; } return $content; @@ -228,8 +231,8 @@ public static function supports(IOInterface $io, Config $config, $url, $deep = f return false; } - $processExecutor = new ProcessExecutor($io); - $exit = $processExecutor->execute(sprintf('hg identify -- %s', ProcessExecutor::escape($url)), $ignored); + $process = new ProcessExecutor($io); + $exit = $process->execute(sprintf('hg identify -- %s', ProcessExecutor::escape($url)), $ignored); return $exit === 0; } diff --git a/app/vendor/composer/composer/src/Composer/Repository/Vcs/PerforceDriver.php b/app/vendor/composer/composer/src/Composer/Repository/Vcs/PerforceDriver.php index 09b5d4b16..76249fd54 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/app/vendor/composer/composer/src/Composer/Repository/Vcs/PerforceDriver.php @@ -23,10 +23,12 @@ */ class PerforceDriver extends VcsDriver { + /** @var string */ protected $depot; + /** @var string */ protected $branch; - /** @var Perforce */ - protected $perforce; + /** @var ?Perforce */ + protected $perforce = null; /** * {@inheritDoc} @@ -116,14 +118,12 @@ public function getDist($identifier) */ public function getSource($identifier) { - $source = array( + return array( 'type' => 'perforce', 'url' => $this->repoConfig['url'], 'reference' => $identifier, 'p4user' => $this->perforce->getUser(), ); - - return $source; } /** @@ -140,7 +140,6 @@ public function getUrl() public function hasComposerFile($identifier) { $composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier); - $composerInfoIdentifier = $identifier; return !empty($composerInfo); } @@ -150,7 +149,7 @@ public function hasComposerFile($identifier) */ public function getContents($url) { - return false; + throw new \BadMethodCallException('Not implemented/used in PerforceDriver'); } /** diff --git a/app/vendor/composer/composer/src/Composer/Repository/Vcs/SvnDriver.php b/app/vendor/composer/composer/src/Composer/Repository/Vcs/SvnDriver.php index 216c886ef..9ea596115 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/Vcs/SvnDriver.php +++ b/app/vendor/composer/composer/src/Composer/Repository/Vcs/SvnDriver.php @@ -17,6 +17,7 @@ use Composer\Json\JsonFile; use Composer\Util\ProcessExecutor; use Composer\Util\Filesystem; +use Composer\Util\Url; use Composer\Util\Svn as SvnUtil; use Composer\IO\IOInterface; use Composer\Downloader\TransportException; @@ -27,20 +28,24 @@ */ class SvnDriver extends VcsDriver { - /** - * @var Cache - */ - protected $cache; + /** @var string */ protected $baseUrl; + /** @var array Map of tag name to identifier */ protected $tags; + /** @var array Map of branch name to identifier */ protected $branches; + /** @var ?string */ protected $rootIdentifier; - protected $infoCache = array(); + /** @var string|false */ protected $trunkPath = 'trunk'; + /** @var string */ protected $branchesPath = 'branches'; + /** @var string */ protected $tagsPath = 'tags'; + /** @var string */ protected $packagePath = ''; + /** @var bool */ protected $cacheCredentials = true; /** @@ -77,7 +82,8 @@ public function initialize() $this->baseUrl = substr($this->url, 0, $pos); } - $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->baseUrl)); + $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', Url::sanitize($this->baseUrl))); + $this->cache->setReadOnly($this->config->get('cache-read-only')); $this->getBranches(); $this->getTags(); @@ -224,7 +230,7 @@ public function getTags() foreach ($this->process->splitLines($output) as $line) { $line = trim($line); if ($line && preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match)) { - if (isset($match[1]) && isset($match[2]) && $match[2] !== './') { + if (isset($match[1], $match[2]) && $match[2] !== './') { $this->tags[rtrim($match[2], '/')] = $this->buildIdentifier( '/' . $this->tagsPath . '/' . $match[2], $match[1] @@ -258,7 +264,7 @@ public function getBranches() foreach ($this->process->splitLines($output) as $line) { $line = trim($line); if ($line && preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match)) { - if (isset($match[1]) && isset($match[2]) && $match[2] === './') { + if (isset($match[1], $match[2]) && $match[2] === './') { $this->branches['trunk'] = $this->buildIdentifier( '/' . $this->trunkPath, $match[1] @@ -277,7 +283,7 @@ public function getBranches() foreach ($this->process->splitLines(trim($output)) as $line) { $line = trim($line); if ($line && preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match)) { - if (isset($match[1]) && isset($match[2]) && $match[2] !== './') { + if (isset($match[1], $match[2]) && $match[2] !== './') { $this->branches[rtrim($match[2], '/')] = $this->buildIdentifier( '/' . $this->branchesPath . '/' . $match[2], $match[1] @@ -307,9 +313,8 @@ public static function supports(IOInterface $io, Config $config, $url, $deep = f return false; } - $processExecutor = new ProcessExecutor($io); - - $exit = $processExecutor->execute( + $process = new ProcessExecutor($io); + $exit = $process->execute( "svn info --non-interactive -- ".ProcessExecutor::escape($url), $ignoredOutput ); @@ -320,14 +325,14 @@ public static function supports(IOInterface $io, Config $config, $url, $deep = f } // Subversion client 1.7 and older - if (false !== stripos($processExecutor->getErrorOutput(), 'authorization failed:')) { + if (false !== stripos($process->getErrorOutput(), 'authorization failed:')) { // This is likely a remote Subversion repository that requires // authentication. We will handle actual authentication later. return true; } // Subversion client 1.8 and newer - if (false !== stripos($processExecutor->getErrorOutput(), 'Authentication failed')) { + if (false !== stripos($process->getErrorOutput(), 'Authentication failed')) { // This is likely a remote Subversion or newer repository that requires // authentication. We will handle actual authentication later. return true; diff --git a/app/vendor/composer/composer/src/Composer/Repository/Vcs/VcsDriver.php b/app/vendor/composer/composer/src/Composer/Repository/Vcs/VcsDriver.php index 5227630f6..7e287e33c 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/Vcs/VcsDriver.php +++ b/app/vendor/composer/composer/src/Composer/Repository/Vcs/VcsDriver.php @@ -15,12 +15,12 @@ use Composer\Cache; use Composer\Downloader\TransportException; use Composer\Config; -use Composer\Factory; use Composer\IO\IOInterface; use Composer\Json\JsonFile; use Composer\Util\ProcessExecutor; -use Composer\Util\RemoteFilesystem; +use Composer\Util\HttpDownloader; use Composer\Util\Filesystem; +use Composer\Util\Http\Response; /** * A driver implementation for driver with authentication interaction. @@ -33,7 +33,7 @@ abstract class VcsDriver implements VcsDriverInterface protected $url; /** @var string */ protected $originUrl; - /** @var array */ + /** @var array */ protected $repoConfig; /** @var IOInterface */ protected $io; @@ -41,23 +41,23 @@ abstract class VcsDriver implements VcsDriverInterface protected $config; /** @var ProcessExecutor */ protected $process; - /** @var RemoteFilesystem */ - protected $remoteFilesystem; - /** @var array */ + /** @var HttpDownloader */ + protected $httpDownloader; + /** @var array */ protected $infoCache = array(); - /** @var Cache */ + /** @var ?Cache */ protected $cache; /** * Constructor. * - * @param array $repoConfig The repository configuration - * @param IOInterface $io The IO instance - * @param Config $config The composer configuration - * @param ProcessExecutor $process Process instance, injectable for mocking - * @param RemoteFilesystem $remoteFilesystem Remote Filesystem, injectable for mocking + * @param array $repoConfig The repository configuration + * @param IOInterface $io The IO instance + * @param Config $config The composer configuration + * @param HttpDownloader $httpDownloader Remote Filesystem, injectable for mocking + * @param ProcessExecutor $process Process instance, injectable for mocking */ - final public function __construct(array $repoConfig, IOInterface $io, Config $config, ProcessExecutor $process = null, RemoteFilesystem $remoteFilesystem = null) + final public function __construct(array $repoConfig, IOInterface $io, Config $config, HttpDownloader $httpDownloader, ProcessExecutor $process) { if (Filesystem::isLocalPath($repoConfig['url'])) { $repoConfig['url'] = Filesystem::getPlatformPath($repoConfig['url']); @@ -68,8 +68,8 @@ final public function __construct(array $repoConfig, IOInterface $io, Config $co $this->repoConfig = $repoConfig; $this->io = $io; $this->config = $config; - $this->process = $process ?: new ProcessExecutor($io); - $this->remoteFilesystem = $remoteFilesystem ?: Factory::createRemoteFilesystem($this->io, $config); + $this->httpDownloader = $httpDownloader; + $this->process = $process; } /** @@ -80,7 +80,7 @@ final public function __construct(array $repoConfig, IOInterface $io, Config $co */ protected function shouldCache($identifier) { - return $this->cache && preg_match('{[a-f0-9]{40}}i', $identifier); + return $this->cache && preg_match('{^[a-f0-9]{40}$}iD', $identifier); } /** @@ -156,13 +156,14 @@ protected function getScheme() * * @param string $url The URL of content * - * @return mixed The result + * @return Response + * @throws TransportException */ protected function getContents($url) { $options = isset($this->repoConfig['options']) ? $this->repoConfig['options'] : array(); - return $this->remoteFilesystem->getContents($this->originUrl, $url, false, $options); + return $this->httpDownloader->get($url, $options); } /** diff --git a/app/vendor/composer/composer/src/Composer/Repository/Vcs/VcsDriverInterface.php b/app/vendor/composer/composer/src/Composer/Repository/Vcs/VcsDriverInterface.php index 5e3bcec68..22cb33007 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/Vcs/VcsDriverInterface.php +++ b/app/vendor/composer/composer/src/Composer/Repository/Vcs/VcsDriverInterface.php @@ -36,17 +36,17 @@ public function getComposerInformation($identifier); /** * Return the content of $file or null if the file does not exist. * - * @param string $file - * @param string $identifier - * @return string + * @param string $file + * @param string $identifier + * @return string|null */ public function getFileContent($file, $identifier); /** * Get the changedate for $identifier. * - * @param string $identifier - * @return \DateTime + * @param string $identifier + * @return \DateTime|null */ public function getChangeDate($identifier); @@ -72,8 +72,8 @@ public function getBranches(); public function getTags(); /** - * @param string $identifier Any identifier to a specific branch/tag/commit - * @return array With type, url reference and shasum keys. + * @param string $identifier Any identifier to a specific branch/tag/commit + * @return array|null With type, url reference and shasum keys. */ public function getDist($identifier); diff --git a/app/vendor/composer/composer/src/Composer/Repository/VcsRepository.php b/app/vendor/composer/composer/src/Composer/Repository/VcsRepository.php index f248420b0..1b7c72949 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/VcsRepository.php +++ b/app/vendor/composer/composer/src/Composer/Repository/VcsRepository.php @@ -20,6 +20,9 @@ use Composer\Package\Loader\InvalidPackageException; use Composer\Package\Loader\LoaderInterface; use Composer\EventDispatcher\EventDispatcher; +use Composer\Util\ProcessExecutor; +use Composer\Util\HttpDownloader; +use Composer\Util\Url; use Composer\Semver\Constraint\Constraint; use Composer\IO\IOInterface; use Composer\Config; @@ -29,26 +32,44 @@ */ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInterface { + /** @var string */ protected $url; + /** @var ?string */ protected $packageName; + /** @var bool */ protected $isVerbose; + /** @var bool */ protected $isVeryVerbose; + /** @var IOInterface */ protected $io; + /** @var Config */ protected $config; + /** @var VersionParser */ protected $versionParser; + /** @var string */ protected $type; + /** @var ?LoaderInterface */ protected $loader; + /** @var array */ protected $repoConfig; + /** @var HttpDownloader */ + protected $httpDownloader; + /** @var ProcessExecutor */ + protected $processExecutor; + /** @var bool */ protected $branchErrorOccurred = false; + /** @var array */ private $drivers; - /** @var VcsDriverInterface */ + /** @var ?VcsDriverInterface */ private $driver; - /** @var VersionCacheInterface */ + /** @var ?VersionCacheInterface */ private $versionCache; + /** @var string[] */ private $emptyReferences = array(); + /** @var array<'tags'|'branches', array> */ private $versionTransportExceptions = array(); - public function __construct(array $repoConfig, IOInterface $io, Config $config, EventDispatcher $dispatcher = null, array $drivers = null, VersionCacheInterface $versionCache = null) + public function __construct(array $repoConfig, IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $dispatcher = null, ProcessExecutor $process = null, array $drivers = null, VersionCacheInterface $versionCache = null) { parent::__construct(); $this->drivers = $drivers ?: array( @@ -72,6 +93,19 @@ public function __construct(array $repoConfig, IOInterface $io, Config $config, $this->config = $config; $this->repoConfig = $repoConfig; $this->versionCache = $versionCache; + $this->httpDownloader = $httpDownloader; + $this->processExecutor = $process ?: new ProcessExecutor($io); + } + + public function getRepoName() + { + $driverClass = get_class($this->getDriver()); + $driverType = array_search($driverClass, $this->drivers); + if (!$driverType) { + $driverType = $driverClass; + } + + return 'vcs repo ('.$driverType.' '.Url::sanitize($this->url).')'; } public function getRepoConfig() @@ -92,7 +126,7 @@ public function getDriver() if (isset($this->drivers[$this->type])) { $class = $this->drivers[$this->type]; - $this->driver = new $class($this->repoConfig, $this->io, $this->config); + $this->driver = new $class($this->repoConfig, $this->io, $this->config, $this->httpDownloader, $this->processExecutor); $this->driver->initialize(); return $this->driver; @@ -100,7 +134,7 @@ public function getDriver() foreach ($this->drivers as $driver) { if ($driver::supports($this->io, $this->config, $this->url)) { - $this->driver = new $driver($this->repoConfig, $this->io, $this->config); + $this->driver = new $driver($this->repoConfig, $this->io, $this->config, $this->httpDownloader, $this->processExecutor); $this->driver->initialize(); return $this->driver; @@ -109,7 +143,7 @@ public function getDriver() foreach ($this->drivers as $driver) { if ($driver::supports($this->io, $this->config, $this->url, true)) { - $this->driver = new $driver($this->repoConfig, $this->io, $this->config); + $this->driver = new $driver($this->repoConfig, $this->io, $this->config, $this->httpDownloader, $this->processExecutor); $this->driver->initialize(); return $this->driver; @@ -149,8 +183,10 @@ protected function initialize() $this->loader = new ArrayLoader($this->versionParser); } + $hasRootIdentifierComposerJson = false; try { - if ($driver->hasComposerFile($driver->getRootIdentifier())) { + $hasRootIdentifierComposerJson = $driver->hasComposerFile($driver->getRootIdentifier()); + if ($hasRootIdentifierComposerJson) { $data = $driver->getComposerInformation($driver->getRootIdentifier()); $this->packageName = !empty($data['name']) ? $data['name'] : null; } @@ -176,7 +212,8 @@ protected function initialize() $this->addPackage($cachedPackage); continue; - } elseif ($cachedPackage === false) { + } + if ($cachedPackage === false) { $this->emptyReferences[] = $identifier; continue; @@ -211,6 +248,9 @@ protected function initialize() $data['version'] = preg_replace('{[.-]?dev$}i', '', $data['version']); $data['version_normalized'] = preg_replace('{(^dev-|[.-]?dev$)}i', '', $data['version_normalized']); + // make sure tag do not contain the default-branch marker + unset($data['default-branch']); + // broken package, version doesn't match tag if ($data['version_normalized'] !== $parsedTag) { if ($isVeryVerbose) { @@ -223,7 +263,7 @@ protected function initialize() continue; } - $tagPackageName = isset($data['name']) ? $data['name'] : $this->packageName; + $tagPackageName = $this->packageName ?: (isset($data['name']) ? $data['name'] : ''); if ($existingPackage = $this->findPackage($tagPackageName, $data['version_normalized'])) { if ($isVeryVerbose) { $this->io->writeError('Skipped tag '.$tag.', it conflicts with an another tag ('.$existingPackage->getPrettyVersion().') as both resolve to '.$data['version_normalized'].' internally'); @@ -242,6 +282,9 @@ protected function initialize() if ($e->getCode() === 404) { $this->emptyReferences[] = $identifier; } + if (in_array($e->getCode(), array(401, 403, 429), true)) { + throw $e; + } } if ($isVeryVerbose) { $this->io->writeError('Skipped tag '.$tag.', '.($e instanceof TransportException ? 'no composer file was found (' . $e->getCode() . ' HTTP status code)' : $e->getMessage()).''); @@ -255,6 +298,11 @@ protected function initialize() } $branches = $driver->getBranches(); + // make sure the root identifier branch gets loaded first + if ($hasRootIdentifierComposerJson && isset($branches[$driver->getRootIdentifier()])) { + $branches = array($driver->getRootIdentifier() => $branches[$driver->getRootIdentifier()]) + $branches; + } + foreach ($branches as $branch => $identifier) { $msg = 'Reading composer.json of ' . ($this->packageName ?: $this->url) . ' (' . $branch . ')'; if ($isVeryVerbose) { @@ -263,13 +311,6 @@ protected function initialize() $this->io->overwriteError($msg, false); } - if ($branch === 'trunk' && isset($branches['master'])) { - if ($isVeryVerbose) { - $this->io->writeError('Skipped branch '.$branch.', can not parse both master and trunk branches as they both resolve to 9999999-dev internally'); - } - continue; - } - if (!$parsedBranch = $this->validateBranch($branch)) { if ($isVeryVerbose) { $this->io->writeError('Skipped branch '.$branch.', invalid name'); @@ -278,19 +319,20 @@ protected function initialize() } // make sure branch packages have a dev flag - if ('dev-' === substr($parsedBranch, 0, 4) || '9999999-dev' === $parsedBranch) { + if (strpos($parsedBranch, 'dev-') === 0 || VersionParser::DEFAULT_BRANCH_ALIAS === $parsedBranch) { $version = 'dev-' . $branch; } else { - $prefix = substr($branch, 0, 1) === 'v' ? 'v' : ''; + $prefix = strpos($branch, 'v') === 0 ? 'v' : ''; $version = $prefix . preg_replace('{(\.9{7})+}', '.x', $parsedBranch); } - $cachedPackage = $this->getCachedPackageVersion($version, $identifier, $isVerbose, $isVeryVerbose); + $cachedPackage = $this->getCachedPackageVersion($version, $identifier, $isVerbose, $isVeryVerbose, $driver->getRootIdentifier() === $branch); if ($cachedPackage) { $this->addPackage($cachedPackage); continue; - } elseif ($cachedPackage === false) { + } + if ($cachedPackage === false) { $this->emptyReferences[] = $identifier; continue; @@ -309,6 +351,11 @@ protected function initialize() $data['version'] = $version; $data['version_normalized'] = $parsedBranch; + unset($data['default-branch']); + if ($driver->getRootIdentifier() === $branch) { + $data['default-branch'] = true; + } + if ($isVeryVerbose) { $this->io->writeError('Importing branch '.$branch.' ('.$data['version'].')'); } @@ -324,6 +371,9 @@ protected function initialize() if ($e->getCode() === 404) { $this->emptyReferences[] = $identifier; } + if (in_array($e->getCode(), array(401, 403, 429), true)) { + throw $e; + } if ($isVeryVerbose) { $this->io->writeError('Skipped branch '.$branch.', no composer file was found (' . $e->getCode() . ' HTTP status code)'); } @@ -352,6 +402,8 @@ protected function initialize() protected function preProcess(VcsDriverInterface $driver, array $data, $identifier) { // keep the name of the main identifier for all packages + // this ensures that a package can be renamed in one place and that all old tags + // will still be installable using that new name without requiring re-tagging $dataPackageName = isset($data['name']) ? $data['name'] : null; $data['name'] = $this->packageName ?: $dataPackageName; @@ -390,7 +442,7 @@ private function validateTag($version) return false; } - private function getCachedPackageVersion($version, $identifier, $isVerbose, $isVeryVerbose) + private function getCachedPackageVersion($version, $identifier, $isVerbose, $isVeryVerbose, $isDefaultBranch = false) { if (!$this->versionCache) { return; @@ -413,6 +465,11 @@ private function getCachedPackageVersion($version, $identifier, $isVerbose, $isV $this->io->overwriteError($msg, false); } + unset($cachedPackage['default-branch']); + if ($isDefaultBranch) { + $cachedPackage['default-branch'] = true; + } + if ($existingPackage = $this->findPackage($cachedPackage['name'], new Constraint('=', $cachedPackage['version_normalized']))) { if ($isVeryVerbose) { $this->io->writeError('Skipped cached version '.$version.', it conflicts with an another tag ('.$existingPackage->getPrettyVersion().') as both resolve to '.$cachedPackage['version_normalized'].' internally'); diff --git a/app/vendor/composer/composer/src/Composer/Repository/VersionCacheInterface.php b/app/vendor/composer/composer/src/Composer/Repository/VersionCacheInterface.php index 41d485c64..3b05fb8ff 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/VersionCacheInterface.php +++ b/app/vendor/composer/composer/src/Composer/Repository/VersionCacheInterface.php @@ -15,8 +15,8 @@ interface VersionCacheInterface { /** - * @param string $version - * @param string $identifier + * @param string $version + * @param string $identifier * @return array|null|false Package version data if found, false to indicate the identifier is known but has no package, null for an unknown identifier */ public function getVersionPackage($version, $identifier); diff --git a/app/vendor/composer/composer/src/Composer/Repository/WritableArrayRepository.php b/app/vendor/composer/composer/src/Composer/Repository/WritableArrayRepository.php index 041e40562..de253112d 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/WritableArrayRepository.php +++ b/app/vendor/composer/composer/src/Composer/Repository/WritableArrayRepository.php @@ -13,6 +13,7 @@ namespace Composer\Repository; use Composer\Package\AliasPackage; +use Composer\Installer\InstallationManager; /** * Writable array repository. @@ -21,10 +22,31 @@ */ class WritableArrayRepository extends ArrayRepository implements WritableRepositoryInterface { + /** + * @var string[] + */ + protected $devPackageNames = array(); + + /** + * {@inheritDoc} + */ + public function setDevPackageNames(array $devPackageNames) + { + $this->devPackageNames = $devPackageNames; + } + + /** + * {@inheritDoc} + */ + public function getDevPackageNames() + { + return $this->devPackageNames; + } + /** * {@inheritDoc} */ - public function write() + public function write($devMode, InstallationManager $installationManager) { } diff --git a/app/vendor/composer/composer/src/Composer/Repository/WritableRepositoryInterface.php b/app/vendor/composer/composer/src/Composer/Repository/WritableRepositoryInterface.php index 4500005d9..f1d8b9e95 100644 --- a/app/vendor/composer/composer/src/Composer/Repository/WritableRepositoryInterface.php +++ b/app/vendor/composer/composer/src/Composer/Repository/WritableRepositoryInterface.php @@ -13,6 +13,7 @@ namespace Composer\Repository; use Composer\Package\PackageInterface; +use Composer\Installer\InstallationManager; /** * Writable repository interface. @@ -23,8 +24,10 @@ interface WritableRepositoryInterface extends RepositoryInterface { /** * Writes repository (f.e. to the disc). + * + * @param bool $devMode Whether dev requirements were included or not in this installation */ - public function write(); + public function write($devMode, InstallationManager $installationManager); /** * Adds package to the repository. @@ -51,4 +54,14 @@ public function getCanonicalPackages(); * Forces a reload of all packages. */ public function reload(); + + /** + * @param string[] $devPackageNames + */ + public function setDevPackageNames(array $devPackageNames); + + /** + * @return string[] Names of dependencies installed through require-dev + */ + public function getDevPackageNames(); } diff --git a/app/vendor/composer/composer/src/Composer/Script/CommandEvent.php b/app/vendor/composer/composer/src/Composer/Script/CommandEvent.php deleted file mode 100644 index 84c52008c..000000000 --- a/app/vendor/composer/composer/src/Composer/Script/CommandEvent.php +++ /dev/null @@ -1,22 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Script; - -/** - * The Command Event. - * - * @deprecated use Composer\Script\Event instead - */ -class CommandEvent extends Event -{ -} diff --git a/app/vendor/composer/composer/src/Composer/Script/Event.php b/app/vendor/composer/composer/src/Composer/Script/Event.php index 5fab172bf..af8bf55e7 100644 --- a/app/vendor/composer/composer/src/Composer/Script/Event.php +++ b/app/vendor/composer/composer/src/Composer/Script/Event.php @@ -60,7 +60,6 @@ public function __construct($name, Composer $composer, IOInterface $io, $devMode $this->composer = $composer; $this->io = $io; $this->devMode = $devMode; - $this->originatingEvent = null; } /** @@ -96,7 +95,7 @@ public function isDevMode() /** * Set the originating event. * - * @return \Composer\EventDispatcher\Event|null + * @return ?BaseEvent */ public function getOriginatingEvent() { @@ -106,7 +105,7 @@ public function getOriginatingEvent() /** * Set the originating event. * - * @param \Composer\EventDispatcher\Event $event + * @param BaseEvent $event * @return $this */ public function setOriginatingEvent(BaseEvent $event) @@ -119,8 +118,8 @@ public function setOriginatingEvent(BaseEvent $event) /** * Returns the upper-most event in chain. * - * @param \Composer\EventDispatcher\Event $event - * @return \Composer\EventDispatcher\Event + * @param BaseEvent $event + * @return BaseEvent */ private function calculateOriginatingEvent(BaseEvent $event) { diff --git a/app/vendor/composer/composer/src/Composer/Script/PackageEvent.php b/app/vendor/composer/composer/src/Composer/Script/PackageEvent.php deleted file mode 100644 index 531b86a40..000000000 --- a/app/vendor/composer/composer/src/Composer/Script/PackageEvent.php +++ /dev/null @@ -1,24 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Script; - -use Composer\Installer\PackageEvent as BasePackageEvent; - -/** - * The Package Event. - * - * @deprecated Use Composer\Installer\PackageEvent instead - */ -class PackageEvent extends BasePackageEvent -{ -} diff --git a/app/vendor/composer/composer/src/Composer/Script/ScriptEvents.php b/app/vendor/composer/composer/src/Composer/Script/ScriptEvents.php index 491c6bc65..d2dc66625 100644 --- a/app/vendor/composer/composer/src/Composer/Script/ScriptEvents.php +++ b/app/vendor/composer/composer/src/Composer/Script/ScriptEvents.php @@ -128,66 +128,4 @@ class ScriptEvents * @var string */ const POST_ARCHIVE_CMD = 'post-archive-cmd'; - - /** Deprecated constants below */ - - /** - * The PRE_PACKAGE_INSTALL event occurs before a package is installed. - * - * The event listener method receives a Composer\Installer\PackageEvent instance. - * - * @deprecated Use Composer\Installer\PackageEvents::PRE_PACKAGE_INSTALL instead. - * @var string - */ - const PRE_PACKAGE_INSTALL = 'pre-package-install'; - - /** - * The POST_PACKAGE_INSTALL event occurs after a package is installed. - * - * The event listener method receives a Composer\Installer\PackageEvent instance. - * - * @deprecated Use Composer\Installer\PackageEvents::POST_PACKAGE_INSTALL instead. - * @var string - */ - const POST_PACKAGE_INSTALL = 'post-package-install'; - - /** - * The PRE_PACKAGE_UPDATE event occurs before a package is updated. - * - * The event listener method receives a Composer\Installer\PackageEvent instance. - * - * @deprecated Use Composer\Installer\PackageEvents::PRE_PACKAGE_UPDATE instead. - * @var string - */ - const PRE_PACKAGE_UPDATE = 'pre-package-update'; - - /** - * The POST_PACKAGE_UPDATE event occurs after a package is updated. - * - * The event listener method receives a Composer\Installer\PackageEvent instance. - * - * @deprecated Use Composer\Installer\PackageEvents::POST_PACKAGE_UPDATE instead. - * @var string - */ - const POST_PACKAGE_UPDATE = 'post-package-update'; - - /** - * The PRE_PACKAGE_UNINSTALL event occurs before a package has been uninstalled. - * - * The event listener method receives a Composer\Installer\PackageEvent instance. - * - * @deprecated Use Composer\Installer\PackageEvents::PRE_PACKAGE_UNINSTALL instead. - * @var string - */ - const PRE_PACKAGE_UNINSTALL = 'pre-package-uninstall'; - - /** - * The POST_PACKAGE_UNINSTALL event occurs after a package has been uninstalled. - * - * The event listener method receives a Composer\Installer\PackageEvent instance. - * - * @deprecated Use Composer\Installer\PackageEvents::POST_PACKAGE_UNINSTALL instead. - * @var string - */ - const POST_PACKAGE_UNINSTALL = 'post-package-uninstall'; } diff --git a/app/vendor/composer/composer/src/Composer/SelfUpdate/Versions.php b/app/vendor/composer/composer/src/Composer/SelfUpdate/Versions.php index 7a0835ae8..591c11253 100644 --- a/app/vendor/composer/composer/src/Composer/SelfUpdate/Versions.php +++ b/app/vendor/composer/composer/src/Composer/SelfUpdate/Versions.php @@ -12,25 +12,29 @@ namespace Composer\SelfUpdate; -use Composer\Util\RemoteFilesystem; +use Composer\Util\HttpDownloader; use Composer\Config; -use Composer\Json\JsonFile; /** * @author Jordi Boggiano */ class Versions { + /** @var string[] */ public static $channels = array('stable', 'preview', 'snapshot', '1', '2'); - private $rfs; + /** @var HttpDownloader */ + private $httpDownloader; + /** @var Config */ private $config; + /** @var string */ private $channel; + /** @var array> */ private $versionsData; - public function __construct(Config $config, RemoteFilesystem $rfs) + public function __construct(Config $config, HttpDownloader $httpDownloader) { - $this->rfs = $rfs; + $this->httpDownloader = $httpDownloader; $this->config = $config; } @@ -84,7 +88,7 @@ private function getVersionsData() $protocol = 'https'; } - $this->versionsData = JsonFile::parseJson($this->rfs->getContents('getcomposer.org', $protocol . '://getcomposer.org/versions', false)); + $this->versionsData = $this->httpDownloader->get($protocol . '://getcomposer.org/versions')->decodeJson(); } return $this->versionsData; diff --git a/app/vendor/composer/composer/src/Composer/Util/AuthHelper.php b/app/vendor/composer/composer/src/Composer/Util/AuthHelper.php index 72b23ba22..b8dc44407 100644 --- a/app/vendor/composer/composer/src/Composer/Util/AuthHelper.php +++ b/app/vendor/composer/composer/src/Composer/Util/AuthHelper.php @@ -14,14 +14,19 @@ use Composer\Config; use Composer\IO\IOInterface; +use Composer\Downloader\TransportException; /** * @author Jordi Boggiano */ class AuthHelper { + /** @var IOInterface */ protected $io; + /** @var Config */ protected $config; + /** @var array Map of origins to message displayed */ + private $displayedOriginAuthentications = array(); public function __construct(IOInterface $io, Config $config) { @@ -29,7 +34,11 @@ public function __construct(IOInterface $io, Config $config) $this->config = $config; } - public function storeAuth($originUrl, $storeAuth) + /** + * @param string $origin + * @param string|bool $storeAuth + */ + public function storeAuth($origin, $storeAuth) { $store = false; $configSource = $this->config->getAuthConfigSource(); @@ -37,7 +46,7 @@ public function storeAuth($originUrl, $storeAuth) $store = $configSource; } elseif ($storeAuth === 'prompt') { $answer = $this->io->askAndValidate( - 'Do you want to store credentials for '.$originUrl.' in '.$configSource->getName().' ? [Yn] ', + 'Do you want to store credentials for '.$origin.' in '.$configSource->getName().' ? [Yn] ', function ($value) { $input = strtolower(substr(trim($value), 0, 1)); if (in_array($input, array('y','n'))) { @@ -55,9 +64,208 @@ function ($value) { } if ($store) { $store->addConfigSetting( - 'http-basic.'.$originUrl, - $this->io->getAuthentication($originUrl) + 'http-basic.'.$origin, + $this->io->getAuthentication($origin) ); } } + + /** + * @param string $url + * @param string $origin + * @param int $statusCode HTTP status code that triggered this call + * @param string|null $reason a message/description explaining why this was called + * @param string[] $headers + * @return array|null containing retry (bool) and storeAuth (string|bool) keys, if retry is true the request should be + * retried, if storeAuth is true then on a successful retry the authentication should be persisted to auth.json + * @phpstan-return ?array{retry: bool, storeAuth: string|bool} + */ + public function promptAuthIfNeeded($url, $origin, $statusCode, $reason = null, $headers = array()) + { + $storeAuth = false; + + if (in_array($origin, $this->config->get('github-domains'), true)) { + $gitHubUtil = new GitHub($this->io, $this->config, null); + $message = "\n"; + + $rateLimited = $gitHubUtil->isRateLimited($headers); + if ($rateLimited) { + $rateLimit = $gitHubUtil->getRateLimit($headers); + if ($this->io->hasAuthentication($origin)) { + $message = 'Review your configured GitHub OAuth token or enter a new one to go over the API rate limit.'; + } else { + $message = 'Create a GitHub OAuth token to go over the API rate limit.'; + } + + $message = sprintf( + 'GitHub API limit (%d calls/hr) is exhausted, could not fetch '.$url.'. '.$message.' You can also wait until %s for the rate limit to reset.', + $rateLimit['limit'], + $rateLimit['reset'] + )."\n"; + } else { + $message .= 'Could not fetch '.$url.', please '; + if ($this->io->hasAuthentication($origin)) { + $message .= 'review your configured GitHub OAuth token or enter a new one to access private repos'; + } else { + $message .= 'create a GitHub OAuth token to access private repos'; + } + } + + if (!$gitHubUtil->authorizeOAuth($origin) + && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($origin, $message)) + ) { + throw new TransportException('Could not authenticate against '.$origin, 401); + } + } elseif (in_array($origin, $this->config->get('gitlab-domains'), true)) { + $message = "\n".'Could not fetch '.$url.', enter your ' . $origin . ' credentials ' .($statusCode === 401 ? 'to access private repos' : 'to go over the API rate limit'); + $gitLabUtil = new GitLab($this->io, $this->config, null); + + if ($this->io->hasAuthentication($origin) && ($auth = $this->io->getAuthentication($origin)) && in_array($auth['password'], array('gitlab-ci-token', 'private-token', 'oauth2'), true)) { + throw new TransportException("Invalid credentials for '" . $url . "', aborting.", $statusCode); + } + + if (!$gitLabUtil->authorizeOAuth($origin) + && (!$this->io->isInteractive() || !$gitLabUtil->authorizeOAuthInteractively(parse_url($url, PHP_URL_SCHEME), $origin, $message)) + ) { + throw new TransportException('Could not authenticate against '.$origin, 401); + } + } elseif ($origin === 'bitbucket.org' || $origin === 'api.bitbucket.org') { + $askForOAuthToken = true; + $origin = 'bitbucket.org'; + if ($this->io->hasAuthentication($origin)) { + $auth = $this->io->getAuthentication($origin); + if ($auth['username'] !== 'x-token-auth') { + $bitbucketUtil = new Bitbucket($this->io, $this->config); + $accessToken = $bitbucketUtil->requestToken($origin, $auth['username'], $auth['password']); + if (!empty($accessToken)) { + $this->io->setAuthentication($origin, 'x-token-auth', $accessToken); + $askForOAuthToken = false; + } + } else { + throw new TransportException('Could not authenticate against ' . $origin, 401); + } + } + + if ($askForOAuthToken) { + $message = "\n".'Could not fetch ' . $url . ', please create a bitbucket OAuth token to ' . (($statusCode === 401 || $statusCode === 403) ? 'access private repos' : 'go over the API rate limit'); + $bitBucketUtil = new Bitbucket($this->io, $this->config); + if (!$bitBucketUtil->authorizeOAuth($origin) + && (!$this->io->isInteractive() || !$bitBucketUtil->authorizeOAuthInteractively($origin, $message)) + ) { + throw new TransportException('Could not authenticate against ' . $origin, 401); + } + } + } else { + // 404s are only handled for github + if ($statusCode === 404) { + return null; + } + + // fail if the console is not interactive + if (!$this->io->isInteractive()) { + if ($statusCode === 401) { + $message = "The '" . $url . "' URL required authentication.\nYou must be using the interactive console to authenticate"; + } elseif ($statusCode === 403) { + $message = "The '" . $url . "' URL could not be accessed: " . $reason; + } else { + $message = "Unknown error code '" . $statusCode . "', reason: " . $reason; + } + + throw new TransportException($message, $statusCode); + } + // fail if we already have auth + if ($this->io->hasAuthentication($origin)) { + throw new TransportException("Invalid credentials for '" . $url . "', aborting.", $statusCode); + } + + $this->io->writeError(' Authentication required ('.$origin.'):'); + $username = $this->io->ask(' Username: '); + $password = $this->io->askAndHideAnswer(' Password: '); + $this->io->setAuthentication($origin, $username, $password); + $storeAuth = $this->config->get('store-auths'); + } + + return array('retry' => true, 'storeAuth' => $storeAuth); + } + + /** + * @param array $headers + * @param string $origin + * @param string $url + * @return array updated headers array + */ + public function addAuthenticationHeader(array $headers, $origin, $url) + { + if ($this->io->hasAuthentication($origin)) { + $authenticationDisplayMessage = null; + $auth = $this->io->getAuthentication($origin); + if ($auth['password'] === 'bearer') { + $headers[] = 'Authorization: Bearer '.$auth['username']; + } elseif ('github.com' === $origin && 'x-oauth-basic' === $auth['password']) { + // only add the access_token if it is actually a github API URL + if (preg_match('{^https?://api\.github\.com/}', $url)) { + $headers[] = 'Authorization: token '.$auth['username']; + $authenticationDisplayMessage = 'Using GitHub token authentication'; + } + } elseif ( + in_array($origin, $this->config->get('gitlab-domains'), true) + && in_array($auth['password'], array('oauth2', 'private-token', 'gitlab-ci-token'), true) + ) { + if ($auth['password'] === 'oauth2') { + $headers[] = 'Authorization: Bearer '.$auth['username']; + $authenticationDisplayMessage = 'Using GitLab OAuth token authentication'; + } else { + $headers[] = 'PRIVATE-TOKEN: '.$auth['username']; + $authenticationDisplayMessage = 'Using GitLab private token authentication'; + } + } elseif ( + 'bitbucket.org' === $origin + && $url !== Bitbucket::OAUTH2_ACCESS_TOKEN_URL + && 'x-token-auth' === $auth['username'] + ) { + if (!$this->isPublicBitBucketDownload($url)) { + $headers[] = 'Authorization: Bearer ' . $auth['password']; + $authenticationDisplayMessage = 'Using Bitbucket OAuth token authentication'; + } + } else { + $authStr = base64_encode($auth['username'] . ':' . $auth['password']); + $headers[] = 'Authorization: Basic '.$authStr; + $authenticationDisplayMessage = 'Using HTTP basic authentication with username "' . $auth['username'] . '"'; + } + + if ($authenticationDisplayMessage && (!isset($this->displayedOriginAuthentications[$origin]) || $this->displayedOriginAuthentications[$origin] !== $authenticationDisplayMessage)) { + $this->io->writeError($authenticationDisplayMessage, true, IOInterface::DEBUG); + $this->displayedOriginAuthentications[$origin] = $authenticationDisplayMessage; + } + } elseif (in_array($origin, array('api.bitbucket.org', 'api.github.com'), true)) { + return $this->addAuthenticationHeader($headers, str_replace('api.', '', $origin), $url); + } + + return $headers; + } + + /** + * @link https://github.com/composer/composer/issues/5584 + * + * @param string $urlToBitBucketFile URL to a file at bitbucket.org. + * + * @return bool Whether the given URL is a public BitBucket download which requires no authentication. + */ + public function isPublicBitBucketDownload($urlToBitBucketFile) + { + $domain = parse_url($urlToBitBucketFile, PHP_URL_HOST); + if (strpos($domain, 'bitbucket.org') === false) { + // Bitbucket downloads are hosted on amazonaws. + // We do not need to authenticate there at all + return true; + } + + $path = parse_url($urlToBitBucketFile, PHP_URL_PATH); + + // Path for a public download follows this pattern /{user}/{repo}/downloads/{whatever} + // {@link https://blog.bitbucket.org/2009/04/12/new-feature-downloads/} + $pathParts = explode('/', $path); + + return count($pathParts) >= 4 && $pathParts[3] == 'downloads'; + } } diff --git a/app/vendor/composer/composer/src/Composer/Util/Bitbucket.php b/app/vendor/composer/composer/src/Composer/Util/Bitbucket.php index 0875bc34a..445dbed7e 100644 --- a/app/vendor/composer/composer/src/Composer/Util/Bitbucket.php +++ b/app/vendor/composer/composer/src/Composer/Util/Bitbucket.php @@ -22,11 +22,17 @@ */ class Bitbucket { + /** @var IOInterface */ private $io; + /** @var Config */ private $config; + /** @var ProcessExecutor */ private $process; - private $remoteFilesystem; - private $token = array(); + /** @var HttpDownloader */ + private $httpDownloader; + /** @var array{access_token: string, expires_in?: int}|null */ + private $token = null; + /** @var int|null */ private $time; const OAUTH2_ACCESS_TOKEN_URL = 'https://bitbucket.org/site/oauth2/access_token'; @@ -34,18 +40,18 @@ class Bitbucket /** * Constructor. * - * @param IOInterface $io The IO instance - * @param Config $config The composer configuration - * @param ProcessExecutor $process Process instance, injectable for mocking - * @param RemoteFilesystem $remoteFilesystem Remote Filesystem, injectable for mocking - * @param int $time Timestamp, injectable for mocking + * @param IOInterface $io The IO instance + * @param Config $config The composer configuration + * @param ProcessExecutor $process Process instance, injectable for mocking + * @param HttpDownloader $httpDownloader Remote Filesystem, injectable for mocking + * @param int $time Timestamp, injectable for mocking */ - public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, RemoteFilesystem $remoteFilesystem = null, $time = null) + public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, HttpDownloader $httpDownloader = null, $time = null) { $this->io = $io; $this->config = $config; $this->process = $process ?: new ProcessExecutor($io); - $this->remoteFilesystem = $remoteFilesystem ?: Factory::createRemoteFilesystem($this->io, $config); + $this->httpDownloader = $httpDownloader ?: Factory::createHttpDownloader($this->io, $config); $this->time = $time; } @@ -84,13 +90,12 @@ public function authorizeOAuth($originUrl) } /** - * @param string $originUrl * @return bool */ - private function requestAccessToken($originUrl) + private function requestAccessToken() { try { - $json = $this->remoteFilesystem->getContents($originUrl, self::OAUTH2_ACCESS_TOKEN_URL, false, array( + $response = $this->httpDownloader->get(self::OAUTH2_ACCESS_TOKEN_URL, array( 'retry-auth-failure' => false, 'http' => array( 'method' => 'POST', @@ -98,7 +103,12 @@ private function requestAccessToken($originUrl) ), )); - $this->token = json_decode($json, true); + $token = $response->decodeJson(); + if (!isset($token['expires_in']) || !isset($token['access_token'])) { + throw new \LogicException('Expected a token configured with expires_in and access_token present, got '.json_encode($token)); + } + + $this->token = $token; } catch (TransportException $e) { if ($e->getCode() === 400) { $this->io->writeError('Invalid OAuth consumer provided.'); @@ -107,7 +117,8 @@ private function requestAccessToken($originUrl) $this->io->writeError('2. You are using an OAuth consumer, but didn\'t configure a (dummy) callback url'); return false; - } elseif (in_array($e->getCode(), array(403, 401))) { + } + if (in_array($e->getCode(), array(403, 401))) { $this->io->writeError('Invalid OAuth consumer provided.'); $this->io->writeError('You can also add it manually later by using "composer config --global --auth bitbucket-oauth.bitbucket.org "'); @@ -140,7 +151,7 @@ public function authorizeOAuthInteractively($originUrl, $message = null) $this->io->writeError(sprintf('to create a consumer. It will be stored in "%s" for future use by Composer.', $this->config->getAuthConfigSource()->getName())); $this->io->writeError('Ensure you enter a "Callback URL" (http://example.com is fine) or it will not be possible to create an Access Token (this callback url will not be used by composer)'); - $consumerKey = trim($this->io->askAndHideAnswer('Consumer Key (hidden): ')); + $consumerKey = trim((string) $this->io->askAndHideAnswer('Consumer Key (hidden): ')); if (!$consumerKey) { $this->io->writeError('No consumer key given, aborting.'); @@ -149,7 +160,7 @@ public function authorizeOAuthInteractively($originUrl, $message = null) return false; } - $consumerSecret = trim($this->io->askAndHideAnswer('Consumer Secret (hidden): ')); + $consumerSecret = trim((string) $this->io->askAndHideAnswer('Consumer Secret (hidden): ')); if (!$consumerSecret) { $this->io->writeError('No consumer secret given, aborting.'); @@ -160,7 +171,7 @@ public function authorizeOAuthInteractively($originUrl, $message = null) $this->io->setAuthentication($originUrl, $consumerKey, $consumerSecret); - if (!$this->requestAccessToken($originUrl)) { + if (!$this->requestAccessToken()) { return false; } @@ -185,17 +196,23 @@ public function authorizeOAuthInteractively($originUrl, $message = null) */ public function requestToken($originUrl, $consumerKey, $consumerSecret) { - if (!empty($this->token) || $this->getTokenFromConfig($originUrl)) { + if ($this->token !== null || $this->getTokenFromConfig($originUrl)) { return $this->token['access_token']; } $this->io->setAuthentication($originUrl, $consumerKey, $consumerSecret); - if (!$this->requestAccessToken($originUrl)) { + if (!$this->requestAccessToken()) { return ''; } $this->storeInAuthConfig($originUrl, $consumerKey, $consumerSecret); + if (!isset($this->token['access_token'])) { + throw new \LogicException('Failed to initialize token above'); + } + + // side effect above caused this, https://github.com/phpstan/phpstan/issues/5129 + // @phpstan-ignore-next-line return $this->token['access_token']; } @@ -209,6 +226,10 @@ private function storeInAuthConfig($originUrl, $consumerKey, $consumerSecret) { $this->config->getConfigSource()->removeConfigSetting('bitbucket-oauth.'.$originUrl); + if (null === $this->token || !isset($this->token['expires_in'])) { + throw new \LogicException('Expected a token configured with expires_in present, got '.json_encode($this->token)); + } + $time = null === $this->time ? time() : $this->time; $consumer = array( "consumer-key" => $consumerKey, @@ -229,8 +250,7 @@ private function getTokenFromConfig($originUrl) $authConfig = $this->config->get('bitbucket-oauth'); if ( - !isset($authConfig[$originUrl]['access-token']) - || !isset($authConfig[$originUrl]['access-token-expiration']) + !isset($authConfig[$originUrl]['access-token'], $authConfig[$originUrl]['access-token-expiration']) || time() > $authConfig[$originUrl]['access-token-expiration'] ) { return false; diff --git a/app/vendor/composer/composer/src/Composer/Util/ComposerMirror.php b/app/vendor/composer/composer/src/Composer/Util/ComposerMirror.php index 036444d20..c73a2702c 100644 --- a/app/vendor/composer/composer/src/Composer/Util/ComposerMirror.php +++ b/app/vendor/composer/composer/src/Composer/Util/ComposerMirror.php @@ -19,18 +19,21 @@ */ class ComposerMirror { - public static function processUrl($mirrorUrl, $packageName, $version, $reference, $type) + public static function processUrl($mirrorUrl, $packageName, $version, $reference, $type, $prettyVersion = null) { if ($reference) { $reference = preg_match('{^([a-f0-9]*|%reference%)$}', $reference) ? $reference : md5($reference); } $version = strpos($version, '/') === false ? $version : md5($version); - return str_replace( - array('%package%', '%version%', '%reference%', '%type%'), - array($packageName, $version, $reference, $type), - $mirrorUrl - ); + $from = array('%package%', '%version%', '%reference%', '%type%'); + $to = array($packageName, $version, $reference, $type); + if (null !== $prettyVersion) { + $from[] = '%prettyVersion%'; + $to[] = $prettyVersion; + } + + return str_replace($from, $to, $mirrorUrl); } public static function processGitUrl($mirrorUrl, $packageName, $url, $type) diff --git a/app/vendor/composer/composer/src/Composer/Util/ConfigValidator.php b/app/vendor/composer/composer/src/Composer/Util/ConfigValidator.php index 8790edd09..a73b91651 100644 --- a/app/vendor/composer/composer/src/Composer/Util/ConfigValidator.php +++ b/app/vendor/composer/composer/src/Composer/Util/ConfigValidator.php @@ -28,6 +28,9 @@ */ class ConfigValidator { + const CHECK_VERSION = 1; + + /** @var IOInterface */ private $io; public function __construct(IOInterface $io) @@ -40,10 +43,11 @@ public function __construct(IOInterface $io) * * @param string $file The path to the file * @param int $arrayLoaderValidationFlags Flags for ArrayLoader validation + * @param int $flags Flags for validation * * @return array a triple containing the errors, publishable errors, and warnings */ - public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoader::CHECK_ALL) + public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoader::CHECK_ALL, $flags = self::CHECK_VERSION) { $errors = array(); $publishErrors = array(); @@ -109,7 +113,7 @@ public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoa } } - if (isset($manifest['version'])) { + if (($flags & self::CHECK_VERSION) && isset($manifest['version'])) { $warnings[] = 'The version field is present, it is recommended to leave it out if the package is published on Packagist.'; } @@ -129,7 +133,7 @@ public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoa } // check for require-dev overrides - if (isset($manifest['require']) && isset($manifest['require-dev'])) { + if (isset($manifest['require'], $manifest['require-dev'])) { $requireOverrides = array_intersect_key($manifest['require'], $manifest['require-dev']); if (!empty($requireOverrides)) { @@ -138,7 +142,6 @@ public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoa } } - // check for meaningless provide/replace satisfying requirements foreach (array('provide', 'replace') as $linkType) { if (isset($manifest[$linkType])) { @@ -187,8 +190,8 @@ public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoa $warnings[] = "Defining autoload.psr-4 with an empty namespace prefix is a bad idea for performance"; } + $loader = new ValidatingArrayLoader(new ArrayLoader(), true, null, $arrayLoaderValidationFlags); try { - $loader = new ValidatingArrayLoader(new ArrayLoader(), true, null, $arrayLoaderValidationFlags); if (!isset($manifest['version'])) { $manifest['version'] = '1.0.0'; } diff --git a/app/vendor/composer/composer/src/Composer/Util/ErrorHandler.php b/app/vendor/composer/composer/src/Composer/Util/ErrorHandler.php index c4dabd1d7..8e96191f2 100644 --- a/app/vendor/composer/composer/src/Composer/Util/ErrorHandler.php +++ b/app/vendor/composer/composer/src/Composer/Util/ErrorHandler.php @@ -21,6 +21,7 @@ */ class ErrorHandler { + /** @var ?IOInterface */ private static $io; /** @@ -39,7 +40,7 @@ public static function handle($level, $message, $file, $line) { // error code is not included in error_reporting if (!(error_reporting() & $level)) { - return; + return true; } if (filter_var(ini_get('xdebug.scream'), FILTER_VALIDATE_BOOLEAN)) { @@ -52,6 +53,15 @@ public static function handle($level, $message, $file, $line) } if (self::$io) { + // ignore symfony/* deprecation warnings + // TODO remove in 2.3 + if (preg_match('{^Return type of Symfony\\\\.*ReturnTypeWillChange}is', $message)) { + return true; + } + if (strpos(strtr($file, '\\', '/'), 'vendor/symfony/') !== false) { + return true; + } + self::$io->writeError('Deprecation Notice: '.$message.' in '.$file.':'.$line.''); if (self::$io->isVerbose()) { self::$io->writeError('Stack trace:'); diff --git a/app/vendor/composer/composer/src/Composer/Util/Filesystem.php b/app/vendor/composer/composer/src/Composer/Util/Filesystem.php index 008ed584a..5f99d8d30 100644 --- a/app/vendor/composer/composer/src/Composer/Util/Filesystem.php +++ b/app/vendor/composer/composer/src/Composer/Util/Filesystem.php @@ -12,6 +12,7 @@ namespace Composer\Util; +use React\Promise\PromiseInterface; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; use Symfony\Component\Filesystem\Exception\IOException; @@ -23,11 +24,12 @@ */ class Filesystem { + /** @var ?ProcessExecutor */ private $processExecutor; public function __construct(ProcessExecutor $executor = null) { - $this->processExecutor = $executor ?: new ProcessExecutor(); + $this->processExecutor = $executor; } public function remove($file) @@ -57,12 +59,12 @@ public function isDirEmpty($dir) ->depth(0) ->in($dir); - return count($finder) === 0; + return \count($finder) === 0; } public function emptyDirectory($dir, $ensureDirectoryExists = true) { - if (file_exists($dir) && is_link($dir)) { + if (is_link($dir) && file_exists($dir)) { $this->unlink($dir); } @@ -95,28 +97,44 @@ public function emptyDirectory($dir, $ensureDirectoryExists = true) */ public function removeDirectory($directory) { - if ($this->isSymlinkedDirectory($directory)) { - return $this->unlinkSymlinkedDirectory($directory); + $edgeCaseResult = $this->removeEdgeCases($directory); + if ($edgeCaseResult !== null) { + return $edgeCaseResult; } - if ($this->isJunction($directory)) { - return $this->removeJunction($directory); + if (Platform::isWindows()) { + $cmd = sprintf('rmdir /S /Q %s', ProcessExecutor::escape(realpath($directory))); + } else { + $cmd = sprintf('rm -rf %s', ProcessExecutor::escape($directory)); } - if (is_link($directory)) { - return unlink($directory); - } + $result = $this->getProcess()->execute($cmd, $output) === 0; - if (!file_exists($directory) || !is_dir($directory)) { + // clear stat cache because external processes aren't tracked by the php stat cache + clearstatcache(); + + if ($result && !is_dir($directory)) { return true; } - if (preg_match('{^(?:[a-z]:)?[/\\\\]+$}i', $directory)) { - throw new \RuntimeException('Aborting an attempted deletion of '.$directory.', this was probably not intended, if it is a real use case please report it.'); - } + return $this->removeDirectoryPhp($directory); + } - if (!function_exists('proc_open')) { - return $this->removeDirectoryPhp($directory); + /** + * Recursively remove a directory asynchronously + * + * Uses the process component if proc_open is enabled on the PHP + * installation. + * + * @param string $directory + * @throws \RuntimeException + * @return PromiseInterface + */ + public function removeDirectoryAsync($directory) + { + $edgeCaseResult = $this->removeEdgeCases($directory); + if ($edgeCaseResult !== null) { + return \React\Promise\resolve($edgeCaseResult); } if (Platform::isWindows()) { @@ -125,16 +143,56 @@ public function removeDirectory($directory) $cmd = sprintf('rm -rf %s', ProcessExecutor::escape($directory)); } - $result = $this->getProcess()->execute($cmd, $output) === 0; + $promise = $this->getProcess()->executeAsync($cmd); - // clear stat cache because external processes aren't tracked by the php stat cache - clearstatcache(); + $self = $this; + + return $promise->then(function ($process) use ($directory, $self) { + // clear stat cache because external processes aren't tracked by the php stat cache + clearstatcache(); + + if ($process->isSuccessful()) { + if (!is_dir($directory)) { + return \React\Promise\resolve(true); + } + } + + return \React\Promise\resolve($self->removeDirectoryPhp($directory)); + }); + } + + /** + * @param string $directory + * + * @return bool|null Returns null, when no edge case was hit. Otherwise a bool whether removal was successfull + */ + private function removeEdgeCases($directory, $fallbackToPhp = true) + { + if ($this->isSymlinkedDirectory($directory)) { + return $this->unlinkSymlinkedDirectory($directory); + } - if ($result && !file_exists($directory)) { + if ($this->isJunction($directory)) { + return $this->removeJunction($directory); + } + + if (is_link($directory)) { + return unlink($directory); + } + + if (!is_dir($directory) || !file_exists($directory)) { return true; } - return $this->removeDirectoryPhp($directory); + if (preg_match('{^(?:[a-z]:)?[/\\\\]+$}i', $directory)) { + throw new \RuntimeException('Aborting an attempted deletion of '.$directory.', this was probably not intended, if it is a real use case please report it.'); + } + + if (!\function_exists('proc_open') && $fallbackToPhp) { + return $this->removeDirectoryPhp($directory); + } + + return null; } /** @@ -149,6 +207,11 @@ public function removeDirectory($directory) */ public function removeDirectoryPhp($directory) { + $edgeCaseResult = $this->removeEdgeCases($directory, false); + if ($edgeCaseResult !== null) { + return $edgeCaseResult; + } + try { $it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS); } catch (\UnexpectedValueException $e) { @@ -171,6 +234,9 @@ public function removeDirectoryPhp($directory) } } + // release locks on the directory, see https://github.com/composer/composer/issues/9945 + unset($ri, $it, $file); + return $this->rmdir($directory); } @@ -276,8 +342,8 @@ public function copyThenRemove($source, $target) /** * Copies a file or directory from $source to $target. * - * @param string $source - * @param string $target + * @param string $source + * @param string $target * @return bool */ public function copy($source, $target) @@ -291,8 +357,9 @@ public function copy($source, $target) $this->ensureDirectoryExists($target); $result = true; + /** @var RecursiveDirectoryIterator $ri */ foreach ($ri as $file) { - $targetPath = $target . DIRECTORY_SEPARATOR . $ri->getSubPathName(); + $targetPath = $target . DIRECTORY_SEPARATOR . $ri->getSubPathname(); if ($file->isDir()) { $this->ensureDirectoryExists($targetPath); } else { @@ -309,16 +376,16 @@ public function rename($source, $target) return; } - if (!function_exists('proc_open')) { + if (!\function_exists('proc_open')) { $this->copyThenRemove($source, $target); - + return; } if (Platform::isWindows()) { // Try to copy & delete - this is a workaround for random "Access denied" errors. $command = sprintf('xcopy %s %s /E /I /Q /Y', ProcessExecutor::escape($source), ProcessExecutor::escape($target)); - $result = $this->processExecutor->execute($command, $output); + $result = $this->getProcess()->execute($command, $output); // clear stat cache because external processes aren't tracked by the php stat cache clearstatcache(); @@ -332,7 +399,7 @@ public function rename($source, $target) // We do not use PHP's "rename" function here since it does not support // the case where $source, and $target are located on different partitions. $command = sprintf('mv %s %s', ProcessExecutor::escape($source), ProcessExecutor::escape($target)); - $result = $this->processExecutor->execute($command, $output); + $result = $this->getProcess()->execute($command, $output); // clear stat cache because external processes aren't tracked by the php stat cache clearstatcache(); @@ -367,13 +434,13 @@ public function findShortestPath($from, $to, $directories = false) $from = rtrim($from, '/') . '/dummy_file'; } - if (dirname($from) === dirname($to)) { + if (\dirname($from) === \dirname($to)) { return './'.basename($to); } $commonPath = $to; while (strpos($from.'/', $commonPath.'/') !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath)) { - $commonPath = strtr(dirname($commonPath), '\\', '/'); + $commonPath = strtr(\dirname($commonPath), '\\', '/'); } if (0 !== strpos($from, $commonPath) || '/' === $commonPath) { @@ -381,10 +448,10 @@ public function findShortestPath($from, $to, $directories = false) } $commonPath = rtrim($commonPath, '/') . '/'; - $sourcePathDepth = substr_count(substr($from, strlen($commonPath)), '/'); + $sourcePathDepth = substr_count(substr($from, \strlen($commonPath)), '/'); $commonPathCode = str_repeat('../', $sourcePathDepth); - return ($commonPathCode . substr($to, strlen($commonPath))) ?: './'; + return ($commonPathCode . substr($to, \strlen($commonPath))) ?: './'; } /** @@ -412,7 +479,7 @@ public function findShortestPathCode($from, $to, $directories = false, $staticCo $commonPath = $to; while (strpos($from.'/', $commonPath.'/') !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath) && '.' !== $commonPath) { - $commonPath = strtr(dirname($commonPath), '\\', '/'); + $commonPath = strtr(\dirname($commonPath), '\\', '/'); } if (0 !== strpos($from, $commonPath) || '/' === $commonPath || '.' === $commonPath) { @@ -421,17 +488,17 @@ public function findShortestPathCode($from, $to, $directories = false, $staticCo $commonPath = rtrim($commonPath, '/') . '/'; if (strpos($to, $from.'/') === 0) { - return '__DIR__ . '.var_export(substr($to, strlen($from)), true); + return '__DIR__ . '.var_export(substr($to, \strlen($from)), true); } - $sourcePathDepth = substr_count(substr($from, strlen($commonPath)), '/') + $directories; + $sourcePathDepth = substr_count(substr($from, \strlen($commonPath)), '/') + $directories; if ($staticCode) { $commonPathCode = "__DIR__ . '".str_repeat('/..', $sourcePathDepth)."'"; } else { $commonPathCode = str_repeat('dirname(', $sourcePathDepth).'__DIR__'.str_repeat(')', $sourcePathDepth); } - $relTarget = substr($to, strlen($commonPath)); + $relTarget = substr($to, \strlen($commonPath)); - return $commonPathCode . (strlen($relTarget) ? '.' . var_export('/' . $relTarget, true) : ''); + return $commonPathCode . (\strlen($relTarget) ? '.' . var_export('/' . $relTarget, true) : ''); } /** @@ -442,12 +509,12 @@ public function findShortestPathCode($from, $to, $directories = false, $staticCo */ public function isAbsolutePath($path) { - return substr($path, 0, 1) === '/' || substr($path, 1, 1) === ':' || substr($path, 0, 2) === '\\\\'; + return strpos($path, '/') === 0 || substr($path, 1, 1) === ':' || strpos($path, '\\\\') === 0; } /** * Returns size of a file or directory specified by path. If a directory is - * given, it's size will be computed recursively. + * given, its size will be computed recursively. * * @param string $path Path to the file or directory * @throws \RuntimeException @@ -477,22 +544,28 @@ public function normalizePath($path) $parts = array(); $path = strtr($path, '\\', '/'); $prefix = ''; - $absolute = false; + $absolute = ''; + + // extract windows UNC paths e.g. \\foo\bar + if (strpos($path, '//') === 0 && \strlen($path) > 2) { + $absolute = '//'; + $path = substr($path, 2); + } // extract a prefix being a protocol://, protocol:, protocol://drive: or simply drive: if (preg_match('{^( [0-9a-z]{2,}+: (?: // (?: [a-z]: )? )? | [a-z]: )}ix', $path, $match)) { $prefix = $match[1]; - $path = substr($path, strlen($prefix)); + $path = substr($path, \strlen($prefix)); } - if (substr($path, 0, 1) === '/') { - $absolute = true; + if (strpos($path, '/') === 0) { + $absolute = '/'; $path = substr($path, 1); } $up = false; foreach (explode('/', $path) as $chunk) { - if ('..' === $chunk && ($absolute || $up)) { + if ('..' === $chunk && ($absolute !== '' || $up)) { array_pop($parts); $up = !(empty($parts) || '..' === end($parts)); } elseif ('.' !== $chunk && '' !== $chunk) { @@ -501,7 +574,24 @@ public function normalizePath($path) } } - return $prefix.($absolute ? '/' : '').implode('/', $parts); + return $prefix.((string) $absolute).implode('/', $parts); + } + + /** + * Remove trailing slashes if present to avoid issues with symlinks + * + * And other possible unforeseen disasters, see https://github.com/composer/composer/pull/9422 + * + * @param string $path + * @return string + */ + public static function trimTrailingSlash($path) + { + if (!preg_match('{^[/\\\\]+$}', $path)) { + $path = rtrim($path, '/\\'); + } + + return $path; } /** @@ -524,6 +614,33 @@ public static function getPlatformPath($path) return preg_replace('{^file://}i', '', $path); } + /** + * Cross-platform safe version of is_readable() + * + * This will also check for readability by reading the file as is_readable can not be trusted on network-mounts + * and \\wsl$ paths. See https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926 + * + * @param string $path + * @return bool + */ + public static function isReadable($path) + { + if (is_readable($path)) { + return true; + } + + if (is_file($path)) { + return false !== Silencer::call('file_get_contents', $path, false, null, 0, 1); + } + + if (is_dir($path)) { + return false !== Silencer::call('opendir', $path); + } + + // assume false otherwise + return false; + } + protected function directorySize($directory) { $it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS); @@ -539,8 +656,15 @@ protected function directorySize($directory) return $size; } + /** + * @return ProcessExecutor + */ protected function getProcess() { + if (!$this->processExecutor) { + $this->processExecutor = new ProcessExecutor(); + } + return $this->processExecutor; } @@ -571,10 +695,14 @@ private function unlinkImplementation($path) */ public function relativeSymlink($target, $link) { + if (!function_exists('symlink')) { + return false; + } + $cwd = getcwd(); $relativePath = $this->findShortestPath($link, $target); - chdir(dirname($link)); + chdir(\dirname($link)); $result = @symlink($relativePath, $link); chdir($cwd); @@ -627,7 +755,7 @@ private function resolveSymlinkedDirectorySymlink($pathname) $resolved = rtrim($pathname, '/'); - if (!strlen($resolved)) { + if (!\strlen($resolved)) { return $pathname; } @@ -716,4 +844,61 @@ public function removeJunction($junction) return $this->rmdir($junction); } + + public function filePutContentsIfModified($path, $content) + { + $currentContent = @file_get_contents($path); + if (!$currentContent || ($currentContent != $content)) { + return file_put_contents($path, $content); + } + + return 0; + } + + /** + * Copy file using stream_copy_to_stream to work around https://bugs.php.net/bug.php?id=6463 + * + * @param string $source + * @param string $target + */ + public function safeCopy($source, $target) + { + if (!file_exists($target) || !file_exists($source) || !$this->filesAreEqual($source, $target)) { + $source = fopen($source, 'r'); + $target = fopen($target, 'w+'); + + stream_copy_to_stream($source, $target); + fclose($source); + fclose($target); + } + } + + /** + * compare 2 files + * https://stackoverflow.com/questions/3060125/can-i-use-file-get-contents-to-compare-two-files + */ + private function filesAreEqual($a, $b) + { + // Check if filesize is different + if (filesize($a) !== filesize($b)) { + return false; + } + + // Check if content is different + $ah = fopen($a, 'rb'); + $bh = fopen($b, 'rb'); + + $result = true; + while (!feof($ah)) { + if (fread($ah, 8192) != fread($bh, 8192)) { + $result = false; + break; + } + } + + fclose($ah); + fclose($bh); + + return $result; + } } diff --git a/app/vendor/composer/composer/src/Composer/Util/Git.php b/app/vendor/composer/composer/src/Composer/Util/Git.php index 4f59839a9..8eab2c7c7 100644 --- a/app/vendor/composer/composer/src/Composer/Util/Git.php +++ b/app/vendor/composer/composer/src/Composer/Util/Git.php @@ -20,6 +20,7 @@ */ class Git { + /** @var string|false|null */ private static $version = false; /** @var IOInterface */ @@ -79,7 +80,8 @@ public function runCommand($commandCallable, $url, $cwd, $initialClone = false) return; } $messages[] = '- ' . $protoUrl . "\n" . preg_replace('#^#m', ' ', $this->process->getErrorOutput()); - if ($initialClone) { + + if ($initialClone && isset($origCwd)) { $this->filesystem->removeDirectory($origCwd); } } @@ -96,11 +98,12 @@ public function runCommand($commandCallable, $url, $cwd, $initialClone = false) $command = call_user_func($commandCallable, $url); $auth = null; + $credentials = array(); if ($bypassSshForGitHub || 0 !== $this->process->execute($command, $ignoredOutput, $cwd)) { $errorMsg = $this->process->getErrorOutput(); // private github repository without ssh key access, try https with auth if (preg_match('{^git@' . self::getGitHubDomainsRegex($this->config) . ':(.+?)\.git$}i', $url, $match) - || preg_match('{^https?://' . self::getGitHubDomainsRegex($this->config) . '/(.*)}', $url, $match) + || preg_match('{^https?://' . self::getGitHubDomainsRegex($this->config) . '/(.*?)(?:\.git)?$}i', $url, $match) ) { if (!$this->io->hasAuthentication($match[1])) { $gitHubUtil = new GitHub($this->io, $this->config, $this->process); @@ -119,9 +122,10 @@ public function runCommand($commandCallable, $url, $cwd, $initialClone = false) return; } + $credentials = array(rawurlencode($auth['username']), rawurlencode($auth['password'])); $errorMsg = $this->process->getErrorOutput(); } - } elseif (preg_match('{^https://(bitbucket\.org)/(.*)(\.git)?$}U', $url, $match)) { //bitbucket oauth + } elseif (preg_match('{^https://(bitbucket\.org)/(.*?)(?:\.git)?$}i', $url, $match)) { //bitbucket oauth $bitbucketUtil = new Bitbucket($this->io, $this->config, $this->process); if (!$this->io->hasAuthentication($match[1])) { @@ -138,7 +142,7 @@ public function runCommand($commandCallable, $url, $cwd, $initialClone = false) //We already have an access_token from a previous request. if ($auth['username'] !== 'x-token-auth') { $accessToken = $bitbucketUtil->requestToken($match[1], $auth['username'], $auth['password']); - if (! empty($accessToken)) { + if (!empty($accessToken)) { $this->io->setAuthentication($match[1], 'x-token-auth', $accessToken); } } @@ -153,6 +157,7 @@ public function runCommand($commandCallable, $url, $cwd, $initialClone = false) return; } + $credentials = array(rawurlencode($auth['username']), rawurlencode($auth['password'])); $errorMsg = $this->process->getErrorOutput(); } else { // Falling back to ssh $sshUrl = 'git@bitbucket.org:' . $match[2] . '.git'; @@ -166,7 +171,7 @@ public function runCommand($commandCallable, $url, $cwd, $initialClone = false) } } elseif ( preg_match('{^(git)@' . self::getGitLabDomainsRegex($this->config) . ':(.+?\.git)$}i', $url, $match) - || preg_match('{^(https?)://' . self::getGitLabDomainsRegex($this->config) . '/(.*)}', $url, $match) + || preg_match('{^(https?)://' . self::getGitLabDomainsRegex($this->config) . '/(.*)}i', $url, $match) ) { if ($match[1] === 'git') { $match[1] = 'https'; @@ -194,6 +199,7 @@ public function runCommand($commandCallable, $url, $cwd, $initialClone = false) return; } + $credentials = array(rawurlencode($auth['username']), rawurlencode($auth['password'])); $errorMsg = $this->process->getErrorOutput(); } } elseif ($this->isAuthenticationFailure($url, $match)) { // private non-github/gitlab/bitbucket repo that failed to authenticate @@ -234,27 +240,38 @@ public function runCommand($commandCallable, $url, $cwd, $initialClone = false) return; } + $credentials = array(rawurlencode($auth['username']), rawurlencode($auth['password'])); $errorMsg = $this->process->getErrorOutput(); } } - if ($initialClone) { + if ($initialClone && isset($origCwd)) { $this->filesystem->removeDirectory($origCwd); } + if (count($credentials) > 0) { + $command = $this->maskCredentials($command, $credentials); + $errorMsg = $this->maskCredentials($errorMsg, $credentials); + } $this->throwException('Failed to execute ' . $command . "\n\n" . $errorMsg, $url); } } public function syncMirror($url, $dir) { + if (getenv('COMPOSER_DISABLE_NETWORK') && getenv('COMPOSER_DISABLE_NETWORK') !== 'prime') { + $this->io->writeError('Aborting git mirror sync of '.$url.' as network is disabled'); + + return false; + } + // update the repo if it is a valid git repository if (is_dir($dir) && 0 === $this->process->execute('git rev-parse --git-dir', $output, $dir) && trim($output) === '.') { try { $commandCallable = function ($url) { $sanitizedUrl = preg_replace('{://([^@]+?):(.+?)@}', '://', $url); - return sprintf('git remote set-url origin -- %s && git remote update --prune origin && git remote set-url origin -- %s', ProcessExecutor::escape($url), ProcessExecutor::escape($sanitizedUrl)); + return sprintf('git remote set-url origin -- %s && git remote update --prune origin && git remote set-url origin -- %s && git gc --auto', ProcessExecutor::escape($url), ProcessExecutor::escape($sanitizedUrl)); }; $this->runCommand($commandCallable, $url, $dir); } catch (\Exception $e) { @@ -280,12 +297,12 @@ public function syncMirror($url, $dir) public function fetchRefOrSyncMirror($url, $dir, $ref) { - if ($this->checkRefIsInMirror($url, $dir, $ref)) { + if ($this->checkRefIsInMirror($dir, $ref)) { return true; } if ($this->syncMirror($url, $dir)) { - return $this->checkRefIsInMirror($url, $dir, $ref); + return $this->checkRefIsInMirror($dir, $ref); } return false; @@ -301,7 +318,7 @@ public static function getNoShowSignatureFlag(ProcessExecutor $process) return ''; } - private function checkRefIsInMirror($url, $dir, $ref) + private function checkRefIsInMirror($dir, $ref) { if (is_dir($dir) && 0 === $this->process->execute('git rev-parse --git-dir', $output, $dir) && trim($output) === '.') { $escapedRef = ProcessExecutor::escape($ref.'^{commit}'); @@ -346,29 +363,24 @@ public static function cleanEnv() // added in git 1.7.1, prevents prompting the user for username/password if (getenv('GIT_ASKPASS') !== 'echo') { - putenv('GIT_ASKPASS=echo'); - $_SERVER['GIT_ASKPASS'] = 'echo'; + Platform::putEnv('GIT_ASKPASS', 'echo'); } // clean up rogue git env vars in case this is running in a git hook if (getenv('GIT_DIR')) { - putenv('GIT_DIR'); - unset($_SERVER['GIT_DIR']); + Platform::clearEnv('GIT_DIR'); } if (getenv('GIT_WORK_TREE')) { - putenv('GIT_WORK_TREE'); - unset($_SERVER['GIT_WORK_TREE']); + Platform::clearEnv('GIT_WORK_TREE'); } // Run processes with predictable LANGUAGE if (getenv('LANGUAGE') !== 'C') { - putenv('LANGUAGE=C'); - $_SERVER['LANGUAGE'] = 'C'; + Platform::putEnv('LANGUAGE', 'C'); } // clean up env for OSX, see https://github.com/composer/composer/issues/2146#issuecomment-35478940 - putenv("DYLD_LIBRARY_PATH"); - unset($_SERVER['DYLD_LIBRARY_PATH']); + Platform::clearEnv('DYLD_LIBRARY_PATH'); } public static function getGitHubDomainsRegex(Config $config) @@ -381,41 +393,27 @@ public static function getGitLabDomainsRegex(Config $config) return '(' . implode('|', array_map('preg_quote', $config->get('gitlab-domains'))) . ')'; } - public static function sanitizeUrl($message) - { - return preg_replace_callback('{://(?P[^@]+?):(?P.+?)@}', function ($m) { - if (preg_match('{^[a-f0-9]{12,}$}', $m[1])) { - return '://***:***@'; - } - - return '://' . $m[1] . ':***@'; - }, $message); - } - private function throwException($message, $url) { // git might delete a directory when it fails and php will not know clearstatcache(); if (0 !== $this->process->execute('git --version', $ignoredOutput)) { - throw new \RuntimeException(self::sanitizeUrl('Failed to clone ' . $url . ', git was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput())); + throw new \RuntimeException(Url::sanitize('Failed to clone ' . $url . ', git was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput())); } - throw new \RuntimeException(self::sanitizeUrl($message)); + throw new \RuntimeException(Url::sanitize($message)); } /** * Retrieves the current git version. * - * @return string|null The git version number. + * @return string|null The git version number, if present. */ - public static function getVersion(ProcessExecutor $process = null) + public static function getVersion(ProcessExecutor $process) { if (false === self::$version) { self::$version = null; - if (!$process) { - $process = new ProcessExecutor; - } if (0 === $process->execute('git --version', $output) && preg_match('/^git version (\d+(?:\.\d+)+)/m', $output, $matches)) { self::$version = $matches[1]; } @@ -423,4 +421,23 @@ public static function getVersion(ProcessExecutor $process = null) return self::$version; } + + private function maskCredentials(string $error, array $credentials) + { + $maskedCredentials = array(); + + foreach ($credentials as $credential) { + if (in_array($credential, array('private-token', 'x-token-auth', 'oauth2', 'gitlab-ci-token', 'x-oauth-basic'))) { + $maskedCredentials[] = $credential; + } elseif (strlen($credential) > 6) { + $maskedCredentials[] = substr($credential, 0, 3) . '...' . substr($credential, -3); + } elseif (strlen($credential) > 3) { + $maskedCredentials[] = substr($credential, 0, 3) . '...'; + } else { + $maskedCredentials[] = 'XXX'; + } + } + + return str_replace($credentials, $maskedCredentials, $error); + } } diff --git a/app/vendor/composer/composer/src/Composer/Util/GitHub.php b/app/vendor/composer/composer/src/Composer/Util/GitHub.php index 1eca1a9bb..b081afeff 100644 --- a/app/vendor/composer/composer/src/Composer/Util/GitHub.php +++ b/app/vendor/composer/composer/src/Composer/Util/GitHub.php @@ -22,25 +22,29 @@ */ class GitHub { + /** @var IOInterface */ protected $io; + /** @var Config */ protected $config; + /** @var ProcessExecutor */ protected $process; - protected $remoteFilesystem; + /** @var HttpDownloader */ + protected $httpDownloader; /** * Constructor. * - * @param IOInterface $io The IO instance - * @param Config $config The composer configuration - * @param ProcessExecutor $process Process instance, injectable for mocking - * @param RemoteFilesystem $remoteFilesystem Remote Filesystem, injectable for mocking + * @param IOInterface $io The IO instance + * @param Config $config The composer configuration + * @param ProcessExecutor $process Process instance, injectable for mocking + * @param HttpDownloader $httpDownloader Remote Filesystem, injectable for mocking */ - public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, RemoteFilesystem $remoteFilesystem = null) + public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, HttpDownloader $httpDownloader = null) { $this->io = $io; $this->config = $config; $this->process = $process ?: new ProcessExecutor($io); - $this->remoteFilesystem = $remoteFilesystem ?: Factory::createRemoteFilesystem($this->io, $config); + $this->httpDownloader = $httpDownloader ?: Factory::createHttpDownloader($this->io, $config); } /** @@ -104,7 +108,7 @@ public function authorizeOAuthInteractively($originUrl, $message = null) try { $apiUrl = ('github.com' === $originUrl) ? 'api.github.com/' : $originUrl . '/api/v3/'; - $this->remoteFilesystem->getContents($originUrl, 'https://'. $apiUrl, false, array( + $this->httpDownloader->get('https://'. $apiUrl, array( 'retry-auth-failure' => false, )); } catch (TransportException $e) { diff --git a/app/vendor/composer/composer/src/Composer/Util/GitLab.php b/app/vendor/composer/composer/src/Composer/Util/GitLab.php index b3cb421ca..e5287991b 100644 --- a/app/vendor/composer/composer/src/Composer/Util/GitLab.php +++ b/app/vendor/composer/composer/src/Composer/Util/GitLab.php @@ -16,32 +16,35 @@ use Composer\Config; use Composer\Factory; use Composer\Downloader\TransportException; -use Composer\Json\JsonFile; /** * @author Roshan Gautam */ class GitLab { + /** @var IOInterface */ protected $io; + /** @var Config */ protected $config; + /** @var ProcessExecutor */ protected $process; - protected $remoteFilesystem; + /** @var HttpDownloader */ + protected $httpDownloader; /** * Constructor. * - * @param IOInterface $io The IO instance - * @param Config $config The composer configuration - * @param ProcessExecutor $process Process instance, injectable for mocking - * @param RemoteFilesystem $remoteFilesystem Remote Filesystem, injectable for mocking + * @param IOInterface $io The IO instance + * @param Config $config The composer configuration + * @param ProcessExecutor $process Process instance, injectable for mocking + * @param HttpDownloader $httpDownloader Remote Filesystem, injectable for mocking */ - public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, RemoteFilesystem $remoteFilesystem = null) + public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, HttpDownloader $httpDownloader = null) { $this->io = $io; $this->config = $config; $this->process = $process ?: new ProcessExecutor($io); - $this->remoteFilesystem = $remoteFilesystem ?: Factory::createRemoteFilesystem($this->io, $config); + $this->httpDownloader = $httpDownloader ?: Factory::createHttpDownloader($this->io, $config); } /** @@ -67,17 +70,28 @@ public function authorizeOAuth($originUrl) return true; } + // if available use deploy token from git config + if (0 === $this->process->execute('git config gitlab.deploytoken.user', $tokenUser) && 0 === $this->process->execute('git config gitlab.deploytoken.token', $tokenPassword)) { + $this->io->setAuthentication($originUrl, trim($tokenUser), trim($tokenPassword)); + + return true; + } + // if available use token from composer config $authTokens = $this->config->get('gitlab-token'); if (isset($authTokens[$originUrl])) { - $this->io->setAuthentication($originUrl, $authTokens[$originUrl], 'private-token'); - - return true; + $token = $authTokens[$originUrl]; } if (isset($authTokens[$bcOriginUrl])) { - $this->io->setAuthentication($originUrl, $authTokens[$bcOriginUrl], 'private-token'); + $token = $authTokens[$bcOriginUrl]; + } + + if (isset($token)) { + $username = is_array($token) && array_key_exists("username", $token) ? $token["username"] : $token; + $password = is_array($token) && array_key_exists("token", $token) ? $token["token"] : 'private-token'; + $this->io->setAuthentication($originUrl, $username, $password); return true; } @@ -104,7 +118,7 @@ public function authorizeOAuthInteractively($scheme, $originUrl, $message = null } $this->io->writeError(sprintf('A token will be created and stored in "%s", your password will never be stored', $this->config->getAuthConfigSource()->getName())); - $this->io->writeError('To revoke access to this token you can visit '.$scheme.'://'.$originUrl.'/profile/applications'); + $this->io->writeError('To revoke access to this token you can visit '.$scheme.'://'.$originUrl.'/-/profile/personal_access_tokens'); $attemptCounter = 0; @@ -158,7 +172,7 @@ private function createToken($scheme, $originUrl) 'username' => $username, 'password' => $password, 'grant_type' => 'password', - ), null, '&'); + ), '', '&'); $options = array( 'retry-auth-failure' => false, 'http' => array( @@ -168,10 +182,10 @@ private function createToken($scheme, $originUrl) ), ); - $json = $this->remoteFilesystem->getContents($originUrl, $scheme.'://'.$apiUrl.'/oauth/token', false, $options); + $token = $this->httpDownloader->get($scheme.'://'.$apiUrl.'/oauth/token', $options)->decodeJson(); $this->io->writeError('Token successfully created'); - return JsonFile::parseJson($json); + return $token; } } diff --git a/app/vendor/composer/composer/src/Composer/Util/Hg.php b/app/vendor/composer/composer/src/Composer/Util/Hg.php index 3681ad5c7..b6321c17a 100644 --- a/app/vendor/composer/composer/src/Composer/Util/Hg.php +++ b/app/vendor/composer/composer/src/Composer/Util/Hg.php @@ -20,6 +20,9 @@ */ class Hg { + /** @var string|false|null */ + private static $version = false; + /** * @var \Composer\IO\IOInterface */ @@ -72,23 +75,29 @@ public function runCommand($commandCallable, $url, $cwd) $this->throwException('Failed to clone ' . $url . ', ' . "\n\n" . $error, $url); } - public static function sanitizeUrl($message) + private function throwException($message, $url) { - return preg_replace_callback('{://(?P[^@]+?):(?P.+?)@}', function ($m) { - if (preg_match('{^[a-f0-9]{12,}$}', $m[1])) { - return '://***:***@'; - } + if (null === self::getVersion($this->process)) { + throw new \RuntimeException(Url::sanitize('Failed to clone ' . $url . ', hg was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput())); + } - return '://' . $m[1] . ':***@'; - }, $message); + throw new \RuntimeException(Url::sanitize($message)); } - private function throwException($message, $url) + /** + * Retrieves the current hg version. + * + * @return string|null The hg version number, if present. + */ + public static function getVersion(ProcessExecutor $process) { - if (0 !== $this->process->execute('hg --version', $ignoredOutput)) { - throw new \RuntimeException(self::sanitizeUrl('Failed to clone ' . $url . ', hg was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput())); + if (false === self::$version) { + self::$version = null; + if (0 === $process->execute('hg --version', $output) && preg_match('/^.+? (\d+(?:\.\d+)+)\)?\r?\n/', $output, $matches)) { + self::$version = $matches[1]; + } } - throw new \RuntimeException(self::sanitizeUrl($message)); + return self::$version; } } diff --git a/app/vendor/composer/composer/src/Composer/Util/Http/CurlDownloader.php b/app/vendor/composer/composer/src/Composer/Util/Http/CurlDownloader.php new file mode 100644 index 000000000..cb1f6daea --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Util/Http/CurlDownloader.php @@ -0,0 +1,608 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util\Http; + +use Composer\Config; +use Composer\Downloader\MaxFileSizeExceededException; +use Composer\IO\IOInterface; +use Composer\Downloader\TransportException; +use Composer\Util\StreamContextFactory; +use Composer\Util\AuthHelper; +use Composer\Util\Url; +use Composer\Util\HttpDownloader; +use React\Promise\Promise; + +/** + * @internal + * @author Jordi Boggiano + * @author Nicolas Grekas + * @phpstan-type Attributes array{retryAuthFailure: bool, redirects: int, storeAuth: bool} + * @phpstan-type Job array{url: string, origin: string, attributes: Attributes, options: mixed[], progress: mixed[], curlHandle: resource, filename: string|false, headerHandle: resource, bodyHandle: resource, resolve: callable, reject: callable} + */ +class CurlDownloader +{ + /** @var ?resource */ + private $multiHandle; + /** @var ?resource */ + private $shareHandle; + /** @var Job[] */ + private $jobs = array(); + /** @var IOInterface */ + private $io; + /** @var Config */ + private $config; + /** @var AuthHelper */ + private $authHelper; + /** @var float */ + private $selectTimeout = 5.0; + /** @var int */ + private $maxRedirects = 20; + /** @var ProxyManager */ + private $proxyManager; + /** @var bool */ + private $supportsSecureProxy; + /** @var array */ + protected $multiErrors = array( + CURLM_BAD_HANDLE => array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'), + CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."), + CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'), + CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!'), + ); + + /** @var mixed[] */ + private static $options = array( + 'http' => array( + 'method' => CURLOPT_CUSTOMREQUEST, + 'content' => CURLOPT_POSTFIELDS, + 'header' => CURLOPT_HTTPHEADER, + 'timeout' => CURLOPT_TIMEOUT, + ), + 'ssl' => array( + 'cafile' => CURLOPT_CAINFO, + 'capath' => CURLOPT_CAPATH, + 'verify_peer' => CURLOPT_SSL_VERIFYPEER, + 'verify_peer_name' => CURLOPT_SSL_VERIFYHOST, + 'local_cert' => CURLOPT_SSLCERT, + 'local_pk' => CURLOPT_SSLKEY, + 'passphrase' => CURLOPT_SSLKEYPASSWD, + ), + ); + + /** @var array */ + private static $timeInfo = array( + 'total_time' => true, + 'namelookup_time' => true, + 'connect_time' => true, + 'pretransfer_time' => true, + 'starttransfer_time' => true, + 'redirect_time' => true, + ); + + /** + * @param mixed[] $options + * @param bool $disableTls + */ + public function __construct(IOInterface $io, Config $config, array $options = array(), $disableTls = false) + { + $this->io = $io; + $this->config = $config; + + $this->multiHandle = $mh = curl_multi_init(); + if (function_exists('curl_multi_setopt')) { + curl_multi_setopt($mh, CURLMOPT_PIPELINING, PHP_VERSION_ID >= 70400 ? /* CURLPIPE_MULTIPLEX */ 2 : /*CURLPIPE_HTTP1 | CURLPIPE_MULTIPLEX*/ 3); + if (defined('CURLMOPT_MAX_HOST_CONNECTIONS') && !defined('HHVM_VERSION')) { + curl_multi_setopt($mh, CURLMOPT_MAX_HOST_CONNECTIONS, 8); + } + } + + if (function_exists('curl_share_init')) { + $this->shareHandle = $sh = curl_share_init(); + curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); + curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); + curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); + } + + $this->authHelper = new AuthHelper($io, $config); + $this->proxyManager = ProxyManager::getInstance(); + + $version = curl_version(); + $features = $version['features']; + $this->supportsSecureProxy = defined('CURL_VERSION_HTTPS_PROXY') && ($features & CURL_VERSION_HTTPS_PROXY); + } + + /** + * @param callable $resolve + * @param callable $reject + * @param string $origin + * @param string $url + * @param mixed[] $options + * @param ?string $copyTo + * + * @return int internal job id + */ + public function download($resolve, $reject, $origin, $url, $options, $copyTo = null) + { + $attributes = array(); + if (isset($options['retry-auth-failure'])) { + $attributes['retryAuthFailure'] = $options['retry-auth-failure']; + unset($options['retry-auth-failure']); + } + + return $this->initDownload($resolve, $reject, $origin, $url, $options, $copyTo, $attributes); + } + + /** + * @param callable $resolve + * @param callable $reject + * @param string $origin + * @param string $url + * @param mixed[] $options + * @param ?string $copyTo + * + * @param array{retryAuthFailure?: bool, redirects?: int, storeAuth?: bool} $attributes + * + * @return int internal job id + */ + private function initDownload($resolve, $reject, $origin, $url, $options, $copyTo = null, array $attributes = array()) + { + $attributes = array_merge(array( + 'retryAuthFailure' => true, + 'redirects' => 0, + 'storeAuth' => false, + ), $attributes); + + $originalOptions = $options; + + // check URL can be accessed (i.e. is not insecure), but allow insecure Packagist calls to $hashed providers as file integrity is verified with sha256 + if (!preg_match('{^http://(repo\.)?packagist\.org/p/}', $url) || (false === strpos($url, '$') && false === strpos($url, '%24'))) { + $this->config->prohibitUrlByConfig($url, $this->io); + } + + $curlHandle = curl_init(); + $headerHandle = fopen('php://temp/maxmemory:32768', 'w+b'); + + if ($copyTo) { + $errorMessage = ''; + // @phpstan-ignore-next-line + set_error_handler(function ($code, $msg) use (&$errorMessage) { + if ($errorMessage) { + $errorMessage .= "\n"; + } + $errorMessage .= preg_replace('{^fopen\(.*?\): }', '', $msg); + }); + $bodyHandle = fopen($copyTo.'~', 'w+b'); + restore_error_handler(); + if (!$bodyHandle) { + throw new TransportException('The "'.$url.'" file could not be written to '.$copyTo.': '.$errorMessage); + } + } else { + $bodyHandle = @fopen('php://temp/maxmemory:524288', 'w+b'); + } + + curl_setopt($curlHandle, CURLOPT_URL, $url); + curl_setopt($curlHandle, CURLOPT_FOLLOWLOCATION, false); + curl_setopt($curlHandle, CURLOPT_CONNECTTIMEOUT, 10); + curl_setopt($curlHandle, CURLOPT_TIMEOUT, max((int) ini_get("default_socket_timeout"), 300)); + curl_setopt($curlHandle, CURLOPT_WRITEHEADER, $headerHandle); + curl_setopt($curlHandle, CURLOPT_FILE, $bodyHandle); + curl_setopt($curlHandle, CURLOPT_ENCODING, "gzip"); + curl_setopt($curlHandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + if (function_exists('curl_share_init')) { + curl_setopt($curlHandle, CURLOPT_SHARE, $this->shareHandle); + } + + if (!isset($options['http']['header'])) { + $options['http']['header'] = array(); + } + + $options['http']['header'] = array_diff($options['http']['header'], array('Connection: close')); + $options['http']['header'][] = 'Connection: keep-alive'; + + $version = curl_version(); + $features = $version['features']; + if (0 === strpos($url, 'https://') && \defined('CURL_VERSION_HTTP2') && \defined('CURL_HTTP_VERSION_2_0') && (CURL_VERSION_HTTP2 & $features)) { + curl_setopt($curlHandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); + } + + $options['http']['header'] = $this->authHelper->addAuthenticationHeader($options['http']['header'], $origin, $url); + $options = StreamContextFactory::initOptions($url, $options, true); + + foreach (self::$options as $type => $curlOptions) { + foreach ($curlOptions as $name => $curlOption) { + if (isset($options[$type][$name])) { + if ($type === 'ssl' && $name === 'verify_peer_name') { + curl_setopt($curlHandle, $curlOption, $options[$type][$name] === true ? 2 : $options[$type][$name]); + } else { + curl_setopt($curlHandle, $curlOption, $options[$type][$name]); + } + } + } + } + + // Always set CURLOPT_PROXY to enable/disable proxy handling + // Any proxy authorization is included in the proxy url + $proxy = $this->proxyManager->getProxyForRequest($url); + curl_setopt($curlHandle, CURLOPT_PROXY, $proxy->getUrl()); + + // Curl needs certificate locations for secure proxies. + // CURLOPT_PROXY_SSL_VERIFY_PEER/HOST are enabled by default + if ($proxy->isSecure()) { + if (!$this->supportsSecureProxy) { + throw new TransportException('Connecting to a secure proxy using curl is not supported on PHP versions below 7.3.0.'); + } + if (!empty($options['ssl']['cafile'])) { + curl_setopt($curlHandle, CURLOPT_PROXY_CAINFO, $options['ssl']['cafile']); + } + if (!empty($options['ssl']['capath'])) { + curl_setopt($curlHandle, CURLOPT_PROXY_CAPATH, $options['ssl']['capath']); + } + } + + $progress = array_diff_key(curl_getinfo($curlHandle), self::$timeInfo); + + $this->jobs[(int) $curlHandle] = array( + 'url' => $url, + 'origin' => $origin, + 'attributes' => $attributes, + 'options' => $originalOptions, + 'progress' => $progress, + 'curlHandle' => $curlHandle, + 'filename' => $copyTo, + 'headerHandle' => $headerHandle, + 'bodyHandle' => $bodyHandle, + 'resolve' => $resolve, + 'reject' => $reject, + ); + + $usingProxy = $proxy->getFormattedUrl(' using proxy (%s)'); + $ifModified = false !== stripos(implode(',', $options['http']['header']), 'if-modified-since:') ? ' if modified' : ''; + if ($attributes['redirects'] === 0) { + $this->io->writeError('Downloading ' . Url::sanitize($url) . $usingProxy . $ifModified, true, IOInterface::DEBUG); + } + + $this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $curlHandle)); + // TODO progress + + return (int) $curlHandle; + } + + /** + * @param int $id + * @return void + */ + public function abortRequest($id) + { + if (isset($this->jobs[$id], $this->jobs[$id]['handle'])) { + $job = $this->jobs[$id]; + curl_multi_remove_handle($this->multiHandle, $job['handle']); + curl_close($job['handle']); + if (is_resource($job['headerHandle'])) { + fclose($job['headerHandle']); + } + if (is_resource($job['bodyHandle'])) { + fclose($job['bodyHandle']); + } + if ($job['filename']) { + @unlink($job['filename'].'~'); + } + unset($this->jobs[$id]); + } + } + + /** + * @return void + */ + public function tick() + { + if (!$this->jobs) { + return; + } + + $active = true; + $this->checkCurlResult(curl_multi_exec($this->multiHandle, $active)); + if (-1 === curl_multi_select($this->multiHandle, $this->selectTimeout)) { + // sleep in case select returns -1 as it can happen on old php versions or some platforms where curl does not manage to do the select + usleep(150); + } + + while ($progress = curl_multi_info_read($this->multiHandle)) { + $curlHandle = $progress['handle']; + $result = $progress['result']; + $i = (int) $curlHandle; + if (!isset($this->jobs[$i])) { + continue; + } + + $progress = curl_getinfo($curlHandle); + $job = $this->jobs[$i]; + unset($this->jobs[$i]); + $error = curl_error($curlHandle); + $errno = curl_errno($curlHandle); + curl_multi_remove_handle($this->multiHandle, $curlHandle); + curl_close($curlHandle); + + $headers = null; + $statusCode = null; + $response = null; + try { + // TODO progress + if (CURLE_OK !== $errno || $error || $result !== CURLE_OK) { + $errno = $errno ?: $result; + if (!$error && function_exists('curl_strerror')) { + $error = curl_strerror($errno); + } + $progress['error_code'] = $errno; + throw new TransportException('curl error '.$errno.' while downloading '.Url::sanitize($progress['url']).': '.$error); + } + $statusCode = $progress['http_code']; + rewind($job['headerHandle']); + $headers = explode("\r\n", rtrim(stream_get_contents($job['headerHandle']))); + fclose($job['headerHandle']); + + if ($statusCode === 0) { + throw new \LogicException('Received unexpected http status code 0 without error for '.Url::sanitize($progress['url']).': headers '.var_export($headers, true).' curl info '.var_export($progress, true)); + } + + // prepare response object + if ($job['filename']) { + $contents = $job['filename'].'~'; + if ($statusCode >= 300) { + rewind($job['bodyHandle']); + $contents = stream_get_contents($job['bodyHandle']); + } + $response = new CurlResponse(array('url' => $progress['url']), $statusCode, $headers, $contents, $progress); + $this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG); + } else { + rewind($job['bodyHandle']); + $contents = stream_get_contents($job['bodyHandle']); + $response = new CurlResponse(array('url' => $progress['url']), $statusCode, $headers, $contents, $progress); + $this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG); + } + fclose($job['bodyHandle']); + + if ($response->getStatusCode() >= 400 && $response->getHeader('content-type') === 'application/json') { + HttpDownloader::outputWarnings($this->io, $job['origin'], json_decode($response->getBody(), true)); + } + + $result = $this->isAuthenticatedRetryNeeded($job, $response); + if ($result['retry']) { + $this->restartJob($job, $job['url'], array('storeAuth' => $result['storeAuth'])); + continue; + } + + // handle 3xx redirects, 304 Not Modified is excluded + if ($statusCode >= 300 && $statusCode <= 399 && $statusCode !== 304 && $job['attributes']['redirects'] < $this->maxRedirects) { + $location = $this->handleRedirect($job, $response); + if ($location) { + $this->restartJob($job, $location, array('redirects' => $job['attributes']['redirects'] + 1)); + continue; + } + } + + // fail 4xx and 5xx responses and capture the response + if ($statusCode >= 400 && $statusCode <= 599) { + throw $this->failResponse($job, $response, $response->getStatusMessage()); + } + + if ($job['attributes']['storeAuth']) { + $this->authHelper->storeAuth($job['origin'], $job['attributes']['storeAuth']); + } + + // resolve promise + if ($job['filename']) { + rename($job['filename'].'~', $job['filename']); + call_user_func($job['resolve'], $response); + } else { + call_user_func($job['resolve'], $response); + } + } catch (\Exception $e) { + if ($e instanceof TransportException && $headers) { + $e->setHeaders($headers); + $e->setStatusCode($statusCode); + } + if ($e instanceof TransportException && $response) { + $e->setResponse($response->getBody()); + } + if ($e instanceof TransportException && $progress) { + $e->setResponseInfo($progress); + } + + $this->rejectJob($job, $e); + } + } + + foreach ($this->jobs as $i => $curlHandle) { + if (!isset($this->jobs[$i])) { + continue; + } + $curlHandle = $this->jobs[$i]['curlHandle']; + $progress = array_diff_key(curl_getinfo($curlHandle), self::$timeInfo); + + if ($this->jobs[$i]['progress'] !== $progress) { + $this->jobs[$i]['progress'] = $progress; + + if (isset($this->jobs[$i]['options']['max_file_size'])) { + // Compare max_file_size with the content-length header this value will be -1 until the header is parsed + if ($this->jobs[$i]['options']['max_file_size'] < $progress['download_content_length']) { + $this->rejectJob($this->jobs[$i], new MaxFileSizeExceededException('Maximum allowed download size reached. Content-length header indicates ' . $progress['download_content_length'] . ' bytes. Allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes')); + } + + // Compare max_file_size with the download size in bytes + if ($this->jobs[$i]['options']['max_file_size'] < $progress['size_download']) { + $this->rejectJob($this->jobs[$i], new MaxFileSizeExceededException('Maximum allowed download size reached. Downloaded ' . $progress['size_download'] . ' of allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes')); + } + } + + // TODO progress + } + } + } + + /** + * @param Job $job + * @return string + */ + private function handleRedirect(array $job, Response $response) + { + if ($locationHeader = $response->getHeader('location')) { + if (parse_url($locationHeader, PHP_URL_SCHEME)) { + // Absolute URL; e.g. https://example.com/composer + $targetUrl = $locationHeader; + } elseif (parse_url($locationHeader, PHP_URL_HOST)) { + // Scheme relative; e.g. //example.com/foo + $targetUrl = parse_url($job['url'], PHP_URL_SCHEME).':'.$locationHeader; + } elseif ('/' === $locationHeader[0]) { + // Absolute path; e.g. /foo + $urlHost = parse_url($job['url'], PHP_URL_HOST); + + // Replace path using hostname as an anchor. + $targetUrl = preg_replace('{^(.+(?://|@)'.preg_quote($urlHost).'(?::\d+)?)(?:[/\?].*)?$}', '\1'.$locationHeader, $job['url']); + } else { + // Relative path; e.g. foo + // This actually differs from PHP which seems to add duplicate slashes. + $targetUrl = preg_replace('{^(.+/)[^/?]*(?:\?.*)?$}', '\1'.$locationHeader, $job['url']); + } + } + + if (!empty($targetUrl)) { + $this->io->writeError(sprintf('Following redirect (%u) %s', $job['attributes']['redirects'] + 1, Url::sanitize($targetUrl)), true, IOInterface::DEBUG); + + return $targetUrl; + } + + throw new TransportException('The "'.$job['url'].'" file could not be downloaded, got redirect without Location ('.$response->getStatusMessage().')'); + } + + /** + * @param Job $job + * @return array{retry: bool, storeAuth: string|bool} + */ + private function isAuthenticatedRetryNeeded(array $job, Response $response) + { + if (in_array($response->getStatusCode(), array(401, 403)) && $job['attributes']['retryAuthFailure']) { + $result = $this->authHelper->promptAuthIfNeeded($job['url'], $job['origin'], $response->getStatusCode(), $response->getStatusMessage(), $response->getHeaders()); + + if ($result['retry']) { + return $result; + } + } + + $locationHeader = $response->getHeader('location'); + $needsAuthRetry = false; + + // check for bitbucket login page asking to authenticate + if ( + $job['origin'] === 'bitbucket.org' + && !$this->authHelper->isPublicBitBucketDownload($job['url']) + && substr($job['url'], -4) === '.zip' + && (!$locationHeader || substr($locationHeader, -4) !== '.zip') + && preg_match('{^text/html\b}i', $response->getHeader('content-type')) + ) { + $needsAuthRetry = 'Bitbucket requires authentication and it was not provided'; + } + + // check for gitlab 404 when downloading archives + if ( + $response->getStatusCode() === 404 + && in_array($job['origin'], $this->config->get('gitlab-domains'), true) + && false !== strpos($job['url'], 'archive.zip') + ) { + $needsAuthRetry = 'GitLab requires authentication and it was not provided'; + } + + if ($needsAuthRetry) { + if ($job['attributes']['retryAuthFailure']) { + $result = $this->authHelper->promptAuthIfNeeded($job['url'], $job['origin'], 401); + if ($result['retry']) { + return $result; + } + } + + throw $this->failResponse($job, $response, $needsAuthRetry); + } + + return array('retry' => false, 'storeAuth' => false); + } + + /** + * @param Job $job + * @param string $url + * + * @param array{retryAuthFailure?: bool, redirects?: int, storeAuth?: bool} $attributes + * + * @return void + */ + private function restartJob(array $job, $url, array $attributes = array()) + { + if ($job['filename']) { + @unlink($job['filename'].'~'); + } + + $attributes = array_merge($job['attributes'], $attributes); + $origin = Url::getOrigin($this->config, $url); + + $this->initDownload($job['resolve'], $job['reject'], $origin, $url, $job['options'], $job['filename'], $attributes); + } + + /** + * @param Job $job + * @param string $errorMessage + * @return TransportException + */ + private function failResponse(array $job, Response $response, $errorMessage) + { + if ($job['filename']) { + @unlink($job['filename'].'~'); + } + + $details = ''; + if ($response->getHeader('content-type') === 'application/json') { + $details = ':'.PHP_EOL.substr($response->getBody(), 0, 200).(strlen($response->getBody()) > 200 ? '...' : ''); + } + + return new TransportException('The "'.$job['url'].'" file could not be downloaded ('.$errorMessage.')' . $details, $response->getStatusCode()); + } + + /** + * @param Job $job + * @return void + */ + private function rejectJob(array $job, \Exception $e) + { + if (is_resource($job['headerHandle'])) { + fclose($job['headerHandle']); + } + if (is_resource($job['bodyHandle'])) { + fclose($job['bodyHandle']); + } + if ($job['filename']) { + @unlink($job['filename'].'~'); + } + call_user_func($job['reject'], $e); + } + + /** + * @param int $code + * @return void + */ + private function checkCurlResult($code) + { + if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) { + throw new \RuntimeException( + isset($this->multiErrors[$code]) + ? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}" + : 'Unexpected cURL error: ' . $code + ); + } + } +} diff --git a/app/vendor/composer/composer/src/Composer/Util/Http/CurlResponse.php b/app/vendor/composer/composer/src/Composer/Util/Http/CurlResponse.php new file mode 100644 index 000000000..495a73b85 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Util/Http/CurlResponse.php @@ -0,0 +1,42 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util\Http; + +/** + * @phpstan-type CurlInfo array{url: mixed, content_type: mixed, http_code: mixed, header_size: mixed, request_size: mixed, filetime: mixed, ssl_verify_result: mixed, redirect_count: mixed, total_time: mixed, namelookup_time: mixed, connect_time: mixed, pretransfer_time: mixed, size_upload: mixed, size_download: mixed, speed_download: mixed, speed_upload: mixed, download_content_length: mixed, upload_content_length: mixed, starttransfer_time: mixed, redirect_time: mixed, certinfo: mixed, primary_ip: mixed, primary_port: mixed, local_ip: mixed, local_port: mixed, redirect_url: mixed} + */ +class CurlResponse extends Response +{ + /** + * @see https://www.php.net/curl_getinfo + * @var CurlInfo + */ + private $curlInfo; + + /** + * @param CurlInfo $curlInfo + */ + public function __construct(array $request, $code, array $headers, $body, array $curlInfo) + { + parent::__construct($request, $code, $headers, $body); + $this->curlInfo = $curlInfo; + } + + /** + * @return CurlInfo + */ + public function getCurlInfo() + { + return $this->curlInfo; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Util/Http/ProxyHelper.php b/app/vendor/composer/composer/src/Composer/Util/Http/ProxyHelper.php new file mode 100644 index 000000000..611980676 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Util/Http/ProxyHelper.php @@ -0,0 +1,183 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util\Http; + +/** + * Proxy discovery and helper class + * + * @internal + * @author John Stevenson + */ +class ProxyHelper +{ + /** + * Returns proxy environment values + * + * @throws \RuntimeException on malformed url + * @return array httpProxy, httpsProxy, noProxy values + */ + public static function getProxyData() + { + $httpProxy = null; + $httpsProxy = null; + + // Handle http_proxy/HTTP_PROXY on CLI only for security reasons + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + if ($env = self::getProxyEnv(array('http_proxy', 'HTTP_PROXY'), $name)) { + $httpProxy = self::checkProxy($env, $name); + } + } + + // Prefer CGI_HTTP_PROXY if available + if ($env = self::getProxyEnv(array('CGI_HTTP_PROXY'), $name)) { + $httpProxy = self::checkProxy($env, $name); + } + + // Handle https_proxy/HTTPS_PROXY + if ($env = self::getProxyEnv(array('https_proxy', 'HTTPS_PROXY'), $name)) { + $httpsProxy = self::checkProxy($env, $name); + } else { + $httpsProxy = $httpProxy; + } + + // Handle no_proxy + $noProxy = self::getProxyEnv(array('no_proxy', 'NO_PROXY'), $name); + + return array($httpProxy, $httpsProxy, $noProxy); + } + + /** + * Returns http context options for the proxy url + * + * @param string $proxyUrl + * @return array + */ + public static function getContextOptions($proxyUrl) + { + $proxy = parse_url($proxyUrl); + + // Remove any authorization + $proxyUrl = self::formatParsedUrl($proxy, false); + $proxyUrl = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxyUrl); + + $options['http']['proxy'] = $proxyUrl; + + // Handle any authorization + if (isset($proxy['user'])) { + $auth = rawurldecode($proxy['user']); + + if (isset($proxy['pass'])) { + $auth .= ':' . rawurldecode($proxy['pass']); + } + $auth = base64_encode($auth); + // Set header as a string + $options['http']['header'] = "Proxy-Authorization: Basic {$auth}"; + } + + return $options; + } + + /** + * Sets/unsets request_fulluri value in http context options array + * + * @param string $requestUrl + * @param array $options Set by method + */ + public static function setRequestFullUri($requestUrl, array &$options) + { + if ('http' === parse_url($requestUrl, PHP_URL_SCHEME)) { + $options['http']['request_fulluri'] = true; + } else { + unset($options['http']['request_fulluri']); + } + } + + /** + * Searches $_SERVER for case-sensitive values + * + * @param array $names Names to search for + * @param mixed $name Name of any found value + * @return string|null The found value + */ + private static function getProxyEnv(array $names, &$name) + { + foreach ($names as $name) { + if (!empty($_SERVER[$name])) { + return $_SERVER[$name]; + } + } + + return null; + } + + /** + * Checks and formats a proxy url from the environment + * + * @param string $proxyUrl + * @param string $envName + * @throws \RuntimeException on malformed url + * @return string The formatted proxy url + */ + private static function checkProxy($proxyUrl, $envName) + { + $error = sprintf('malformed %s url', $envName); + $proxy = parse_url($proxyUrl); + + // We need parse_url to have identified a host + if (!isset($proxy['host'])) { + throw new \RuntimeException($error); + } + + $proxyUrl = self::formatParsedUrl($proxy, true); + + // We need a port because streams and curl use different defaults + if (!parse_url($proxyUrl, PHP_URL_PORT)) { + throw new \RuntimeException($error); + } + + return $proxyUrl; + } + + /** + * Formats a url from its component parts + * + * @param array $proxy Values from parse_url + * @param bool $includeAuth Whether to include authorization values + * @return string The formatted value + */ + private static function formatParsedUrl(array $proxy, $includeAuth) + { + $proxyUrl = isset($proxy['scheme']) ? strtolower($proxy['scheme']) . '://' : ''; + + if ($includeAuth && isset($proxy['user'])) { + $proxyUrl .= $proxy['user']; + + if (isset($proxy['pass'])) { + $proxyUrl .= ':' . $proxy['pass']; + } + $proxyUrl .= '@'; + } + + $proxyUrl .= $proxy['host']; + + if (isset($proxy['port'])) { + $proxyUrl .= ':' . $proxy['port']; + } elseif (strpos($proxyUrl, 'http://') === 0) { + $proxyUrl .= ':80'; + } elseif (strpos($proxyUrl, 'https://') === 0) { + $proxyUrl .= ':443'; + } + + return $proxyUrl; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Util/Http/ProxyManager.php b/app/vendor/composer/composer/src/Composer/Util/Http/ProxyManager.php new file mode 100644 index 000000000..586b08e83 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Util/Http/ProxyManager.php @@ -0,0 +1,188 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util\Http; + +use Composer\Downloader\TransportException; +use Composer\Util\NoProxyPattern; +use Composer\Util\Url; + +/** + * @internal + * @author John Stevenson + */ +class ProxyManager +{ + private $error; + private $fullProxy; + private $safeProxy; + private $streams; + private $hasProxy; + private $info; + private $lastProxy; + /** @var ?NoProxyPattern */ + private $noProxyHandler = null; + + /** @var ?ProxyManager */ + private static $instance = null; + + private function __construct() + { + $this->fullProxy = $this->safeProxy = array( + 'http' => null, + 'https' => null, + ); + + $this->streams['http'] = $this->streams['https'] = array( + 'options' => null, + ); + + $this->hasProxy = false; + $this->initProxyData(); + } + + /** + * @return ProxyManager + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Clears the persistent instance + */ + public static function reset() + { + self::$instance = null; + } + + /** + * Returns a RequestProxy instance for the request url + * + * @param string $requestUrl + * @return RequestProxy + */ + public function getProxyForRequest($requestUrl) + { + if ($this->error) { + throw new TransportException('Unable to use a proxy: '.$this->error); + } + + $scheme = parse_url($requestUrl, PHP_URL_SCHEME) ?: 'http'; + $proxyUrl = ''; + $options = array(); + $formattedProxyUrl = ''; + + if ($this->hasProxy && in_array($scheme, array('http', 'https'), true) && $this->fullProxy[$scheme]) { + if ($this->noProxy($requestUrl)) { + $formattedProxyUrl = 'excluded by no_proxy'; + } else { + $proxyUrl = $this->fullProxy[$scheme]; + $options = $this->streams[$scheme]['options']; + ProxyHelper::setRequestFullUri($requestUrl, $options); + $formattedProxyUrl = $this->safeProxy[$scheme]; + } + } + + return new RequestProxy($proxyUrl, $options, $formattedProxyUrl); + } + + /** + * Returns true if a proxy is being used + * + * @return bool If false any error will be in $message + */ + public function isProxying() + { + return $this->hasProxy; + } + + /** + * Returns proxy configuration info which can be shown to the user + * + * @return string|null Safe proxy URL or an error message if setting up proxy failed or null if no proxy was configured + */ + public function getFormattedProxy() + { + return $this->hasProxy ? $this->info : $this->error; + } + + /** + * Initializes proxy values from the environment + */ + private function initProxyData() + { + try { + list($httpProxy, $httpsProxy, $noProxy) = ProxyHelper::getProxyData(); + } catch (\RuntimeException $e) { + $this->error = $e->getMessage(); + + return; + } + + $info = array(); + + if ($httpProxy) { + $info[] = $this->setData($httpProxy, 'http'); + } + if ($httpsProxy) { + $info[] = $this->setData($httpsProxy, 'https'); + } + if ($this->hasProxy) { + $this->info = implode(', ', $info); + if ($noProxy) { + $this->noProxyHandler = new NoProxyPattern($noProxy); + } + } + } + + /** + * Sets initial data + * + * @param string $url Proxy url + * @param string $scheme Environment variable scheme + */ + private function setData($url, $scheme) + { + $safeProxy = Url::sanitize($url); + $this->fullProxy[$scheme] = $url; + $this->safeProxy[$scheme] = $safeProxy; + $this->streams[$scheme]['options'] = ProxyHelper::getContextOptions($url); + $this->hasProxy = true; + + return sprintf('%s=%s', $scheme, $safeProxy); + } + + /** + * Returns true if a url matches no_proxy value + * + * @param string $requestUrl + * @return bool + */ + private function noProxy($requestUrl) + { + if ($this->noProxyHandler) { + if ($this->noProxyHandler->test($requestUrl)) { + $this->lastProxy = 'excluded by no_proxy'; + + return true; + } + } + + return false; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Util/Http/RequestProxy.php b/app/vendor/composer/composer/src/Composer/Util/Http/RequestProxy.php new file mode 100644 index 000000000..5a832706f --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Util/Http/RequestProxy.php @@ -0,0 +1,91 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util\Http; + +use Composer\Util\Url; + +/** + * @internal + * @author John Stevenson + */ +class RequestProxy +{ + /** @var mixed[] */ + private $contextOptions; + /** @var bool */ + private $isSecure; + /** @var string */ + private $formattedUrl; + /** @var string */ + private $url; + + /** + * @param string $url + * @param mixed[] $contextOptions + * @param string $formattedUrl + */ + public function __construct($url, array $contextOptions, $formattedUrl) + { + $this->url = $url; + $this->contextOptions = $contextOptions; + $this->formattedUrl = $formattedUrl; + $this->isSecure = 0 === strpos($url, 'https://'); + } + + /** + * Returns an array of context options + * + * @return mixed[] + */ + public function getContextOptions() + { + return $this->contextOptions; + } + + /** + * Returns the safe proxy url from the last request + * + * @param string|null $format Output format specifier + * @return string Safe proxy, no proxy or empty + */ + public function getFormattedUrl($format = '') + { + $result = ''; + if ($this->formattedUrl) { + $format = $format ?: '%s'; + $result = sprintf($format, $this->formattedUrl); + } + + return $result; + } + + /** + * Returns the proxy url + * + * @return string Proxy url or empty + */ + public function getUrl() + { + return $this->url; + } + + /** + * Returns true if this is a secure-proxy + * + * @return bool False if not secure or there is no proxy + */ + public function isSecure() + { + return $this->isSecure; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Util/Http/Response.php b/app/vendor/composer/composer/src/Composer/Util/Http/Response.php new file mode 100644 index 000000000..5f3cf73e5 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Util/Http/Response.php @@ -0,0 +1,133 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util\Http; + +use Composer\Json\JsonFile; +use Composer\Util\HttpDownloader; + +/** + * @phpstan-import-type Request from HttpDownloader + */ +class Response +{ + /** @var Request */ + private $request; + /** @var int */ + private $code; + /** @var string[] */ + private $headers; + /** @var ?string */ + private $body; + + /** + * @param Request $request + * @param int $code + * @param string[] $headers + * @param ?string $body + */ + public function __construct(array $request, $code, array $headers, $body) + { + if (!isset($request['url'])) { + throw new \LogicException('url key missing from request array'); + } + $this->request = $request; + $this->code = (int) $code; + $this->headers = $headers; + $this->body = $body; + } + + /** + * @return int + */ + public function getStatusCode() + { + return $this->code; + } + + /** + * @return string|null + */ + public function getStatusMessage() + { + $value = null; + foreach ($this->headers as $header) { + if (preg_match('{^HTTP/\S+ \d+}i', $header)) { + // In case of redirects, headers contain the headers of all responses + // so we can not return directly and need to keep iterating + $value = $header; + } + } + + return $value; + } + + /** + * @return string[] + */ + public function getHeaders() + { + return $this->headers; + } + + /** + * @param string $name + * @return ?string + */ + public function getHeader($name) + { + return self::findHeaderValue($this->headers, $name); + } + + /** + * @return ?string + */ + public function getBody() + { + return $this->body; + } + + /** + * @return mixed + */ + public function decodeJson() + { + return JsonFile::parseJson($this->body, $this->request['url']); + } + + /** + * @return void + * @phpstan-impure + */ + public function collect() + { + /** @phpstan-ignore-next-line */ + $this->request = $this->code = $this->headers = $this->body = null; + } + + /** + * @param string[] $headers array of returned headers like from getLastHeaders() + * @param string $name header name (case insensitive) + * @return string|null + */ + public static function findHeaderValue(array $headers, $name) + { + $value = null; + foreach ($headers as $header) { + if (preg_match('{^'.preg_quote($name).':\s*(.+?)\s*$}i', $header, $match)) { + $value = $match[1]; + } + } + + return $value; + } +} diff --git a/app/vendor/composer/composer/src/Composer/Util/HttpDownloader.php b/app/vendor/composer/composer/src/Composer/Util/HttpDownloader.php new file mode 100644 index 000000000..1706dab9f --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Util/HttpDownloader.php @@ -0,0 +1,538 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util; + +use Composer\Config; +use Composer\IO\IOInterface; +use Composer\Downloader\TransportException; +use Composer\Util\Http\Response; +use Composer\Util\Http\CurlDownloader; +use Composer\Composer; +use Composer\Package\Version\VersionParser; +use Composer\Semver\Constraint\Constraint; +use Composer\Exception\IrrecoverableDownloadException; +use React\Promise\Promise; +use React\Promise\PromiseInterface; + +/** + * @author Jordi Boggiano + * @phpstan-type Request array{url: string, options?: mixed[], copyTo?: ?string} + * @phpstan-type Job array{id: int, status: int, request: Request, sync: bool, origin: string, resolve?: callable, reject?: callable, curl_id?: int, response?: Response, exception?: TransportException} + */ +class HttpDownloader +{ + const STATUS_QUEUED = 1; + const STATUS_STARTED = 2; + const STATUS_COMPLETED = 3; + const STATUS_FAILED = 4; + const STATUS_ABORTED = 5; + + /** @var IOInterface */ + private $io; + /** @var Config */ + private $config; + /** @var array */ + private $jobs = array(); + /** @var mixed[] */ + private $options = array(); + /** @var int */ + private $runningJobs = 0; + /** @var int */ + private $maxJobs = 12; + /** @var ?CurlDownloader */ + private $curl; + /** @var ?RemoteFilesystem */ + private $rfs; + /** @var int */ + private $idGen = 0; + /** @var bool */ + private $disabled; + /** @var bool */ + private $allowAsync = false; + + /** + * @param IOInterface $io The IO instance + * @param Config $config The config + * @param mixed[] $options The options + * @param bool $disableTls + */ + public function __construct(IOInterface $io, Config $config, array $options = array(), $disableTls = false) + { + $this->io = $io; + + $this->disabled = (bool) getenv('COMPOSER_DISABLE_NETWORK'); + + // Setup TLS options + // The cafile option can be set via config.json + if ($disableTls === false) { + $this->options = StreamContextFactory::getTlsDefaults($options, $io); + } + + // handle the other externally set options normally. + $this->options = array_replace_recursive($this->options, $options); + $this->config = $config; + + if (self::isCurlEnabled()) { + $this->curl = new CurlDownloader($io, $config, $options, $disableTls); + } + + $this->rfs = new RemoteFilesystem($io, $config, $options, $disableTls); + + if (is_numeric($maxJobs = getenv('COMPOSER_MAX_PARALLEL_HTTP'))) { + $this->maxJobs = max(1, min(50, (int) $maxJobs)); + } + } + + /** + * Download a file synchronously + * + * @param string $url URL to download + * @param mixed[] $options Stream context options e.g. https://www.php.net/manual/en/context.http.php + * although not all options are supported when using the default curl downloader + * @throws TransportException + * @return Response + */ + public function get($url, $options = array()) + { + list($job) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => null), true); + $this->wait($job['id']); + + $response = $this->getResponse($job['id']); + + // check for failed curl response (empty body but successful looking response) + if ( + $this->curl + && PHP_VERSION_ID < 70000 + && $response->getBody() === null + && $response->getStatusCode() === 200 + && $response->getHeader('content-length') !== '0' + ) { + $this->io->writeError('cURL downloader failed to return a response, disabling it and proceeding in slow mode.'); + + $this->curl = null; + + list($job) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => null), true); + $this->wait($job['id']); + + $response = $this->getResponse($job['id']); + } + + return $response; + } + + /** + * Create an async download operation + * + * @param string $url URL to download + * @param mixed[] $options Stream context options e.g. https://www.php.net/manual/en/context.http.php + * although not all options are supported when using the default curl downloader + * @throws TransportException + * @return PromiseInterface + */ + public function add($url, $options = array()) + { + list(, $promise) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => null)); + + return $promise; + } + + /** + * Copy a file synchronously + * + * @param string $url URL to download + * @param string $to Path to copy to + * @param mixed[] $options Stream context options e.g. https://www.php.net/manual/en/context.http.php + * although not all options are supported when using the default curl downloader + * @throws TransportException + * @return Response + */ + public function copy($url, $to, $options = array()) + { + list($job) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => $to), true); + $this->wait($job['id']); + + return $this->getResponse($job['id']); + } + + /** + * Create an async copy operation + * + * @param string $url URL to download + * @param string $to Path to copy to + * @param mixed[] $options Stream context options e.g. https://www.php.net/manual/en/context.http.php + * although not all options are supported when using the default curl downloader + * @throws TransportException + * @return PromiseInterface + */ + public function addCopy($url, $to, $options = array()) + { + list(, $promise) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => $to)); + + return $promise; + } + + /** + * Retrieve the options set in the constructor + * + * @return mixed[] Options + */ + public function getOptions() + { + return $this->options; + } + + /** + * Merges new options + * + * @param mixed[] $options + * @return void + */ + public function setOptions(array $options) + { + $this->options = array_replace_recursive($this->options, $options); + } + + /** + * @param Request $request + * @param bool $sync + * + * @return array{Job, PromiseInterface} + */ + private function addJob($request, $sync = false) + { + $request['options'] = array_replace_recursive($this->options, $request['options']); + + /** @var Job */ + $job = array( + 'id' => $this->idGen++, + 'status' => self::STATUS_QUEUED, + 'request' => $request, + 'sync' => $sync, + 'origin' => Url::getOrigin($this->config, $request['url']), + ); + + if (!$sync && !$this->allowAsync) { + throw new \LogicException('You must use the HttpDownloader instance which is part of a Composer\Loop instance to be able to run async http requests'); + } + + // capture username/password from URL if there is one + if (preg_match('{^https?://([^:/]+):([^@/]+)@([^/]+)}i', $request['url'], $match)) { + $this->io->setAuthentication($job['origin'], rawurldecode($match[1]), rawurldecode($match[2])); + } + + $rfs = $this->rfs; + + if ($this->canUseCurl($job)) { + $resolver = function ($resolve, $reject) use (&$job) { + $job['status'] = HttpDownloader::STATUS_QUEUED; + $job['resolve'] = $resolve; + $job['reject'] = $reject; + }; + } else { + $resolver = function ($resolve, $reject) use (&$job, $rfs) { + // start job + $url = $job['request']['url']; + $options = $job['request']['options']; + + $job['status'] = HttpDownloader::STATUS_STARTED; + + if ($job['request']['copyTo']) { + $rfs->copy($job['origin'], $url, $job['request']['copyTo'], false /* TODO progress */, $options); + + $headers = $rfs->getLastHeaders(); + $response = new Http\Response($job['request'], $rfs->findStatusCode($headers), $headers, $job['request']['copyTo'].'~'); + + $resolve($response); + } else { + $body = $rfs->getContents($job['origin'], $url, false /* TODO progress */, $options); + $headers = $rfs->getLastHeaders(); + $response = new Http\Response($job['request'], $rfs->findStatusCode($headers), $headers, $body); + + $resolve($response); + } + }; + } + + $downloader = $this; + $curl = $this->curl; + + $canceler = function () use (&$job, $curl) { + if ($job['status'] === HttpDownloader::STATUS_QUEUED) { + $job['status'] = HttpDownloader::STATUS_ABORTED; + } + if ($job['status'] !== HttpDownloader::STATUS_STARTED) { + return; + } + $job['status'] = HttpDownloader::STATUS_ABORTED; + if (isset($job['curl_id'])) { + $curl->abortRequest($job['curl_id']); + } + throw new IrrecoverableDownloadException('Download of ' . Url::sanitize($job['request']['url']) . ' canceled'); + }; + + $promise = new Promise($resolver, $canceler); + $promise = $promise->then(function ($response) use (&$job, $downloader) { + $job['status'] = HttpDownloader::STATUS_COMPLETED; + $job['response'] = $response; + + // TODO 3.0 this should be done directly on $this when PHP 5.3 is dropped + $downloader->markJobDone(); + + return $response; + }, function ($e) use (&$job, $downloader) { + $job['status'] = HttpDownloader::STATUS_FAILED; + $job['exception'] = $e; + + $downloader->markJobDone(); + + throw $e; + }); + $this->jobs[$job['id']] = &$job; + + if ($this->runningJobs < $this->maxJobs) { + $this->startJob($job['id']); + } + + return array($job, $promise); + } + + /** + * @param int $id + * @return void + */ + private function startJob($id) + { + $job = &$this->jobs[$id]; + if ($job['status'] !== self::STATUS_QUEUED) { + return; + } + + // start job + $job['status'] = self::STATUS_STARTED; + $this->runningJobs++; + + $resolve = $job['resolve']; + $reject = $job['reject']; + $url = $job['request']['url']; + $options = $job['request']['options']; + $origin = $job['origin']; + + if ($this->disabled) { + if (isset($job['request']['options']['http']['header']) && false !== stripos(implode('', $job['request']['options']['http']['header']), 'if-modified-since')) { + $resolve(new Response(array('url' => $url), 304, array(), '')); + } else { + $e = new TransportException('Network disabled, request canceled: '.Url::sanitize($url), 499); + $e->setStatusCode(499); + $reject($e); + } + + return; + } + + try { + if ($job['request']['copyTo']) { + $job['curl_id'] = $this->curl->download($resolve, $reject, $origin, $url, $options, $job['request']['copyTo']); + } else { + $job['curl_id'] = $this->curl->download($resolve, $reject, $origin, $url, $options); + } + } catch (\Exception $exception) { + $reject($exception); + } + } + + /** + * @private + * @return void + */ + public function markJobDone() + { + $this->runningJobs--; + } + + /** + * Wait for current async download jobs to complete + * + * @param int|null $index For internal use only, the job id + * + * @return void + */ + public function wait($index = null) + { + do { + $jobCount = $this->countActiveJobs($index); + } while ($jobCount); + } + + /** + * @internal + * + * @return void + */ + public function enableAsync() + { + $this->allowAsync = true; + } + + /** + * @internal + * + * @param int|null $index For internal use only, the job id + * @return int number of active (queued or started) jobs + */ + public function countActiveJobs($index = null) + { + if ($this->runningJobs < $this->maxJobs) { + foreach ($this->jobs as $job) { + if ($job['status'] === self::STATUS_QUEUED && $this->runningJobs < $this->maxJobs) { + $this->startJob($job['id']); + } + } + } + + if ($this->curl) { + $this->curl->tick(); + } + + if (null !== $index) { + return $this->jobs[$index]['status'] < self::STATUS_COMPLETED ? 1 : 0; + } + + $active = 0; + foreach ($this->jobs as $job) { + if ($job['status'] < self::STATUS_COMPLETED) { + $active++; + } elseif (!$job['sync']) { + unset($this->jobs[$job['id']]); + } + } + + return $active; + } + + /** + * @param int $index Job id + * @return Response + */ + private function getResponse($index) + { + if (!isset($this->jobs[$index])) { + throw new \LogicException('Invalid request id'); + } + + if ($this->jobs[$index]['status'] === self::STATUS_FAILED) { + throw $this->jobs[$index]['exception']; + } + + if (!isset($this->jobs[$index]['response'])) { + throw new \LogicException('Response not available yet, call wait() first'); + } + + $resp = $this->jobs[$index]['response']; + + unset($this->jobs[$index]); + + return $resp; + } + + /** + * @internal + * + * @param string $url + * @param array{warning?: string, info?: string, warning-versions?: string, info-versions?: string} $data + * @return void + */ + public static function outputWarnings(IOInterface $io, $url, $data) + { + foreach (array('warning', 'info') as $type) { + if (empty($data[$type])) { + continue; + } + + if (!empty($data[$type . '-versions'])) { + $versionParser = new VersionParser(); + $constraint = $versionParser->parseConstraints($data[$type . '-versions']); + $composer = new Constraint('==', $versionParser->normalize(Composer::getVersion())); + if (!$constraint->matches($composer)) { + continue; + } + } + + $io->writeError('<'.$type.'>'.ucfirst($type).' from '.Url::sanitize($url).': '.$data[$type].''); + } + } + + /** + * @internal + * + * @return ?string[] + */ + public static function getExceptionHints(\Exception $e) + { + if (!$e instanceof TransportException) { + return null; + } + + if ( + false !== strpos($e->getMessage(), 'Resolving timed out') + || false !== strpos($e->getMessage(), 'Could not resolve host') + ) { + Silencer::suppress(); + $testConnectivity = file_get_contents('https://8.8.8.8', false, stream_context_create(array( + 'ssl' => array('verify_peer' => false), + 'http' => array('follow_location' => false, 'ignore_errors' => true), + ))); + Silencer::restore(); + if (false !== $testConnectivity) { + return array( + 'The following exception probably indicates you have misconfigured DNS resolver(s)', + ); + } + + return array( + 'The following exception probably indicates you are offline or have misconfigured DNS resolver(s)', + ); + } + + return null; + } + + /** + * @param Job $job + * @return bool + */ + private function canUseCurl(array $job) + { + if (!$this->curl) { + return false; + } + + if (!preg_match('{^https?://}i', $job['request']['url'])) { + return false; + } + + if (!empty($job['request']['options']['ssl']['allow_self_signed'])) { + return false; + } + + return true; + } + + /** + * @internal + * @return bool + */ + public static function isCurlEnabled() + { + return \extension_loaded('curl') && \function_exists('curl_multi_exec') && \function_exists('curl_multi_init'); + } +} diff --git a/app/vendor/composer/composer/src/Composer/Util/IniHelper.php b/app/vendor/composer/composer/src/Composer/Util/IniHelper.php index d655419fc..8076f82ed 100644 --- a/app/vendor/composer/composer/src/Composer/Util/IniHelper.php +++ b/app/vendor/composer/composer/src/Composer/Util/IniHelper.php @@ -29,7 +29,7 @@ class IniHelper * The equivalent of calling php_ini_loaded_file then php_ini_scanned_files. * The loaded ini location is the first entry and may be empty. * - * @return array + * @return string[] */ public static function getAll() { diff --git a/app/vendor/composer/composer/src/Composer/Util/Loop.php b/app/vendor/composer/composer/src/Composer/Util/Loop.php new file mode 100644 index 000000000..b103011a4 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Util/Loop.php @@ -0,0 +1,135 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util; + +use React\Promise\CancellablePromiseInterface; +use Symfony\Component\Console\Helper\ProgressBar; +use React\Promise\PromiseInterface; + +/** + * @author Jordi Boggiano + */ +class Loop +{ + /** @var HttpDownloader */ + private $httpDownloader; + /** @var ProcessExecutor|null */ + private $processExecutor; + /** @var PromiseInterface[][] */ + private $currentPromises = array(); + /** @var int */ + private $waitIndex = 0; + + public function __construct(HttpDownloader $httpDownloader, ProcessExecutor $processExecutor = null) + { + $this->httpDownloader = $httpDownloader; + $this->httpDownloader->enableAsync(); + + $this->processExecutor = $processExecutor; + if ($this->processExecutor) { + $this->processExecutor->enableAsync(); + } + } + + /** + * @return HttpDownloader + */ + public function getHttpDownloader() + { + return $this->httpDownloader; + } + + /** + * @return ProcessExecutor|null + */ + public function getProcessExecutor() + { + return $this->processExecutor; + } + + /** + * @param PromiseInterface[] $promises + * @param ?ProgressBar $progress + * @return void + */ + public function wait(array $promises, ProgressBar $progress = null) + { + /** @var \Exception|null */ + $uncaught = null; + + \React\Promise\all($promises)->then( + function () { + }, + function ($e) use (&$uncaught) { + $uncaught = $e; + } + ); + + // keep track of every group of promises that is waited on, so abortJobs can + // cancel them all, even if wait() was called within a wait() + $waitIndex = $this->waitIndex++; + $this->currentPromises[$waitIndex] = $promises; + + if ($progress) { + $totalJobs = 0; + $totalJobs += $this->httpDownloader->countActiveJobs(); + if ($this->processExecutor) { + $totalJobs += $this->processExecutor->countActiveJobs(); + } + $progress->start($totalJobs); + } + + $lastUpdate = 0; + while (true) { + $activeJobs = 0; + + $activeJobs += $this->httpDownloader->countActiveJobs(); + if ($this->processExecutor) { + $activeJobs += $this->processExecutor->countActiveJobs(); + } + + if ($progress && microtime(true) - $lastUpdate > 0.1) { + $lastUpdate = microtime(true); + $progress->setProgress($progress->getMaxSteps() - $activeJobs); + } + + if (!$activeJobs) { + break; + } + } + + // as we skip progress updates if they are too quick, make sure we do one last one here at 100% + if ($progress) { + $progress->finish(); + } + + unset($this->currentPromises[$waitIndex]); + if ($uncaught) { + throw $uncaught; + } + } + + /** + * @return void + */ + public function abortJobs() + { + foreach ($this->currentPromises as $promiseGroup) { + foreach ($promiseGroup as $promise) { + if ($promise instanceof CancellablePromiseInterface) { + $promise->cancel(); + } + } + } + } +} diff --git a/app/vendor/composer/composer/src/Composer/Util/MetadataMinifier.php b/app/vendor/composer/composer/src/Composer/Util/MetadataMinifier.php new file mode 100644 index 000000000..6fae871c3 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Util/MetadataMinifier.php @@ -0,0 +1,22 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util; + +@trigger_error('Composer\Util\MetadataMinifier is deprecated, use Composer\MetadataMinifier\MetadataMinifier from composer/metadata-minifier instead.', E_USER_DEPRECATED); + +/** + * @deprecated Use Composer\MetadataMinifier\MetadataMinifier instead + */ +class MetadataMinifier extends \Composer\MetadataMinifier\MetadataMinifier +{ +} diff --git a/app/vendor/composer/composer/src/Composer/Util/NoProxyPattern.php b/app/vendor/composer/composer/src/Composer/Util/NoProxyPattern.php index adf60f0f7..21f7c4fca 100644 --- a/app/vendor/composer/composer/src/Composer/Util/NoProxyPattern.php +++ b/app/vendor/composer/composer/src/Composer/Util/NoProxyPattern.php @@ -25,7 +25,7 @@ class NoProxyPattern protected $hostNames = array(); /** - * @var object[] + * @var (null|object)[] */ protected $rules = array(); @@ -39,7 +39,7 @@ class NoProxyPattern */ public function __construct($pattern) { - $this->hostNames = preg_split('{[\s,]+}', $pattern, null, PREG_SPLIT_NO_EMPTY); + $this->hostNames = preg_split('{[\s,]+}', $pattern, -1, PREG_SPLIT_NO_EMPTY); $this->noproxy = empty($this->hostNames) || '*' === $this->hostNames[0]; } @@ -74,7 +74,7 @@ public function test($url) * * @param string $url * - * @return bool|stdclass + * @return bool|stdClass */ protected function getUrlData($url) { @@ -108,9 +108,9 @@ protected function getUrlData($url) /** * Returns true if the url is matched by a rule * - * @param int $index - * @param string $hostName - * @param string $url + * @param int $index + * @param string $hostName + * @param stdClass $url * * @return bool */ @@ -134,7 +134,7 @@ protected function match($index, $hostName, $url) $match = $rule->ipdata->ip === $url->ipdata->ip; } else { // Match host and port - $haystack = substr($url->name, - strlen($rule->name)); + $haystack = substr($url->name, -strlen($rule->name)); $match = stripos($haystack, $rule->name) === 0; } @@ -171,10 +171,10 @@ protected function matchRange(stdClass $network, stdClass $target) /** * Finds or creates rule data for a hostname * - * @param int $index + * @param int $index * @param string $hostName * - * @return {null|stdClass} Null if the hostname is invalid + * @return null|stdClass Null if the hostname is invalid */ private function getRule($index, $hostName) { @@ -197,9 +197,9 @@ private function getRule($index, $hostName) /** * Creates an object containing IP data if the host is an IP address * - * @param string $host - * @param null|stdclass $ipdata Set by method if IP address found - * @param bool $allowPrefix Whether a CIDR prefix-length is expected + * @param string $host + * @param null|stdClass $ipdata Set by method if IP address found + * @param bool $allowPrefix Whether a CIDR prefix-length is expected * * @return bool False if the host contains invalid data */ @@ -264,8 +264,8 @@ private function ipGetAddr($host) /** * Returns the binary network mask mapped to IPv6 * - * @param string $prefix CIDR prefix-length - * @param int $size Byte size of in_addr + * @param int $prefix CIDR prefix-length + * @param int $size Byte size of in_addr * * @return string */ @@ -274,7 +274,7 @@ private function ipGetMask($prefix, $size) $mask = ''; if ($ones = floor($prefix / 8)) { - $mask = str_repeat(chr(255), $ones); + $mask = str_repeat(chr(255), (int) $ones); } if ($remainder = $prefix % 8) { @@ -290,8 +290,8 @@ private function ipGetMask($prefix, $size) * Calculates and returns the network and mask * * @param string $rangeIp IP in_addr - * @param int $size Byte size of in_addr - * @param string $prefix CIDR prefix-length + * @param int $size Byte size of in_addr + * @param int $prefix CIDR prefix-length * * @return string[] network in_addr, binary mask */ @@ -315,7 +315,7 @@ private function ipGetNetwork($rangeIp, $size, $prefix) * Maps an IPv4 address to IPv6 * * @param string $binary in_addr - * @param int $size Byte size of in_addr + * @param int $size Byte size of in_addr * * @return string Mapped or existing in_addr */ @@ -332,11 +332,11 @@ private function ipMapTo6($binary, $size) /** * Creates a rule data object * - * @param string $host - * @param int $port - * @param null|stdclass $ipdata + * @param string $host + * @param int $port + * @param null|stdClass $ipdata * - * @return stdclass + * @return stdClass */ private function makeData($host, $port, $ipdata) { @@ -351,11 +351,11 @@ private function makeData($host, $port, $ipdata) /** * Creates an ip data object * - * @param string $ip in_addr - * @param int $size Byte size of in_addr + * @param string $ip in_addr + * @param int $size Byte size of in_addr * @param null|string $netmask Network mask * - * @return stdclass + * @return stdClass */ private function makeIpData($ip, $size, $netmask) { @@ -392,8 +392,7 @@ private function splitHostPort($hostName) $ip6 = substr($hostName, 1, $index - 1); $hostName = substr($hostName, $index + 1); - if (strpbrk($hostName, '[]') !== false - || substr_count($hostName, ':') > 1) { + if (strpbrk($hostName, '[]') !== false || substr_count($hostName, ':') > 1) { return $error; } } @@ -419,15 +418,18 @@ private function splitHostPort($hostName) * Wrapper around filter_var FILTER_VALIDATE_INT * * @param string $int - * @param int $min - * @param int $max + * @param int $min + * @param int $max + * + * @return bool */ private function validateInt($int, $min, $max) { $options = array( 'options' => array( 'min_range' => $min, - 'max_range' => $max) + 'max_range' => $max, + ), ); return false !== filter_var($int, FILTER_VALIDATE_INT, $options); diff --git a/app/vendor/composer/composer/src/Composer/Util/PackageSorter.php b/app/vendor/composer/composer/src/Composer/Util/PackageSorter.php index 8d8c9a06c..df2d89a4b 100644 --- a/app/vendor/composer/composer/src/Composer/Util/PackageSorter.php +++ b/app/vendor/composer/composer/src/Composer/Util/PackageSorter.php @@ -1,9 +1,17 @@ + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace Composer\Util; -use Composer\Package\Link; use Composer\Package\PackageInterface; class PackageSorter @@ -13,14 +21,15 @@ class PackageSorter * * Packages of equal weight retain the original order * - * @param array $packages - * @return array + * @param PackageInterface[] $packages + * @return PackageInterface[] sorted array */ - public static function sortPackages(array $packages) { + public static function sortPackages(array $packages) + { $usageList = array(); - foreach ($packages as $package) { /** @var PackageInterface $package */ - foreach (array_merge($package->getRequires(), $package->getDevRequires()) as $link) { /** @var Link $link */ + foreach ($packages as $package) { + foreach (array_merge($package->getRequires(), $package->getDevRequires()) as $link) { $target = $link->getTarget(); $usageList[$target][] = $package->getName(); } @@ -55,9 +64,9 @@ public static function sortPackages(array $packages) { $weightList = array(); - foreach ($packages as $name => $package) { - $weight = $computeImportance($name); - $weightList[$name] = $weight; + foreach ($packages as $index => $package) { + $weight = $computeImportance($package->getName()); + $weightList[$index] = $weight; } $stable_sort = function (&$array) { @@ -84,9 +93,10 @@ public static function sortPackages(array $packages) { $sortedPackages = array(); - foreach (array_keys($weightList) as $name) { - $sortedPackages[] = $packages[$name]; + foreach (array_keys($weightList) as $index) { + $sortedPackages[] = $packages[$index]; } + return $sortedPackages; } } diff --git a/app/vendor/composer/composer/src/Composer/Util/Perforce.php b/app/vendor/composer/composer/src/Composer/Util/Perforce.php index 52080d663..ea2b1152c 100644 --- a/app/vendor/composer/composer/src/Composer/Util/Perforce.php +++ b/app/vendor/composer/composer/src/Composer/Util/Perforce.php @@ -160,7 +160,7 @@ public function setStream($stream) public function isStream() { - return (strcmp($this->p4DepotType, 'stream') === 0); + return is_string($this->p4DepotType) && (strcmp($this->p4DepotType, 'stream') === 0); } public function getStream() @@ -204,11 +204,11 @@ public function setUser($user) public function queryP4User() { $this->getUser(); - if (strlen($this->p4User) > 0) { + if (strlen((string) $this->p4User) > 0) { return; } $this->p4User = $this->getP4variable('P4USER'); - if (strlen($this->p4User) > 0) { + if (strlen((string) $this->p4User) > 0) { return; } $this->p4User = $this->io->ask('Enter P4 User:'); @@ -220,6 +220,10 @@ public function queryP4User() $this->executeCommand($command); } + /** + * @param string $name + * @return ?string + */ protected function getP4variable($name) { if ($this->windowsFlag) { @@ -258,7 +262,7 @@ public function queryP4Password() return $this->p4Password; } $password = $this->getP4variable('P4PASSWD'); - if (strlen($password) <= 0) { + if (strlen((string) $password) <= 0) { $password = $this->io->askAndHideAnswer('Enter password for Perforce user ' . $this->getUser() . ': '); } $this->p4Password = $password; @@ -273,7 +277,7 @@ public function generateP4Command($command, $useClient = true) if ($useClient) { $p4Command .= '-c ' . $this->getClient() . ' '; } - $p4Command = $p4Command . '-p ' . $this->getPort() . ' ' . $command; + $p4Command .= '-p ' . $this->getPort() . ' ' . $command; return $p4Command; } @@ -389,7 +393,6 @@ public function p4Login() } else { $command = 'echo ' . ProcessExecutor::escape($password) . ' | ' . $this->generateP4Command(' login -a', false); $exitCode = $this->executeCommand($command); - $result = trim($this->commandResult); if ($exitCode) { throw new \Exception("Error logging in:" . $this->process->getErrorOutput()); } @@ -427,9 +430,7 @@ public function getFilePath($file, $identifier) { $index = strpos($identifier, '@'); if ($index === false) { - $path = $identifier. '/' . $file; - - return $path; + return $identifier. '/' . $file; } $path = substr($identifier, 0, $index) . '/' . $file . substr($identifier, $index); @@ -476,9 +477,7 @@ public function getBranches() $lastCommitArr = explode(' ', $lastCommit); $lastCommitNum = $lastCommitArr[1]; - $branches = array('master' => $possibleBranches[$this->p4Branch] . '@'. $lastCommitNum); - - return $branches; + return array('master' => $possibleBranches[$this->p4Branch] . '@'. $lastCommitNum); } public function getTags() @@ -519,7 +518,7 @@ public function checkStream() } /** - * @param string $reference + * @param string $reference * @return mixed|null */ protected function getChangeList($reference) @@ -541,8 +540,8 @@ protected function getChangeList($reference) } /** - * @param string $fromReference - * @param string $toReference + * @param string $fromReference + * @param string $toReference * @return mixed|null */ public function getCommitLogs($fromReference, $toReference) diff --git a/app/vendor/composer/composer/src/Composer/Util/Platform.php b/app/vendor/composer/composer/src/Composer/Util/Platform.php index 60bf9efa9..8f8ed2682 100644 --- a/app/vendor/composer/composer/src/Composer/Util/Platform.php +++ b/app/vendor/composer/composer/src/Composer/Util/Platform.php @@ -19,6 +19,37 @@ */ class Platform { + /** @var ?bool */ + private static $isVirtualBoxGuest = null; + /** @var ?bool */ + private static $isWindowsSubsystemForLinux = null; + + /** + * putenv() equivalent but updates the runtime global variables too + * + * @param string $name + * @param string $value + * @return void + */ + public static function putEnv($name, $value) + { + $value = (string) $value; + putenv($name . '=' . $value); + $_SERVER[$name] = $_ENV[$name] = $value; + } + + /** + * putenv('X') equivalent but updates the runtime global variables too + * + * @param string $name + * @return void + */ + public static function clearEnv($name) + { + putenv($name); + unset($_SERVER[$name], $_ENV[$name]); + } + /** * Parses tildes and environment variables in paths. * @@ -55,7 +86,7 @@ public static function getUserDirectory() return $home; } - if (function_exists('posix_getuid') && function_exists('posix_getpwuid')) { + if (\function_exists('posix_getuid') && \function_exists('posix_getpwuid')) { $info = posix_getpwuid(posix_getuid()); return $info['dir']; @@ -64,12 +95,38 @@ public static function getUserDirectory() throw new \RuntimeException('Could not determine user directory'); } + /** + * @return bool Whether the host machine is running on the Windows Subsystem for Linux (WSL) + */ + public static function isWindowsSubsystemForLinux() + { + if (null === self::$isWindowsSubsystemForLinux) { + self::$isWindowsSubsystemForLinux = false; + + // while WSL will be hosted within windows, WSL itself cannot be windows based itself. + if (self::isWindows()) { + return self::$isWindowsSubsystemForLinux = false; + } + + if ( + !ini_get('open_basedir') + && is_readable('/proc/version') + && false !== stripos(Silencer::call('file_get_contents', '/proc/version'), 'microsoft') + && !file_exists('/.dockerenv') // docker running inside WSL should not be seen as WSL + ) { + return self::$isWindowsSubsystemForLinux = true; + } + } + + return self::$isWindowsSubsystemForLinux; + } + /** * @return bool Whether the host machine is running a Windows OS */ public static function isWindows() { - return defined('PHP_WINDOWS_VERSION_BUILD'); + return \defined('PHP_WINDOWS_VERSION_BUILD'); } /** @@ -80,13 +137,96 @@ public static function strlen($str) { static $useMbString = null; if (null === $useMbString) { - $useMbString = function_exists('mb_strlen') && ini_get('mbstring.func_overload'); + $useMbString = \function_exists('mb_strlen') && ini_get('mbstring.func_overload'); } if ($useMbString) { return mb_strlen($str, '8bit'); } - return strlen($str); + return \strlen($str); + } + + /** + * @param ?resource $fd Open file descriptor or null to default to STDOUT + * @return bool + */ + public static function isTty($fd = null) + { + if ($fd === null) { + $fd = defined('STDOUT') ? STDOUT : fopen('php://stdout', 'w'); + } + + // detect msysgit/mingw and assume this is a tty because detection + // does not work correctly, see https://github.com/composer/composer/issues/9690 + if (in_array(strtoupper(getenv('MSYSTEM') ?: ''), array('MINGW32', 'MINGW64'), true)) { + return true; + } + + // modern cross-platform function, includes the fstat + // fallback so if it is present we trust it + if (function_exists('stream_isatty')) { + return stream_isatty($fd); + } + + // only trusting this if it is positive, otherwise prefer fstat fallback + if (function_exists('posix_isatty') && posix_isatty($fd)) { + return true; + } + + $stat = @fstat($fd); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; + } + + /** + * @return void + */ + public static function workaroundFilesystemIssues() + { + if (self::isVirtualBoxGuest()) { + usleep(200000); + } + } + + /** + * Attempts detection of VirtualBox guest VMs + * + * This works based on the process' user being "vagrant", the COMPOSER_RUNTIME_ENV env var being set to "virtualbox", or lsmod showing the virtualbox guest additions are loaded + * + * @return bool + */ + private static function isVirtualBoxGuest() + { + if (null === self::$isVirtualBoxGuest) { + self::$isVirtualBoxGuest = false; + if (self::isWindows()) { + return self::$isVirtualBoxGuest; + } + + if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) { + $processUser = posix_getpwuid(posix_geteuid()); + if ($processUser && $processUser['name'] === 'vagrant') { + return self::$isVirtualBoxGuest = true; + } + } + + if (getenv('COMPOSER_RUNTIME_ENV') === 'virtualbox') { + return self::$isVirtualBoxGuest = true; + } + + if (defined('PHP_OS_FAMILY') && PHP_OS_FAMILY === 'Linux') { + $process = new ProcessExecutor(); + try { + if (0 === $process->execute('lsmod | grep vboxguest', $ignoredOutput)) { + return self::$isVirtualBoxGuest = true; + } + } catch (\Exception $e) { + // noop + } + } + } + + return self::$isVirtualBoxGuest; } } diff --git a/app/vendor/composer/composer/src/Composer/Util/ProcessExecutor.php b/app/vendor/composer/composer/src/Composer/Util/ProcessExecutor.php index 658c2a136..665b0e7b3 100644 --- a/app/vendor/composer/composer/src/Composer/Util/ProcessExecutor.php +++ b/app/vendor/composer/composer/src/Composer/Util/ProcessExecutor.php @@ -15,18 +15,45 @@ use Composer\IO\IOInterface; use Symfony\Component\Process\Process; use Symfony\Component\Process\ProcessUtils; +use Symfony\Component\Process\Exception\RuntimeException; +use React\Promise\Promise; +use React\Promise\PromiseInterface; /** * @author Robert Schönthal + * @author Jordi Boggiano */ class ProcessExecutor { + const STATUS_QUEUED = 1; + const STATUS_STARTED = 2; + const STATUS_COMPLETED = 3; + const STATUS_FAILED = 4; + const STATUS_ABORTED = 5; + + /** @var int */ protected static $timeout = 300; - protected $captureOutput; - protected $errorOutput; + /** @var bool */ + protected $captureOutput = false; + /** @var string */ + protected $errorOutput = ''; + /** @var ?IOInterface */ protected $io; + /** + * @phpstan-var array> + */ + private $jobs = array(); + /** @var int */ + private $runningJobs = 0; + /** @var int */ + private $maxJobs = 10; + /** @var int */ + private $idGen = 0; + /** @var bool */ + private $allowAsync = false; + public function __construct(IOInterface $io = null) { $this->io = $io; @@ -35,13 +62,45 @@ public function __construct(IOInterface $io = null) /** * runs a process on the commandline * - * @param string $command the command to execute - * @param mixed $output the output will be written into this var if passed by ref - * if a callable is passed it will be used as output handler - * @param string $cwd the working directory - * @return int statuscode + * @param string $command the command to execute + * @param mixed $output the output will be written into this var if passed by ref + * if a callable is passed it will be used as output handler + * @param ?string $cwd the working directory + * @return int statuscode */ public function execute($command, &$output = null, $cwd = null) + { + if (func_num_args() > 1) { + return $this->doExecute($command, $cwd, false, $output); + } + + return $this->doExecute($command, $cwd, false); + } + + /** + * runs a process on the commandline in TTY mode + * + * @param string $command the command to execute + * @param ?string $cwd the working directory + * @return int statuscode + */ + public function executeTty($command, $cwd = null) + { + if (Platform::isTty()) { + return $this->doExecute($command, $cwd, true); + } + + return $this->doExecute($command, $cwd, false); + } + + /** + * @param string $command + * @param ?string $cwd + * @param bool $tty + * @param mixed $output + * @return int + */ + private function doExecute($command, $cwd, $tty, &$output = null) { if ($this->io && $this->io->isDebug()) { $safeCommand = preg_replace_callback('{://(?P[^:/\s]+):(?P[^@\s/]+)@}i', function ($m) { @@ -56,25 +115,33 @@ public function execute($command, &$output = null, $cwd = null) $this->io->writeError('Executing command ('.($cwd ?: 'CWD').'): '.$safeCommand); } + // TODO in 2.2, these two checks can be dropped as Symfony 4+ supports them out of the box // make sure that null translate to the proper directory in case the dir is a symlink // and we call a git command, because msysgit does not handle symlinks properly if (null === $cwd && Platform::isWindows() && false !== strpos($command, 'git') && getcwd()) { $cwd = realpath(getcwd()); } - if (null !== $cwd && !is_dir($cwd)) { throw new \RuntimeException('The given CWD for the process does not exist: '.$cwd); } - $this->captureOutput = func_num_args() > 1; - $this->errorOutput = null; + $this->captureOutput = func_num_args() > 3; + $this->errorOutput = ''; // TODO in v3, commands should be passed in as arrays of cmd + args if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) { $process = Process::fromShellCommandline($command, $cwd, null, null, static::getTimeout()); } else { + /** @phpstan-ignore-next-line */ $process = new Process($command, $cwd, null, null, static::getTimeout()); } + if (!Platform::isWindows() && $tty) { + try { + $process->setTty(true); + } catch (RuntimeException $e) { + // ignore TTY enabling errors + } + } $callback = is_callable($output) ? $output : array($this, 'outputHandler'); $process->run($callback); @@ -88,11 +155,237 @@ public function execute($command, &$output = null, $cwd = null) return $process->getExitCode(); } + /** + * starts a process on the commandline in async mode + * + * @param string $command the command to execute + * @param string $cwd the working directory + * @return PromiseInterface + */ + public function executeAsync($command, $cwd = null) + { + if (!$this->allowAsync) { + throw new \LogicException('You must use the ProcessExecutor instance which is part of a Composer\Loop instance to be able to run async processes'); + } + + $job = array( + 'id' => $this->idGen++, + 'status' => self::STATUS_QUEUED, + 'command' => $command, + 'cwd' => $cwd, + ); + + $resolver = function ($resolve, $reject) use (&$job) { + $job['status'] = ProcessExecutor::STATUS_QUEUED; + $job['resolve'] = $resolve; + $job['reject'] = $reject; + }; + + $self = $this; + + $canceler = function () use (&$job) { + if ($job['status'] === ProcessExecutor::STATUS_QUEUED) { + $job['status'] = ProcessExecutor::STATUS_ABORTED; + } + if ($job['status'] !== ProcessExecutor::STATUS_STARTED) { + return; + } + $job['status'] = ProcessExecutor::STATUS_ABORTED; + try { + if (defined('SIGINT')) { + $job['process']->signal(SIGINT); + } + } catch (\Exception $e) { + // signal can throw in various conditions, but we don't care if it fails + } + $job['process']->stop(1); + + throw new \RuntimeException('Aborted process'); + }; + + $promise = new Promise($resolver, $canceler); + $promise = $promise->then(function () use (&$job, $self) { + if ($job['process']->isSuccessful()) { + $job['status'] = ProcessExecutor::STATUS_COMPLETED; + } else { + $job['status'] = ProcessExecutor::STATUS_FAILED; + } + + // TODO 3.0 this should be done directly on $this when PHP 5.3 is dropped + $self->markJobDone(); + + return $job['process']; + }, function ($e) use (&$job, $self) { + $job['status'] = ProcessExecutor::STATUS_FAILED; + + $self->markJobDone(); + + throw $e; + }); + $this->jobs[$job['id']] = &$job; + + if ($this->runningJobs < $this->maxJobs) { + $this->startJob($job['id']); + } + + return $promise; + } + + /** + * @param int $id + * @return void + */ + private function startJob($id) + { + $job = &$this->jobs[$id]; + if ($job['status'] !== self::STATUS_QUEUED) { + return; + } + + // start job + $job['status'] = self::STATUS_STARTED; + $this->runningJobs++; + + $command = $job['command']; + $cwd = $job['cwd']; + + if ($this->io && $this->io->isDebug()) { + $safeCommand = preg_replace_callback('{://(?P[^:/\s]+):(?P[^@\s/]+)@}i', function ($m) { + if (preg_match('{^[a-f0-9]{12,}$}', $m['user'])) { + return '://***:***@'; + } + + return '://'.$m['user'].':***@'; + }, $command); + $safeCommand = preg_replace("{--password (.*[^\\\\]\') }", '--password \'***\' ', $safeCommand); + $this->io->writeError('Executing async command ('.($cwd ?: 'CWD').'): '.$safeCommand); + } + + // TODO in 2.2, these two checks can be dropped as Symfony 4+ supports them out of the box + // make sure that null translate to the proper directory in case the dir is a symlink + // and we call a git command, because msysgit does not handle symlinks properly + if (null === $cwd && Platform::isWindows() && false !== strpos($command, 'git') && getcwd()) { + $cwd = realpath(getcwd()); + } + if (null !== $cwd && !is_dir($cwd)) { + throw new \RuntimeException('The given CWD for the process does not exist: '.$cwd); + } + + try { + // TODO in v3, commands should be passed in as arrays of cmd + args + if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) { + $process = Process::fromShellCommandline($command, $cwd, null, null, static::getTimeout()); + } else { + $process = new Process($command, $cwd, null, null, static::getTimeout()); + } + } catch (\Exception $e) { + call_user_func($job['reject'], $e); + + return; + } catch (\Throwable $e) { + call_user_func($job['reject'], $e); + + return; + } + + $job['process'] = $process; + + try { + $process->start(); + } catch (\Exception $e) { + call_user_func($job['reject'], $e); + + return; + } catch (\Throwable $e) { + call_user_func($job['reject'], $e); + + return; + } + } + + /** + * @param ?int $index job id + * @return void + */ + public function wait($index = null) + { + while (true) { + if (!$this->countActiveJobs($index)) { + return; + } + + usleep(1000); + } + } + + /** + * @internal + * + * @return void + */ + public function enableAsync() + { + $this->allowAsync = true; + } + + /** + * @internal + * + * @param ?int $index job id + * @return int number of active (queued or started) jobs + */ + public function countActiveJobs($index = null) + { + // tick + foreach ($this->jobs as $job) { + if ($job['status'] === self::STATUS_STARTED) { + if (!$job['process']->isRunning()) { + call_user_func($job['resolve'], $job['process']); + } + } + + if ($this->runningJobs < $this->maxJobs) { + if ($job['status'] === self::STATUS_QUEUED) { + $this->startJob($job['id']); + } + } + } + + if (null !== $index) { + return $this->jobs[$index]['status'] < self::STATUS_COMPLETED ? 1 : 0; + } + + $active = 0; + foreach ($this->jobs as $job) { + if ($job['status'] < self::STATUS_COMPLETED) { + $active++; + } else { + unset($this->jobs[$job['id']]); + } + } + + return $active; + } + + /** + * @private + * + * @return void + */ + public function markJobDone() + { + $this->runningJobs--; + } + + /** + * @param ?string $output + * @return string[] + */ public function splitLines($output) { - $output = trim($output); + $output = trim((string) $output); - return ((string) $output === '') ? array() : preg_split('{\r?\n}', $output); + return $output === '' ? array() : preg_split('{\r?\n}', $output); } /** @@ -105,6 +398,14 @@ public function getErrorOutput() return $this->errorOutput; } + /** + * @private + * + * @param Process::ERR|Process::OUT $type + * @param string $buffer + * + * @return void + */ public function outputHandler($type, $buffer) { if ($this->captureOutput) { @@ -117,26 +418,25 @@ public function outputHandler($type, $buffer) return; } - if (method_exists($this->io, 'writeRaw')) { - if (Process::ERR === $type) { - $this->io->writeErrorRaw($buffer, false); - } else { - $this->io->writeRaw($buffer, false); - } + if (Process::ERR === $type) { + $this->io->writeErrorRaw($buffer, false); } else { - if (Process::ERR === $type) { - $this->io->writeError($buffer, false); - } else { - $this->io->write($buffer, false); - } + $this->io->writeRaw($buffer, false); } } + /** + * @return int the timeout in seconds + */ public static function getTimeout() { return static::$timeout; } + /** + * @param int $timeout the timeout in seconds + * @return void + */ public static function setTimeout($timeout) { static::$timeout = $timeout; @@ -145,7 +445,7 @@ public static function setTimeout($timeout) /** * Escapes a string to be used as a shell argument. * - * @param string $argument The argument that will be escaped + * @param ?string $argument The argument that will be escaped * * @return string The escaped argument */ @@ -155,50 +455,36 @@ public static function escape($argument) } /** - * Copy of ProcessUtils::escapeArgument() that is deprecated in Symfony 3.3 and removed in Symfony 4. + * Copy of Symfony's Process::escapeArgument() which is private * - * @param string $argument + * @param ?string $argument * * @return string */ private static function escapeArgument($argument) { - //Fix for PHP bug #43784 escapeshellarg removes % from given string - //Fix for PHP bug #49446 escapeshellarg doesn't work on Windows - //@see https://bugs.php.net/bug.php?id=43784 - //@see https://bugs.php.net/bug.php?id=49446 - if ('\\' === DIRECTORY_SEPARATOR) { - if ((string) $argument === '') { - return escapeshellarg($argument); - } - - $escapedArgument = ''; - $quote = false; - foreach (preg_split('/(")/', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) { - if ('"' === $part) { - $escapedArgument .= '\\"'; - } elseif (self::isSurroundedBy($part, '%')) { - // Avoid environment variable expansion - $escapedArgument .= '^%"'.substr($part, 1, -1).'"^%'; - } else { - // escape trailing backslash - if ('\\' === substr($part, -1)) { - $part .= '\\'; - } - $quote = true; - $escapedArgument .= $part; - } - } - if ($quote) { - $escapedArgument = '"'.$escapedArgument.'"'; - } - - return $escapedArgument; + if ('' === $argument || null === $argument) { + return '""'; + } + if ('\\' !== \DIRECTORY_SEPARATOR) { + return "'".str_replace("'", "'\\''", $argument)."'"; } + if (false !== strpos($argument, "\0")) { + $argument = str_replace("\0", '?', $argument); + } + if (!preg_match('/[\/()%!^"<>&|\s]/', $argument)) { + return $argument; + } + $argument = preg_replace('/(\\\\+)$/', '$1$1', $argument); - return "'".str_replace("'", "'\\''", $argument)."'"; + return '"'.str_replace(array('"', '^', '%', '!', "\n"), array('""', '"^^"', '"^%"', '"^!"', '!LF!'), $argument).'"'; } + /** + * @param string $arg + * @param string $char + * @return bool + */ private static function isSurroundedBy($arg, $char) { return 2 < strlen($arg) && $char === $arg[0] && $char === $arg[strlen($arg) - 1]; diff --git a/app/vendor/composer/composer/src/Composer/Util/RemoteFilesystem.php b/app/vendor/composer/composer/src/Composer/Util/RemoteFilesystem.php index 12eb05bb2..56ec5cadd 100644 --- a/app/vendor/composer/composer/src/Composer/Util/RemoteFilesystem.php +++ b/app/vendor/composer/composer/src/Composer/Util/RemoteFilesystem.php @@ -13,58 +13,79 @@ namespace Composer\Util; use Composer\Config; -use Composer\Composer; -use Composer\Semver\Constraint\Constraint; -use Composer\Package\Version\VersionParser; +use Composer\Downloader\MaxFileSizeExceededException; use Composer\IO\IOInterface; use Composer\Downloader\TransportException; use Composer\CaBundle\CaBundle; -use Psr\Log\LoggerInterface; +use Composer\Util\Http\Response; +use Composer\Util\Http\ProxyManager; /** + * @internal * @author François Pluchino * @author Jordi Boggiano * @author Nils Adermann */ class RemoteFilesystem { + /** @var IOInterface */ private $io; + /** @var Config */ private $config; + /** @var string */ private $scheme; + /** @var int */ private $bytesMax; + /** @var string */ private $originUrl; + /** @var string */ private $fileUrl; + /** @var ?string */ private $fileName; - private $retry; + /** @var bool */ + private $retry = false; + /** @var bool */ private $progress; + /** @var ?int */ private $lastProgress; + /** @var mixed[] */ private $options = array(); + /** @var array */ private $peerCertificateMap = array(); + /** @var bool */ private $disableTls = false; - private $retryAuthFailure; + /** @var string[] */ private $lastHeaders; - private $storeAuth; + /** @var bool */ + private $storeAuth = false; + /** @var AuthHelper */ + private $authHelper; + /** @var bool */ private $degradedMode = false; + /** @var int */ private $redirects; + /** @var int */ private $maxRedirects = 20; - private $displayedOriginAuthentications = array(); + /** @var ProxyManager */ + private $proxyManager; /** * Constructor. * * @param IOInterface $io The IO instance * @param Config $config The config - * @param array $options The options + * @param mixed[] $options The options * @param bool $disableTls + * @param AuthHelper $authHelper */ - public function __construct(IOInterface $io, Config $config = null, array $options = array(), $disableTls = false) + public function __construct(IOInterface $io, Config $config, array $options = array(), $disableTls = false, AuthHelper $authHelper = null) { $this->io = $io; // Setup TLS options // The cafile option can be set via config.json if ($disableTls === false) { - $this->options = $this->getTlsDefaults($options); + $this->options = StreamContextFactory::getTlsDefaults($options, $io); } else { $this->disableTls = true; } @@ -72,16 +93,18 @@ public function __construct(IOInterface $io, Config $config = null, array $optio // handle the other externally set options normally. $this->options = array_replace_recursive($this->options, $options); $this->config = $config; + $this->authHelper = isset($authHelper) ? $authHelper : new AuthHelper($io, $config); + $this->proxyManager = ProxyManager::getInstance(); } /** * Copy the remote file in local. * - * @param string $originUrl The origin URL - * @param string $fileUrl The file URL - * @param string $fileName the local filename - * @param bool $progress Display the progression - * @param array $options Additional context options + * @param string $originUrl The origin URL + * @param string $fileUrl The file URL + * @param string $fileName the local filename + * @param bool $progress Display the progression + * @param mixed[] $options Additional context options * * @return bool true */ @@ -93,10 +116,10 @@ public function copy($originUrl, $fileUrl, $fileName, $progress = true, $options /** * Get the content. * - * @param string $originUrl The origin URL - * @param string $fileUrl The file URL - * @param bool $progress Display the progression - * @param array $options Additional context options + * @param string $originUrl The origin URL + * @param string $fileUrl The file URL + * @param bool $progress Display the progression + * @param mixed[] $options Additional context options * * @return bool|string The content */ @@ -108,7 +131,7 @@ public function getContents($originUrl, $fileUrl, $progress = true, $options = a /** * Retrieve the options set in the constructor * - * @return array Options + * @return mixed[] Options */ public function getOptions() { @@ -118,7 +141,8 @@ public function getOptions() /** * Merges new options * - * @param array $options + * @param mixed[] $options + * @return void */ public function setOptions(array $options) { @@ -138,7 +162,7 @@ public function isTlsDisabled() /** * Returns the headers of the last request * - * @return array + * @return string[] */ public function getLastHeaders() { @@ -146,31 +170,10 @@ public function getLastHeaders() } /** - * @param array $headers array of returned headers like from getLastHeaders() - * @param string $name header name (case insensitive) - * @return string|null - */ - public function findHeaderValue(array $headers, $name) - { - $value = null; - foreach ($headers as $header) { - if (preg_match('{^'.$name.':\s*(.+?)\s*$}i', $header, $match)) { - $value = $match[1]; - } elseif (preg_match('{^HTTP/}i', $header)) { - // In case of redirects, http_response_headers contains the headers of all responses - // so we reset the flag when a new response is being parsed as we are only interested in the last response - $value = null; - } - } - - return $value; - } - - /** - * @param array $headers array of returned headers like from getLastHeaders() + * @param string[] $headers array of returned headers like from getLastHeaders() * @return int|null */ - public function findStatusCode(array $headers) + public static function findStatusCode(array $headers) { $value = null; foreach ($headers as $header) { @@ -185,7 +188,7 @@ public function findStatusCode(array $headers) } /** - * @param array $headers array of returned headers like from getLastHeaders() + * @param string[] $headers array of returned headers like from getLastHeaders() * @return string|null */ public function findStatusMessage(array $headers) @@ -205,11 +208,11 @@ public function findStatusMessage(array $headers) /** * Get file content or copy action. * - * @param string $originUrl The origin URL - * @param string $fileUrl The file URL - * @param array $additionalOptions context options - * @param string $fileName the local filename - * @param bool $progress Display the progression + * @param string $originUrl The origin URL + * @param string $fileUrl The file URL + * @param mixed[] $additionalOptions context options + * @param string $fileName the local filename + * @param bool $progress Display the progression * * @throws TransportException|\Exception * @throws TransportException When the file could not be downloaded @@ -218,27 +221,6 @@ public function findStatusMessage(array $headers) */ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $fileName = null, $progress = true) { - if (strpos($originUrl, '.github.com') === (strlen($originUrl) - 11)) { - $originUrl = 'github.com'; - } - - // Gitlab can be installed in a non-root context (i.e. gitlab.com/foo). When downloading archives the originUrl - // is the host without the path, so we look for the registered gitlab-domains matching the host here - if ( - $this->config - && is_array($this->config->get('gitlab-domains')) - && false === strpos($originUrl, '/') - && !in_array($originUrl, $this->config->get('gitlab-domains')) - ) { - foreach ($this->config->get('gitlab-domains') as $gitlabDomain) { - if (0 === strpos($gitlabDomain, $originUrl)) { - $originUrl = $gitlabDomain; - break; - } - } - unset($gitlabDomain); - } - $this->scheme = parse_url($fileUrl, PHP_URL_SCHEME); $this->bytesMax = 0; $this->originUrl = $originUrl; @@ -246,18 +228,13 @@ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $file $this->fileName = $fileName; $this->progress = $progress; $this->lastProgress = null; - $this->retryAuthFailure = true; + $retryAuthFailure = true; $this->lastHeaders = array(); $this->redirects = 1; // The first request counts. - // capture username/password from URL if there is one - if (preg_match('{^https?://([^:/]+):([^@/]+)@([^/]+)}i', $fileUrl, $match)) { - $this->io->setAuthentication($originUrl, rawurldecode($match[1]), rawurldecode($match[2])); - } - $tempAdditionalOptions = $additionalOptions; if (isset($tempAdditionalOptions['retry-auth-failure'])) { - $this->retryAuthFailure = (bool) $tempAdditionalOptions['retry-auth-failure']; + $retryAuthFailure = (bool) $tempAdditionalOptions['retry-auth-failure']; unset($tempAdditionalOptions['retry-auth-failure']); } @@ -275,14 +252,6 @@ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $file $origFileUrl = $fileUrl; - if (isset($options['github-token'])) { - // only add the access_token if it is actually a github URL (in case we were redirected to S3) - if (preg_match('{^https?://api\.github\.com/}', $fileUrl)) { - $options['http']['header'][] = 'Authorization: token '.$options['github-token']; - } - unset($options['github-token']); - } - if (isset($options['gitlab-token'])) { $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['gitlab-token']; unset($options['gitlab-token']); @@ -292,21 +261,27 @@ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $file $options['http']['ignore_errors'] = true; } - if ($this->degradedMode && substr($fileUrl, 0, 26) === 'http://repo.packagist.org/') { + if ($this->degradedMode && strpos($fileUrl, 'http://repo.packagist.org/') === 0) { // access packagist using the resolved IPv4 instead of the hostname to force IPv4 protocol $fileUrl = 'http://' . gethostbyname('repo.packagist.org') . substr($fileUrl, 20); $degradedPackagist = true; } + $maxFileSize = null; + if (isset($options['max_file_size'])) { + $maxFileSize = $options['max_file_size']; + unset($options['max_file_size']); + } + $ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet'))); - $actualContextOptions = stream_context_get_options($ctx); - $usingProxy = !empty($actualContextOptions['http']['proxy']) ? ' using proxy ' . $actualContextOptions['http']['proxy'] : ''; - $this->io->writeError((substr($origFileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $this->stripCredentialsFromUrl($origFileUrl) . $usingProxy, true, IOInterface::DEBUG); - unset($origFileUrl, $actualContextOptions); + $proxy = $this->proxyManager->getProxyForRequest($fileUrl); + $usingProxy = $proxy->getFormattedUrl(' using proxy (%s)'); + $this->io->writeError((strpos($origFileUrl, 'http') === 0 ? 'Downloading ' : 'Reading ') . Url::sanitize($origFileUrl) . $usingProxy, true, IOInterface::DEBUG); + unset($origFileUrl, $proxy, $usingProxy); // Check for secure HTTP, but allow insecure Packagist calls to $hashed providers as file integrity is verified with sha256 - if ((!preg_match('{^http://(repo\.)?packagist\.org/p/}', $fileUrl) || (false === strpos($fileUrl, '$') && false === strpos($fileUrl, '%24'))) && empty($degradedPackagist) && $this->config) { + if ((!preg_match('{^http://(repo\.)?packagist\.org/p/}', $fileUrl) || (false === strpos($fileUrl, '$') && false === strpos($fileUrl, '%24'))) && empty($degradedPackagist)) { $this->config->prohibitUrlByConfig($fileUrl, $this->io); } @@ -325,27 +300,33 @@ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $file return true; }); + $http_response_header = array(); try { - $result = $this->getRemoteContents($originUrl, $fileUrl, $ctx, $http_response_header); + $result = $this->getRemoteContents($originUrl, $fileUrl, $ctx, $http_response_header, $maxFileSize); if (!empty($http_response_header[0])) { - $statusCode = $this->findStatusCode($http_response_header); - if ($statusCode >= 400 && $this->findHeaderValue($http_response_header, 'content-type') === 'application/json') { - self::outputWarnings($this->io, $originUrl, json_decode($result, true)); + $statusCode = self::findStatusCode($http_response_header); + if ($statusCode >= 400 && Response::findHeaderValue($http_response_header, 'content-type') === 'application/json') { + HttpDownloader::outputWarnings($this->io, $originUrl, json_decode($result, true)); } - if (in_array($statusCode, array(401, 403)) && $this->retryAuthFailure) { - $this->promptAuthAndRetry($statusCode, $this->findStatusMessage($http_response_header), null, $http_response_header); + if (in_array($statusCode, array(401, 403)) && $retryAuthFailure) { + $this->promptAuthAndRetry($statusCode, $this->findStatusMessage($http_response_header), $http_response_header); } } - $contentLength = !empty($http_response_header[0]) ? $this->findHeaderValue($http_response_header, 'content-length') : null; + $contentLength = !empty($http_response_header[0]) ? Response::findHeaderValue($http_response_header, 'content-length') : null; if ($contentLength && Platform::strlen($result) < $contentLength) { // alas, this is not possible via the stream callback because STREAM_NOTIFY_COMPLETED is documented, but not implemented anywhere in PHP $e = new TransportException('Content-Length mismatch, received '.Platform::strlen($result).' bytes out of the expected '.$contentLength); $e->setHeaders($http_response_header); - $e->setStatusCode($this->findStatusCode($http_response_header)); - $e->setResponse($result); + $e->setStatusCode(self::findStatusCode($http_response_header)); + try { + $e->setResponse($this->decodeResult($result, $http_response_header)); + } catch (\Exception $discarded) { + $e->setResponse($result); + } + $this->io->writeError('Content-Length mismatch, received '.Platform::strlen($result).' out of '.$contentLength.' bytes: (' . base64_encode($result).')', true, IOInterface::DEBUG); throw $e; @@ -365,10 +346,10 @@ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $file } catch (\Exception $e) { if ($e instanceof TransportException && !empty($http_response_header[0])) { $e->setHeaders($http_response_header); - $e->setStatusCode($this->findStatusCode($http_response_header)); + $e->setStatusCode(self::findStatusCode($http_response_header)); } if ($e instanceof TransportException && $result !== false) { - $e->setResponse($result); + $e->setResponse($this->decodeResult($result, $http_response_header)); } $result = false; } @@ -395,31 +376,31 @@ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $file $contentType = null; $locationHeader = null; if (!empty($http_response_header[0])) { - $statusCode = $this->findStatusCode($http_response_header); - $contentType = $this->findHeaderValue($http_response_header, 'content-type'); - $locationHeader = $this->findHeaderValue($http_response_header, 'location'); + $statusCode = self::findStatusCode($http_response_header); + $contentType = Response::findHeaderValue($http_response_header, 'content-type'); + $locationHeader = Response::findHeaderValue($http_response_header, 'location'); } // check for bitbucket login page asking to authenticate if ($originUrl === 'bitbucket.org' - && !$this->isPublicBitBucketDownload($fileUrl) + && !$this->authHelper->isPublicBitBucketDownload($fileUrl) && substr($fileUrl, -4) === '.zip' && (!$locationHeader || substr(parse_url($locationHeader, PHP_URL_PATH), -4) !== '.zip') && $contentType && preg_match('{^text/html\b}i', $contentType) ) { $result = false; - if ($this->retryAuthFailure) { + if ($retryAuthFailure) { $this->promptAuthAndRetry(401); } } // check for gitlab 404 when downloading archives if ($statusCode === 404 - && $this->config && in_array($originUrl, $this->config->get('gitlab-domains'), true) + && in_array($originUrl, $this->config->get('gitlab-domains'), true) && false !== strpos($fileUrl, 'archive.zip') ) { $result = false; - if ($this->retryAuthFailure) { + if ($retryAuthFailure) { $this->promptAuthAndRetry(401); } } @@ -434,13 +415,13 @@ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $file // fail 4xx and 5xx responses and capture the response if ($statusCode && $statusCode >= 400 && $statusCode <= 599) { if (!$this->retry) { - if ($this->progress && !$this->retry && !$isRedirect) { + if ($this->progress && !$isRedirect) { $this->io->overwriteError("Downloading (failed)", false); } $e = new TransportException('The "'.$this->fileUrl.'" file could not be downloaded ('.$http_response_header[0].')', $statusCode); $e->setHeaders($http_response_header); - $e->setResponse($result); + $e->setResponse($this->decodeResult($result, $http_response_header)); $e->setStatusCode($statusCode); throw $e; } @@ -452,36 +433,22 @@ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $file } // decode gzip - if ($result && extension_loaded('zlib') && substr($fileUrl, 0, 4) === 'http' && !$hasFollowedRedirect) { - $contentEncoding = $this->findHeaderValue($http_response_header, 'content-encoding'); - $decode = $contentEncoding && 'gzip' === strtolower($contentEncoding); - - if ($decode) { - try { - if (PHP_VERSION_ID >= 50400) { - $result = zlib_decode($result); - } else { - // work around issue with gzuncompress & co that do not work with all gzip checksums - $result = file_get_contents('compress.zlib://data:application/octet-stream;base64,'.base64_encode($result)); - } - - if (!$result) { - throw new TransportException('Failed to decode zlib stream'); - } - } catch (\Exception $e) { - if ($this->degradedMode) { - throw $e; - } + if ($result && extension_loaded('zlib') && strpos($fileUrl, 'http') === 0 && !$hasFollowedRedirect) { + try { + $result = $this->decodeResult($result, $http_response_header); + } catch (\Exception $e) { + if ($this->degradedMode) { + throw $e; + } - $this->degradedMode = true; - $this->io->writeError(array( - '', - 'Failed to decode response: '.$e->getMessage().'', - 'Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info', - )); + $this->degradedMode = true; + $this->io->writeError(array( + '', + 'Failed to decode response: '.$e->getMessage().'', + 'Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info', + )); - return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress); - } + return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress); } } @@ -547,9 +514,8 @@ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $file $result = $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress); - if ($this->storeAuth && $this->config) { - $authHelper = new AuthHelper($this->io, $this->config); - $authHelper->storeAuth($this->originUrl, $this->storeAuth); + if ($this->storeAuth) { + $this->authHelper->storeAuth($this->originUrl, $this->storeAuth); $this->storeAuth = false; } @@ -586,21 +552,34 @@ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $file /** * Get contents of remote URL. * - * @param string $originUrl The origin URL - * @param string $fileUrl The file URL - * @param resource $context The stream context + * @param string $originUrl The origin URL + * @param string $fileUrl The file URL + * @param resource $context The stream context + * @param int $maxFileSize The maximum allowed file size * * @return string|false The response contents or false on failure */ - protected function getRemoteContents($originUrl, $fileUrl, $context, array &$responseHeaders = null) + protected function getRemoteContents($originUrl, $fileUrl, $context, array &$responseHeaders = null, $maxFileSize = null) { + $result = false; + try { $e = null; - $result = file_get_contents($fileUrl, false, $context); - } catch (\Throwable $e) { + if ($maxFileSize !== null) { + $result = file_get_contents($fileUrl, false, $context, 0, $maxFileSize); + } else { + // passing `null` to file_get_contents will convert `null` to `0` and return 0 bytes + $result = file_get_contents($fileUrl, false, $context); + } } catch (\Exception $e) { + } catch (\Throwable $e) { + } + + if ($maxFileSize !== null && Platform::strlen($result) >= $maxFileSize) { + throw new MaxFileSizeExceededException('Maximum allowed download size reached. Downloaded ' . Platform::strlen($result) . ' of allowed ' . $maxFileSize . ' bytes'); } + // https://www.php.net/manual/en/reserved.variables.httpresponseheader.php $responseHeaders = isset($http_response_header) ? $http_response_header : array(); if (null !== $e) { @@ -638,7 +617,7 @@ protected function callbackGet($notificationCode, $severity, $message, $messageC case STREAM_NOTIFY_PROGRESS: if ($this->bytesMax > 0 && $this->progress) { - $progression = min(100, round($bytesTransferred / $this->bytesMax * 100)); + $progression = min(100, (int) round($bytesTransferred / $this->bytesMax * 100)); if ((0 === $progression % 5) && 100 !== $progression && $progression !== $this->lastProgress) { $this->lastProgress = $progression; @@ -652,111 +631,16 @@ protected function callbackGet($notificationCode, $severity, $message, $messageC } } - protected function promptAuthAndRetry($httpStatus, $reason = null, $warning = null, $headers = array()) + protected function promptAuthAndRetry($httpStatus, $reason = null, $headers = array()) { - if ($this->config && in_array($this->originUrl, $this->config->get('github-domains'), true)) { - $gitHubUtil = new GitHub($this->io, $this->config, null); - $message = "\n"; - - $rateLimited = $gitHubUtil->isRateLimited($headers); - if ($rateLimited) { - $rateLimit = $gitHubUtil->getRateLimit($headers); - if ($this->io->hasAuthentication($this->originUrl)) { - $message = 'Review your configured GitHub OAuth token or enter a new one to go over the API rate limit.'; - } else { - $message = 'Create a GitHub OAuth token to go over the API rate limit.'; - } - - $message = sprintf( - 'GitHub API limit (%d calls/hr) is exhausted, could not fetch '.$this->fileUrl.'. '.$message.' You can also wait until %s for the rate limit to reset.', - $rateLimit['limit'], - $rateLimit['reset'] - )."\n"; - } else { - $message .= 'Could not fetch '.$this->fileUrl.', please '; - if ($this->io->hasAuthentication($this->originUrl)) { - $message .= 'review your configured GitHub OAuth token or enter a new one to access private repos'; - } else { - $message .= 'create a GitHub OAuth token to access private repos'; - } - } - - if (!$gitHubUtil->authorizeOAuth($this->originUrl) - && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($this->originUrl, $message)) - ) { - throw new TransportException('Could not authenticate against '.$this->originUrl, 401); - } - } elseif ($this->config && in_array($this->originUrl, $this->config->get('gitlab-domains'), true)) { - $message = "\n".'Could not fetch '.$this->fileUrl.', enter your ' . $this->originUrl . ' credentials ' .($httpStatus === 401 ? 'to access private repos' : 'to go over the API rate limit'); - $gitLabUtil = new GitLab($this->io, $this->config, null); - - if ($this->io->hasAuthentication($this->originUrl) && ($auth = $this->io->getAuthentication($this->originUrl)) && in_array($auth['password'], array('gitlab-ci-token', 'private-token', 'oauth2'), true)) { - throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", $httpStatus); - } - - if (!$gitLabUtil->authorizeOAuth($this->originUrl) - && (!$this->io->isInteractive() || !$gitLabUtil->authorizeOAuthInteractively($this->scheme, $this->originUrl, $message)) - ) { - throw new TransportException('Could not authenticate against '.$this->originUrl, 401); - } - } elseif ($this->config && ($this->originUrl === 'bitbucket.org' || $this->originUrl === 'api.bitbucket.org')) { - $askForOAuthToken = true; - $this->originUrl = 'bitbucket.org'; - - if ($this->io->hasAuthentication($this->originUrl)) { - $auth = $this->io->getAuthentication($this->originUrl); - if ($auth['username'] !== 'x-token-auth') { - $bitbucketUtil = new Bitbucket($this->io, $this->config); - $accessToken = $bitbucketUtil->requestToken($this->originUrl, $auth['username'], $auth['password']); - if (!empty($accessToken)) { - $this->io->setAuthentication($this->originUrl, 'x-token-auth', $accessToken); - $askForOAuthToken = false; - } - } else { - throw new TransportException('Could not authenticate against ' . $this->originUrl, 401); - } - } - - if ($askForOAuthToken) { - $message = "\n".'Could not fetch ' . $this->fileUrl . ', please create a bitbucket OAuth token to ' . (($httpStatus === 401 || $httpStatus === 403) ? 'access private repos' : 'go over the API rate limit'); - $bitBucketUtil = new Bitbucket($this->io, $this->config); - if (! $bitBucketUtil->authorizeOAuth($this->originUrl) - && (! $this->io->isInteractive() || !$bitBucketUtil->authorizeOAuthInteractively($this->originUrl, $message)) - ) { - throw new TransportException('Could not authenticate against ' . $this->originUrl, 401); - } - } - } else { - // 404s are only handled for github - if ($httpStatus === 404) { - return; - } + $result = $this->authHelper->promptAuthIfNeeded($this->fileUrl, $this->originUrl, $httpStatus, $reason, $headers); - // fail if the console is not interactive - if (!$this->io->isInteractive()) { - if ($httpStatus === 401) { - $message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console to authenticate"; - } - if ($httpStatus === 403) { - $message = "The '" . $this->fileUrl . "' URL could not be accessed: " . $reason; - } + $this->storeAuth = $result['storeAuth']; + $this->retry = $result['retry']; - throw new TransportException($message, $httpStatus); - } - // fail if we already have auth - if ($this->io->hasAuthentication($this->originUrl)) { - throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", $httpStatus); - } - - $this->io->writeError(' Authentication required ('.$this->originUrl.'):'); - $username = $this->io->ask(' Username: '); - $password = $this->io->askAndHideAnswer(' Password: '); - $this->io->setAuthentication($this->originUrl, $username, $password); - $this->storeAuth = $this->config->get('store-auths'); + if ($this->retry) { + throw new TransportException('RETRY'); } - - $this->retry = true; - throw new TransportException('RETRY'); } protected function getOptionsForUrl($originUrl, $additionalOptions) @@ -816,40 +700,7 @@ protected function getOptionsForUrl($originUrl, $additionalOptions) $headers[] = 'Connection: close'; } - if ($this->io->hasAuthentication($originUrl)) { - $authenticationDisplayMessage = null; - $auth = $this->io->getAuthentication($originUrl); - if ($auth['password'] === 'bearer') { - $headers[] = 'Authorization: Bearer '.$auth['username']; - } elseif ('github.com' === $originUrl && 'x-oauth-basic' === $auth['password']) { - $options['github-token'] = $auth['username']; - $authenticationDisplayMessage = 'Using GitHub token authentication'; - } elseif ($this->config && in_array($originUrl, $this->config->get('gitlab-domains'), true)) { - if ($auth['password'] === 'oauth2') { - $headers[] = 'Authorization: Bearer '.$auth['username']; - $authenticationDisplayMessage = 'Using GitLab OAuth token authentication'; - } elseif ($auth['password'] === 'private-token' || $auth['password'] === 'gitlab-ci-token') { - $headers[] = 'PRIVATE-TOKEN: '.$auth['username']; - $authenticationDisplayMessage = 'Using GitLab private token authentication'; - } - } elseif ('bitbucket.org' === $originUrl - && $this->fileUrl !== Bitbucket::OAUTH2_ACCESS_TOKEN_URL && 'x-token-auth' === $auth['username'] - ) { - if (!$this->isPublicBitBucketDownload($this->fileUrl)) { - $headers[] = 'Authorization: Bearer ' . $auth['password']; - $authenticationDisplayMessage = 'Using Bitbucket OAuth token authentication'; - } - } else { - $authStr = base64_encode($auth['username'] . ':' . $auth['password']); - $headers[] = 'Authorization: Basic '.$authStr; - $authenticationDisplayMessage = 'Using HTTP basic authentication with username "' . $auth['username'] . '"'; - } - - if ($authenticationDisplayMessage && !in_array($originUrl, $this->displayedOriginAuthentications, true)) { - $this->io->writeError($authenticationDisplayMessage, true, IOInterface::DEBUG); - $this->displayedOriginAuthentications[] = $originUrl; - } - } + $headers = $this->authHelper->addAuthenticationHeader($headers, $originUrl, $this->fileUrl); $options['http']['follow_location'] = 0; @@ -865,7 +716,7 @@ protected function getOptionsForUrl($originUrl, $additionalOptions) private function handleRedirect(array $http_response_header, array $additionalOptions, $result) { - if ($locationHeader = $this->findHeaderValue($http_response_header, 'location')) { + if ($locationHeader = Response::findHeaderValue($http_response_header, 'location')) { if (parse_url($locationHeader, PHP_URL_SCHEME)) { // Absolute URL; e.g. https://example.com/composer $targetUrl = $locationHeader; @@ -889,7 +740,7 @@ private function handleRedirect(array $http_response_header, array $additionalOp $this->redirects++; $this->io->writeError('', true, IOInterface::DEBUG); - $this->io->writeError(sprintf('Following redirect (%u) %s', $this->redirects, $this->stripCredentialsFromUrl($targetUrl)), true, IOInterface::DEBUG); + $this->io->writeError(sprintf('Following redirect (%u) %s', $this->redirects, Url::sanitize($targetUrl)), true, IOInterface::DEBUG); $additionalOptions['redirects'] = $this->redirects; @@ -899,7 +750,7 @@ private function handleRedirect(array $http_response_header, array $additionalOp if (!$this->retry) { $e = new TransportException('The "'.$this->fileUrl.'" file could not be downloaded, got redirect without Location ('.$http_response_header[0].')'); $e->setHeaders($http_response_header); - $e->setResponse($result); + $e->setResponse($this->decodeResult($result, $http_response_header)); throw $e; } @@ -907,115 +758,12 @@ private function handleRedirect(array $http_response_header, array $additionalOp return false; } - /** - * @param array $options - * - * @return array - */ - private function getTlsDefaults(array $options) - { - $ciphers = implode(':', array( - 'ECDHE-RSA-AES128-GCM-SHA256', - 'ECDHE-ECDSA-AES128-GCM-SHA256', - 'ECDHE-RSA-AES256-GCM-SHA384', - 'ECDHE-ECDSA-AES256-GCM-SHA384', - 'DHE-RSA-AES128-GCM-SHA256', - 'DHE-DSS-AES128-GCM-SHA256', - 'kEDH+AESGCM', - 'ECDHE-RSA-AES128-SHA256', - 'ECDHE-ECDSA-AES128-SHA256', - 'ECDHE-RSA-AES128-SHA', - 'ECDHE-ECDSA-AES128-SHA', - 'ECDHE-RSA-AES256-SHA384', - 'ECDHE-ECDSA-AES256-SHA384', - 'ECDHE-RSA-AES256-SHA', - 'ECDHE-ECDSA-AES256-SHA', - 'DHE-RSA-AES128-SHA256', - 'DHE-RSA-AES128-SHA', - 'DHE-DSS-AES128-SHA256', - 'DHE-RSA-AES256-SHA256', - 'DHE-DSS-AES256-SHA', - 'DHE-RSA-AES256-SHA', - 'AES128-GCM-SHA256', - 'AES256-GCM-SHA384', - 'AES128-SHA256', - 'AES256-SHA256', - 'AES128-SHA', - 'AES256-SHA', - 'AES', - 'CAMELLIA', - 'DES-CBC3-SHA', - '!aNULL', - '!eNULL', - '!EXPORT', - '!DES', - '!RC4', - '!MD5', - '!PSK', - '!aECDH', - '!EDH-DSS-DES-CBC3-SHA', - '!EDH-RSA-DES-CBC3-SHA', - '!KRB5-DES-CBC3-SHA', - )); - - /** - * CN_match and SNI_server_name are only known once a URL is passed. - * They will be set in the getOptionsForUrl() method which receives a URL. - * - * cafile or capath can be overridden by passing in those options to constructor. - */ - $defaults = array( - 'ssl' => array( - 'ciphers' => $ciphers, - 'verify_peer' => true, - 'verify_depth' => 7, - 'SNI_enabled' => true, - 'capture_peer_cert' => true, - ), - ); - - if (isset($options['ssl'])) { - $defaults['ssl'] = array_replace_recursive($defaults['ssl'], $options['ssl']); - } - - $caBundleLogger = $this->io instanceof LoggerInterface ? $this->io : null; - - /** - * Attempt to find a local cafile or throw an exception if none pre-set - * The user may go download one if this occurs. - */ - if (!isset($defaults['ssl']['cafile']) && !isset($defaults['ssl']['capath'])) { - $result = CaBundle::getSystemCaRootBundlePath($caBundleLogger); - - if (is_dir($result)) { - $defaults['ssl']['capath'] = $result; - } else { - $defaults['ssl']['cafile'] = $result; - } - } - - if (isset($defaults['ssl']['cafile']) && (!is_readable($defaults['ssl']['cafile']) || !CaBundle::validateCaFile($defaults['ssl']['cafile'], $caBundleLogger))) { - throw new TransportException('The configured cafile was not valid or could not be read.'); - } - - if (isset($defaults['ssl']['capath']) && (!is_dir($defaults['ssl']['capath']) || !is_readable($defaults['ssl']['capath']))) { - throw new TransportException('The configured capath was not valid or could not be read.'); - } - - /** - * Disable TLS compression to prevent CRIME attacks where supported. - */ - if (PHP_VERSION_ID >= 50413) { - $defaults['ssl']['disable_compression'] = true; - } - - return $defaults; - } - /** * Fetch certificate common name and fingerprint for validation of SAN. * * @todo Remove when PHP 5.6 is minimum supported version. + * + * @return ?array{cn: string, fp: string} */ private function getCertificateCnAndFp($url, $options) { @@ -1036,7 +784,7 @@ private function getCertificateCnAndFp($url, $options) // Ideally this would just use stream_socket_client() to avoid sending a // HTTP request but that does not capture the certificate. if (false === $handle = @fopen($url, 'rb', false, $context)) { - return; + return null; } // Close non authenticated connection without reading any content. @@ -1055,6 +803,8 @@ private function getCertificateCnAndFp($url, $options) ); } } + + return null; } private function getUrlAuthority($url) @@ -1082,68 +832,27 @@ private function getUrlAuthority($url) return parse_url($url, PHP_URL_HOST).':'.$port; } - /** - * @link https://github.com/composer/composer/issues/5584 - * - * @param string $urlToBitBucketFile URL to a file at bitbucket.org. - * - * @return bool Whether the given URL is a public BitBucket download which requires no authentication. - */ - private function isPublicBitBucketDownload($urlToBitBucketFile) + private function decodeResult($result, $http_response_header) { - $domain = parse_url($urlToBitBucketFile, PHP_URL_HOST); - if (strpos($domain, 'bitbucket.org') === false) { - // Bitbucket downloads are hosted on amazonaws. - // We do not need to authenticate there at all - return true; - } - - $path = parse_url($urlToBitBucketFile, PHP_URL_PATH); - - // Path for a public download follows this pattern /{user}/{repo}/downloads/{whatever} - // {@link https://blog.bitbucket.org/2009/04/12/new-feature-downloads/} - $pathParts = explode('/', $path); - - return count($pathParts) >= 4 && $pathParts[3] == 'downloads'; - } + // decode gzip + if ($result && extension_loaded('zlib')) { + $contentEncoding = Response::findHeaderValue($http_response_header, 'content-encoding'); + $decode = $contentEncoding && 'gzip' === strtolower($contentEncoding); - public static function outputWarnings(IOInterface $io, $url, $data) - { - foreach (array('warning', 'info') as $type) { - if (empty($data[$type])) { - continue; - } + if ($decode) { + if (PHP_VERSION_ID >= 50400) { + $result = zlib_decode($result); + } else { + // work around issue with gzuncompress & co that do not work with all gzip checksums + $result = file_get_contents('compress.zlib://data:application/octet-stream;base64,'.base64_encode($result)); + } - if (!empty($data[$type . '-versions'])) { - $versionParser = new VersionParser(); - $constraint = $versionParser->parseConstraints($data[$type . '-versions']); - $composer = new Constraint('==', $versionParser->normalize(Composer::getVersion())); - if (!$constraint->matches($composer)) { - continue; + if ($result === false) { + throw new TransportException('Failed to decode zlib stream'); } } - - $io->writeError('<'.$type.'>'.ucfirst($type).' from '.$url.': '.$data[$type].''); - } - } - - public static function getOrigin($urlOrPath) - { - $hostPort = parse_url($urlOrPath, PHP_URL_HOST); - if (!$hostPort) { - return $urlOrPath; - } - if (parse_url($urlOrPath, PHP_URL_PORT)) { - $hostPort .= ':'.parse_url($urlOrPath, PHP_URL_PORT); } - return $hostPort; - } - - private function stripCredentialsFromUrl($url) - { - // GitHub repository rename result in redirect locations containing the access_token as GET parameter - // e.g. https://api.github.com/repositories/9999999999?access_token=github_token - return preg_replace('{([&?]access_token=)[^&]+}', '$1***', $url); + return $result; } } diff --git a/app/vendor/composer/composer/src/Composer/Util/Silencer.php b/app/vendor/composer/composer/src/Composer/Util/Silencer.php index dcb362b52..361ef41f6 100644 --- a/app/vendor/composer/composer/src/Composer/Util/Silencer.php +++ b/app/vendor/composer/composer/src/Composer/Util/Silencer.php @@ -44,6 +44,8 @@ public static function suppress($mask = null) /** * Restores a single state. + * + * @return void */ public static function restore() { diff --git a/app/vendor/composer/composer/src/Composer/Util/SpdxLicense.php b/app/vendor/composer/composer/src/Composer/Util/SpdxLicense.php deleted file mode 100644 index be4efdc54..000000000 --- a/app/vendor/composer/composer/src/Composer/Util/SpdxLicense.php +++ /dev/null @@ -1,24 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Util; - -use Composer\Spdx\SpdxLicenses; - -trigger_error('The ' . __NAMESPACE__ . '\SpdxLicense class is deprecated, use Composer\Spdx\SpdxLicenses instead.', E_USER_DEPRECATED); - -/** - * @deprecated use Composer\Spdx\SpdxLicenses instead - */ -class SpdxLicense extends SpdxLicenses -{ -} diff --git a/app/vendor/composer/composer/src/Composer/Util/StreamContextFactory.php b/app/vendor/composer/composer/src/Composer/Util/StreamContextFactory.php index da3e578bd..9cab3b2fe 100644 --- a/app/vendor/composer/composer/src/Composer/Util/StreamContextFactory.php +++ b/app/vendor/composer/composer/src/Composer/Util/StreamContextFactory.php @@ -13,6 +13,11 @@ namespace Composer\Util; use Composer\Composer; +use Composer\CaBundle\CaBundle; +use Composer\Downloader\TransportException; +use Composer\Repository\PlatformRepository; +use Composer\Util\Http\ProxyManager; +use Psr\Log\LoggerInterface; /** * Allows the creation of a basic context supporting http proxy @@ -25,9 +30,10 @@ final class StreamContextFactory /** * Creates a context supporting HTTP proxies * - * @param string $url URL the context is to be used for - * @param array $defaultOptions Options to merge with the default - * @param array $defaultParams Parameters to specify on the context + * @param string $url URL the context is to be used for + * @phpstan-param array{http?: array{follow_location?: int, max_redirects?: int, header?: string|array}} $defaultOptions + * @param mixed[] $defaultOptions Options to merge with the default + * @param mixed[] $defaultParams Parameters to specify on the context * @throws \RuntimeException if https proxy required and OpenSSL uninstalled * @return resource Default context */ @@ -39,118 +45,191 @@ public static function getContext($url, array $defaultOptions = array(), array $ 'max_redirects' => 20, )); - // Handle HTTP_PROXY/http_proxy on CLI only for security reasons - if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && (!empty($_SERVER['HTTP_PROXY']) || !empty($_SERVER['http_proxy']))) { - $proxy = parse_url(!empty($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']); - } + $options = array_replace_recursive($options, self::initOptions($url, $defaultOptions)); + unset($defaultOptions['http']['header']); + $options = array_replace_recursive($options, $defaultOptions); - // Prefer CGI_HTTP_PROXY if available - if (!empty($_SERVER['CGI_HTTP_PROXY'])) { - $proxy = parse_url($_SERVER['CGI_HTTP_PROXY']); + if (isset($options['http']['header'])) { + $options['http']['header'] = self::fixHttpHeaderField($options['http']['header']); } - // Override with HTTPS proxy if present and URL is https - if (preg_match('{^https://}i', $url) && (!empty($_SERVER['HTTPS_PROXY']) || !empty($_SERVER['https_proxy']))) { - $proxy = parse_url(!empty($_SERVER['https_proxy']) ? $_SERVER['https_proxy'] : $_SERVER['HTTPS_PROXY']); - } + return stream_context_create($options, $defaultParams); + } - // Remove proxy if URL matches no_proxy directive - if (!empty($_SERVER['NO_PROXY']) || !empty($_SERVER['no_proxy']) && parse_url($url, PHP_URL_HOST)) { - $pattern = new NoProxyPattern(!empty($_SERVER['no_proxy']) ? $_SERVER['no_proxy'] : $_SERVER['NO_PROXY']); - if ($pattern->test($url)) { - unset($proxy); - } + /** + * @param string $url + * @param mixed[] $options + * @param bool $forCurl When true, will not add proxy values as these are handled separately + * @phpstan-return array{http: array{header: string[], proxy?: string, request_fulluri: bool}, ssl: array} + * @return array formatted as a stream context array + */ + public static function initOptions($url, array $options, $forCurl = false) + { + // Make sure the headers are in an array form + if (!isset($options['http']['header'])) { + $options['http']['header'] = array(); + } + if (is_string($options['http']['header'])) { + $options['http']['header'] = explode("\r\n", $options['http']['header']); } - if (!empty($proxy)) { - $proxyURL = isset($proxy['scheme']) ? $proxy['scheme'] . '://' : ''; - $proxyURL .= isset($proxy['host']) ? $proxy['host'] : ''; - - if (isset($proxy['port'])) { - $proxyURL .= ":" . $proxy['port']; - } elseif ('http://' == substr($proxyURL, 0, 7)) { - $proxyURL .= ":80"; - } elseif ('https://' == substr($proxyURL, 0, 8)) { - $proxyURL .= ":443"; - } - - // http(s):// is not supported in proxy - $proxyURL = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxyURL); - - if (0 === strpos($proxyURL, 'ssl:') && !extension_loaded('openssl')) { - throw new \RuntimeException('You must enable the openssl extension to use a proxy over https'); - } - - $options['http']['proxy'] = $proxyURL; + // Add stream proxy options if there is a proxy + if (!$forCurl) { + $proxy = ProxyManager::getInstance()->getProxyForRequest($url); + if ($proxyOptions = $proxy->getContextOptions()) { + $isHttpsRequest = 0 === strpos($url, 'https://'); - // enabled request_fulluri unless it is explicitly disabled - switch (parse_url($url, PHP_URL_SCHEME)) { - case 'http': // default request_fulluri to true - $reqFullUriEnv = getenv('HTTP_PROXY_REQUEST_FULLURI'); - if ($reqFullUriEnv === false || $reqFullUriEnv === '' || (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv)) { - $options['http']['request_fulluri'] = true; + if ($proxy->isSecure()) { + if (!extension_loaded('openssl')) { + throw new TransportException('You must enable the openssl extension to use a secure proxy.'); } - break; - case 'https': // default request_fulluri to true - $reqFullUriEnv = getenv('HTTPS_PROXY_REQUEST_FULLURI'); - if ($reqFullUriEnv === false || $reqFullUriEnv === '' || (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv)) { - $options['http']['request_fulluri'] = true; + if ($isHttpsRequest) { + throw new TransportException('You must enable the curl extension to make https requests through a secure proxy.'); } - break; - } - - // add SNI opts for https URLs - if ('https' === parse_url($url, PHP_URL_SCHEME)) { - $options['ssl']['SNI_enabled'] = true; - if (PHP_VERSION_ID < 50600) { - $options['ssl']['SNI_server_name'] = parse_url($url, PHP_URL_HOST); - } - } - - // handle proxy auth if present - if (isset($proxy['user'])) { - $auth = rawurldecode($proxy['user']); - if (isset($proxy['pass'])) { - $auth .= ':' . rawurldecode($proxy['pass']); + } elseif ($isHttpsRequest && !extension_loaded('openssl')) { + throw new TransportException('You must enable the openssl extension to make https requests through a proxy.'); } - $auth = base64_encode($auth); - // Preserve headers if already set in default options - if (isset($defaultOptions['http']['header'])) { - if (is_string($defaultOptions['http']['header'])) { - $defaultOptions['http']['header'] = array($defaultOptions['http']['header']); - } - $defaultOptions['http']['header'][] = "Proxy-Authorization: Basic {$auth}"; - } else { - $options['http']['header'] = array("Proxy-Authorization: Basic {$auth}"); + // Header will be a Proxy-Authorization string or not set + if (isset($proxyOptions['http']['header'])) { + $options['http']['header'][] = $proxyOptions['http']['header']; + unset($proxyOptions['http']['header']); } + $options = array_replace_recursive($options, $proxyOptions); } } - $options = array_replace_recursive($options, $defaultOptions); - - if (isset($options['http']['header'])) { - $options['http']['header'] = self::fixHttpHeaderField($options['http']['header']); - } - if (defined('HHVM_VERSION')) { $phpVersion = 'HHVM ' . HHVM_VERSION; } else { $phpVersion = 'PHP ' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION; } + if ($forCurl) { + $curl = curl_version(); + $httpVersion = 'cURL '.$curl['version']; + } else { + $httpVersion = 'streams'; + } + if (!isset($options['http']['header']) || false === stripos(implode('', $options['http']['header']), 'user-agent')) { + $platformPhpVersion = PlatformRepository::getPlatformPhpVersion(); $options['http']['header'][] = sprintf( - 'User-Agent: Composer/%s (%s; %s; %s%s)', + 'User-Agent: Composer/%s (%s; %s; %s; %s%s%s)', Composer::getVersion(), function_exists('php_uname') ? php_uname('s') : 'Unknown', function_exists('php_uname') ? php_uname('r') : 'Unknown', $phpVersion, + $httpVersion, + $platformPhpVersion ? '; Platform-PHP '.$platformPhpVersion : '', getenv('CI') ? '; CI' : '' ); } - return stream_context_create($options, $defaultParams); + return $options; + } + + /** + * @param mixed[] $options + * + * @return mixed[] + */ + public static function getTlsDefaults(array $options, LoggerInterface $logger = null) + { + $ciphers = implode(':', array( + 'ECDHE-RSA-AES128-GCM-SHA256', + 'ECDHE-ECDSA-AES128-GCM-SHA256', + 'ECDHE-RSA-AES256-GCM-SHA384', + 'ECDHE-ECDSA-AES256-GCM-SHA384', + 'DHE-RSA-AES128-GCM-SHA256', + 'DHE-DSS-AES128-GCM-SHA256', + 'kEDH+AESGCM', + 'ECDHE-RSA-AES128-SHA256', + 'ECDHE-ECDSA-AES128-SHA256', + 'ECDHE-RSA-AES128-SHA', + 'ECDHE-ECDSA-AES128-SHA', + 'ECDHE-RSA-AES256-SHA384', + 'ECDHE-ECDSA-AES256-SHA384', + 'ECDHE-RSA-AES256-SHA', + 'ECDHE-ECDSA-AES256-SHA', + 'DHE-RSA-AES128-SHA256', + 'DHE-RSA-AES128-SHA', + 'DHE-DSS-AES128-SHA256', + 'DHE-RSA-AES256-SHA256', + 'DHE-DSS-AES256-SHA', + 'DHE-RSA-AES256-SHA', + 'AES128-GCM-SHA256', + 'AES256-GCM-SHA384', + 'AES128-SHA256', + 'AES256-SHA256', + 'AES128-SHA', + 'AES256-SHA', + 'AES', + 'CAMELLIA', + 'DES-CBC3-SHA', + '!aNULL', + '!eNULL', + '!EXPORT', + '!DES', + '!RC4', + '!MD5', + '!PSK', + '!aECDH', + '!EDH-DSS-DES-CBC3-SHA', + '!EDH-RSA-DES-CBC3-SHA', + '!KRB5-DES-CBC3-SHA', + )); + + /** + * CN_match and SNI_server_name are only known once a URL is passed. + * They will be set in the getOptionsForUrl() method which receives a URL. + * + * cafile or capath can be overridden by passing in those options to constructor. + */ + $defaults = array( + 'ssl' => array( + 'ciphers' => $ciphers, + 'verify_peer' => true, + 'verify_depth' => 7, + 'SNI_enabled' => true, + 'capture_peer_cert' => true, + ), + ); + + if (isset($options['ssl'])) { + $defaults['ssl'] = array_replace_recursive($defaults['ssl'], $options['ssl']); + } + + /** + * Attempt to find a local cafile or throw an exception if none pre-set + * The user may go download one if this occurs. + */ + if (!isset($defaults['ssl']['cafile']) && !isset($defaults['ssl']['capath'])) { + $result = CaBundle::getSystemCaRootBundlePath($logger); + + if (is_dir($result)) { + $defaults['ssl']['capath'] = $result; + } else { + $defaults['ssl']['cafile'] = $result; + } + } + + if (isset($defaults['ssl']['cafile']) && (!Filesystem::isReadable($defaults['ssl']['cafile']) || !CaBundle::validateCaFile($defaults['ssl']['cafile'], $logger))) { + throw new TransportException('The configured cafile was not valid or could not be read.'); + } + + if (isset($defaults['ssl']['capath']) && (!is_dir($defaults['ssl']['capath']) || !Filesystem::isReadable($defaults['ssl']['capath']))) { + throw new TransportException('The configured capath was not valid or could not be read.'); + } + + /** + * Disable TLS compression to prevent CRIME attacks where supported. + */ + if (PHP_VERSION_ID >= 50413) { + $defaults['ssl']['disable_compression'] = true; + } + + return $defaults; } /** @@ -160,8 +239,8 @@ function_exists('php_uname') ? php_uname('r') : 'Unknown', * This method fixes the array by moving the content-type header to the end * * @link https://bugs.php.net/bug.php?id=61548 - * @param string|array $header - * @return array + * @param string|string[] $header + * @return string[] */ private static function fixHttpHeaderField($header) { diff --git a/app/vendor/composer/composer/src/Composer/Util/Svn.php b/app/vendor/composer/composer/src/Composer/Util/Svn.php index 80c420feb..0bc489de6 100644 --- a/app/vendor/composer/composer/src/Composer/Util/Svn.php +++ b/app/vendor/composer/composer/src/Composer/Util/Svn.php @@ -24,7 +24,7 @@ class Svn const MAX_QTY_AUTH_TRIES = 5; /** - * @var array + * @var ?array{username: string, password: string} */ protected $credentials; @@ -82,11 +82,13 @@ public function __construct($url, IOInterface $io, Config $config, ProcessExecut $this->process = $process ?: new ProcessExecutor($io); } + /** + * @return void + */ public static function cleanEnv() { // clean up env for OSX, see https://github.com/composer/composer/issues/2146#issuecomment-35478940 - putenv("DYLD_LIBRARY_PATH"); - unset($_SERVER['DYLD_LIBRARY_PATH']); + Platform::clearEnv('DYLD_LIBRARY_PATH'); } /** @@ -128,6 +130,15 @@ public function executeLocal($command, $path, $cwd = null, $verbose = false) return $this->executeWithAuthRetry($command, $cwd, '', $path, $verbose); } + /** + * @param string $svnCommand + * @param string $cwd + * @param string $url + * @param string $path + * @param bool $verbose + * + * @return ?string + */ private function executeWithAuthRetry($svnCommand, $cwd, $url, $path, $verbose) { // Regenerate the command at each try, to use the newly user-provided credentials @@ -137,10 +148,10 @@ private function executeWithAuthRetry($svnCommand, $cwd, $url, $path, $verbose) $io = $this->io; $handler = function ($type, $buffer) use (&$output, $io, $verbose) { if ($type !== 'out') { - return; + return null; } - if ('Redirecting to URL ' === substr($buffer, 0, 19)) { - return; + if (strpos($buffer, 'Redirecting to URL ') === 0) { + return null; } $output .= $buffer; if ($verbose) { @@ -179,7 +190,8 @@ private function executeWithAuthRetry($svnCommand, $cwd, $url, $path, $verbose) } /** - * @param bool $cacheCredentials + * @param bool $cacheCredentials + * @return void */ public function setCacheCredentials($cacheCredentials) { @@ -207,7 +219,7 @@ protected function doAuthDance() $this->credentials['username'] = $this->io->ask("Username: "); $this->credentials['password'] = $this->io->askAndHideAnswer("Password: "); - $this->cacheCredentials = $this->io->askConfirmation("Should Subversion cache these credentials? (yes/no) ", true); + $this->cacheCredentials = $this->io->askConfirmation("Should Subversion cache these credentials? (yes/no) "); return $this; } @@ -304,7 +316,7 @@ protected function hasAuth() $this->createAuthFromUrl(); } - return $this->hasAuth; + return (bool) $this->hasAuth; } /** diff --git a/app/vendor/composer/composer/src/Composer/Util/SyncHelper.php b/app/vendor/composer/composer/src/Composer/Util/SyncHelper.php new file mode 100644 index 000000000..9e8c5d710 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Util/SyncHelper.php @@ -0,0 +1,70 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util; + +use Composer\Downloader\DownloaderInterface; +use Composer\Package\PackageInterface; +use React\Promise\PromiseInterface; + +class SyncHelper +{ + /** + * Helps you download + install a single package in a synchronous way + * + * This executes all the required steps and waits for promises to complete + * + * @param Loop $loop Loop instance which you can get from $composer->getLoop() + * @param DownloaderInterface $downloader Downloader instance you can get from $composer->getDownloadManager()->getDownloader('zip') for example + * @param string $path the installation path for the package + * @param PackageInterface $package the package to install + * @param PackageInterface|null $prevPackage the previous package if this is an update and not an initial installation + * + * @return void + */ + public static function downloadAndInstallPackageSync(Loop $loop, DownloaderInterface $downloader, $path, PackageInterface $package, PackageInterface $prevPackage = null) + { + $type = $prevPackage ? 'update' : 'install'; + + try { + self::await($loop, $downloader->download($package, $path, $prevPackage)); + + self::await($loop, $downloader->prepare($type, $package, $path, $prevPackage)); + + if ($type === 'update') { + self::await($loop, $downloader->update($package, $prevPackage, $path)); + } else { + self::await($loop, $downloader->install($package, $path)); + } + } catch (\Exception $e) { + self::await($loop, $downloader->cleanup($type, $package, $path, $prevPackage)); + throw $e; + } + + self::await($loop, $downloader->cleanup($type, $package, $path, $prevPackage)); + } + + /** + * Waits for a promise to resolve + * + * @param Loop $loop Loop instance which you can get from $composer->getLoop() + * @param PromiseInterface|null $promise + * + * @return void + */ + public static function await(Loop $loop, PromiseInterface $promise = null) + { + if ($promise) { + $loop->wait(array($promise)); + } + } +} diff --git a/app/vendor/composer/composer/src/Composer/Util/Tar.php b/app/vendor/composer/composer/src/Composer/Util/Tar.php new file mode 100644 index 000000000..cae69c906 --- /dev/null +++ b/app/vendor/composer/composer/src/Composer/Util/Tar.php @@ -0,0 +1,68 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util; + +/** + * @author Wissem Riahi + */ +class Tar +{ + /** + * @param string $pathToArchive + * + * @return string|null + */ + public static function getComposerJson($pathToArchive) + { + $phar = new \PharData($pathToArchive); + + if (!$phar->valid()) { + return null; + } + + return self::extractComposerJsonFromFolder($phar); + } + + /** + * @param \PharData $phar + * + * @throws \RuntimeException + * + * @return string + */ + private static function extractComposerJsonFromFolder(\PharData $phar) + { + if (isset($phar['composer.json'])) { + return $phar['composer.json']->getContent(); + } + + $topLevelPaths = array(); + foreach ($phar as $folderFile) { + $name = $folderFile->getBasename(); + + if ($folderFile->isDir()) { + $topLevelPaths[$name] = true; + if (\count($topLevelPaths) > 1) { + throw new \RuntimeException('Archive has more than one top level directories, and no composer.json was found on the top level, so it\'s an invalid archive. Top level paths found were: '.implode(',', array_keys($topLevelPaths))); + } + } + } + + $composerJsonPath = key($topLevelPaths).'/composer.json'; + if ($topLevelPaths && isset($phar[$composerJsonPath])) { + return $phar[$composerJsonPath]->getContent(); + } + + throw new \RuntimeException('No composer.json found either at the top level or within the topmost directory'); + } +} diff --git a/app/vendor/composer/composer/src/Composer/Util/TlsHelper.php b/app/vendor/composer/composer/src/Composer/Util/TlsHelper.php index a53212f2d..01166d7b3 100644 --- a/app/vendor/composer/composer/src/Composer/Util/TlsHelper.php +++ b/app/vendor/composer/composer/src/Composer/Util/TlsHelper.php @@ -57,7 +57,7 @@ public static function checkCertificateHost($certificate, $hostname, &$cn = null * * @param mixed $certificate X.509 certificate * - * @return array|null + * @return array{cn: string, san: string[]}|null */ public static function getCertificateNames($certificate) { @@ -98,7 +98,7 @@ public static function getCertificateNames($certificate) * By Kevin McArthur of StormTide Digital Studios Inc. * @KevinSMcArthur / https://github.com/StormTide * - * See http://tools.ietf.org/html/draft-ietf-websec-key-pinning-02 + * See https://tools.ietf.org/html/draft-ietf-websec-key-pinning-02 * * This method was adapted from Sslurp. * https://github.com/EvanDotPro/Sslurp @@ -130,6 +130,9 @@ public static function getCertificateNames($certificate) * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @param string $certificate + * @return string */ public static function getCertificateFingerprint($certificate) { @@ -162,7 +165,7 @@ public static function isOpensslParseSafe() * * @param string $certName CN/SAN * - * @return callable|void + * @return callable|null */ private static function certNameMatcher($certName) { @@ -180,14 +183,14 @@ private static function certNameMatcher($certName) if (3 > count($components)) { // Must have 3+ components - return; + return null; } $firstComponent = $components[0]; // Wildcard must be the last character. if ('*' !== $firstComponent[strlen($firstComponent) - 1]) { - return; + return null; } $wildcardRegex = preg_quote($certName); @@ -198,5 +201,7 @@ private static function certNameMatcher($certName) return 1 === preg_match($wildcardRegex, $hostname); }; } + + return null; } } diff --git a/app/vendor/composer/composer/src/Composer/Util/Url.php b/app/vendor/composer/composer/src/Composer/Util/Url.php index 4a5d5f90c..7a281c924 100644 --- a/app/vendor/composer/composer/src/Composer/Util/Url.php +++ b/app/vendor/composer/composer/src/Composer/Util/Url.php @@ -19,6 +19,12 @@ */ class Url { + /** + * @param Config $config + * @param string $url + * @param string $ref + * @return string the updated URL + */ public static function updateDistReference(Config $config, $url, $ref) { $host = parse_url($url, PHP_URL_HOST); @@ -52,4 +58,70 @@ public static function updateDistReference(Config $config, $url, $ref) return $url; } + + /** + * @param string $url + * @return string + */ + public static function getOrigin(Config $config, $url) + { + if (0 === strpos($url, 'file://')) { + return $url; + } + + $origin = (string) parse_url($url, PHP_URL_HOST); + if ($port = parse_url($url, PHP_URL_PORT)) { + $origin .= ':'.$port; + } + + if (strpos($origin, '.github.com') === (strlen($origin) - 11)) { + return 'github.com'; + } + + if ($origin === 'repo.packagist.org') { + return 'packagist.org'; + } + + if ($origin === '') { + $origin = $url; + } + + // Gitlab can be installed in a non-root context (i.e. gitlab.com/foo). When downloading archives the originUrl + // is the host without the path, so we look for the registered gitlab-domains matching the host here + if ( + is_array($config->get('gitlab-domains')) + && false === strpos($origin, '/') + && !in_array($origin, $config->get('gitlab-domains')) + ) { + foreach ($config->get('gitlab-domains') as $gitlabDomain) { + if (0 === strpos($gitlabDomain, $origin)) { + return $gitlabDomain; + } + } + } + + return $origin; + } + + /** + * @param string $url + * @return string + */ + public static function sanitize($url) + { + // GitHub repository rename result in redirect locations containing the access_token as GET parameter + // e.g. https://api.github.com/repositories/9999999999?access_token=github_token + $url = preg_replace('{([&?]access_token=)[^&]+}', '$1***', $url); + + $url = preg_replace_callback('{^(?P[a-z0-9]+://)?(?P[^:/\s@]+):(?P[^@\s/]+)@}i', function ($m) { + // if the username looks like a long (12char+) hex string, or a modern github token (e.g. ghp_xxx) we obfuscate that + if (preg_match('{^([a-f0-9]{12,}|gh[a-z]_[a-zA-Z0-9_]+)$}', $m['user'])) { + return $m['prefix'].'***:***@'; + } + + return $m['prefix'].$m['user'].':***@'; + }, $url); + + return $url; + } } diff --git a/app/vendor/composer/composer/src/Composer/Util/Zip.php b/app/vendor/composer/composer/src/Composer/Util/Zip.php index ab10d5bbf..7b1f6285c 100644 --- a/app/vendor/composer/composer/src/Composer/Util/Zip.php +++ b/app/vendor/composer/composer/src/Composer/Util/Zip.php @@ -42,11 +42,6 @@ public static function getComposerJson($pathToZip) } $foundFileIndex = self::locateFile($zip, 'composer.json'); - if (false === $foundFileIndex) { - $zip->close(); - - return null; - } $content = null; $configurationFileName = $zip->getNameIndex($foundFileIndex); @@ -64,44 +59,51 @@ public static function getComposerJson($pathToZip) /** * Find a file by name, returning the one that has the shortest path. * - * @param \ZipArchive $zip - * @param string $filename + * @param \ZipArchive $zip + * @param string $filename + * @throws \RuntimeException * - * @return bool|int + * @return int */ private static function locateFile(\ZipArchive $zip, $filename) { - $indexOfShortestMatch = false; - $lengthOfShortestMatch = -1; + // return root composer.json if it is there and is a file + if (false !== ($index = $zip->locateName($filename)) && $zip->getFromIndex($index) !== false) { + return $index; + } + $topLevelPaths = array(); for ($i = 0; $i < $zip->numFiles; $i++) { - $stat = $zip->statIndex($i); - if (strcmp(basename($stat['name']), $filename) === 0) { - $directoryName = dirname($stat['name']); - if ($directoryName === '.') { - //if composer.json is in root directory - //it has to be the one to use. - return $i; - } + $name = $zip->getNameIndex($i); + $dirname = dirname($name); - if (strpos($directoryName, '\\') !== false || - strpos($directoryName, '/') !== false) { - //composer.json files below first directory are rejected - continue; + // ignore OSX specific resource fork folder + if (strpos($name, '__MACOSX') !== false) { + continue; + } + + // handle archives with proper TOC + if ($dirname === '.') { + $topLevelPaths[$name] = true; + if (\count($topLevelPaths) > 1) { + throw new \RuntimeException('Archive has more than one top level directories, and no composer.json was found on the top level, so it\'s an invalid archive. Top level paths found were: '.implode(',', array_keys($topLevelPaths))); } + continue; + } - $length = strlen($stat['name']); - if ($indexOfShortestMatch === false || $length < $lengthOfShortestMatch) { - //Check it's not a directory. - $contents = $zip->getFromIndex($i); - if ($contents !== false) { - $indexOfShortestMatch = $i; - $lengthOfShortestMatch = $length; - } + // handle archives which do not have a TOC record for the directory itself + if (false === strpos($dirname, '\\') && false === strpos($dirname, '/')) { + $topLevelPaths[$dirname.'/'] = true; + if (\count($topLevelPaths) > 1) { + throw new \RuntimeException('Archive has more than one top level directories, and no composer.json was found on the top level, so it\'s an invalid archive. Top level paths found were: '.implode(',', array_keys($topLevelPaths))); } } } - return $indexOfShortestMatch; + if ($topLevelPaths && false !== ($index = $zip->locateName(key($topLevelPaths).$filename)) && $zip->getFromIndex($index) !== false) { + return $index; + } + + throw new \RuntimeException('No composer.json found either at the top level or within the topmost directory'); } } diff --git a/app/vendor/composer/composer/src/Composer/XdebugHandler.php b/app/vendor/composer/composer/src/Composer/XdebugHandler.php deleted file mode 100644 index eb94e93f4..000000000 --- a/app/vendor/composer/composer/src/Composer/XdebugHandler.php +++ /dev/null @@ -1,31 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer; - -use Symfony\Component\Console\Output\OutputInterface; - -trigger_error('The ' . __NAMESPACE__ . '\XdebugHandler class is deprecated, use Composer\XdebugHandler\XdebugHandler instead,', E_USER_DEPRECATED); - -/** - * @deprecated use Composer\XdebugHandler\XdebugHandler instead - */ -class XdebugHandler extends XdebugHandler\XdebugHandler -{ - const ENV_ALLOW = 'COMPOSER_ALLOW_XDEBUG'; - const ENV_VERSION = 'COMPOSER_XDEBUG_VERSION'; - - public function __construct(OutputInterface $output) - { - parent::__construct('composer', '--ansi'); - } -} diff --git a/app/vendor/composer/composer/src/bootstrap.php b/app/vendor/composer/composer/src/bootstrap.php index a3832ce1d..9b33ec1b0 100644 --- a/app/vendor/composer/composer/src/bootstrap.php +++ b/app/vendor/composer/composer/src/bootstrap.php @@ -10,9 +10,13 @@ * file that was distributed with this source code. */ +/** + * @param string $file + * @return ?\Composer\Autoload\ClassLoader + */ function includeIfExists($file) { - return file_exists($file) ? include $file : false; + return file_exists($file) ? include $file : null; } if ((!$loader = includeIfExists(__DIR__.'/../vendor/autoload.php')) && (!$loader = includeIfExists(__DIR__.'/../../../autoload.php'))) { diff --git a/app/vendor/composer/installed.json b/app/vendor/composer/installed.json index c3dab8d7c..aab924cf3 100644 --- a/app/vendor/composer/installed.json +++ b/app/vendor/composer/installed.json @@ -65,80 +65,79 @@ "install-path": "../adodb/adodb-php" }, { - "name": "aura/intl", - "version": "3.0.0", - "version_normalized": "3.0.0.0", + "name": "brick/varexporter", + "version": "0.3.5", + "version_normalized": "0.3.5.0", "source": { "type": "git", - "url": "https://github.com/auraphp/Aura.Intl.git", - "reference": "7fce228980b19bf4dee2d7bbd6202a69b0dde926" + "url": "https://github.com/brick/varexporter.git", + "reference": "05241f28dfcba2b51b11e2d750e296316ebbe518" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/auraphp/Aura.Intl/zipball/7fce228980b19bf4dee2d7bbd6202a69b0dde926", - "reference": "7fce228980b19bf4dee2d7bbd6202a69b0dde926", + "url": "https://api.github.com/repos/brick/varexporter/zipball/05241f28dfcba2b51b11e2d750e296316ebbe518", + "reference": "05241f28dfcba2b51b11e2d750e296316ebbe518", "shasum": "" }, "require": { - "php": "^5.6|^7.0" + "nikic/php-parser": "^4.0", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^8.5 || ^9.0", + "vimeo/psalm": "4.4.1" }, - "time": "2017-01-20T05:00:11+00:00", + "time": "2021-02-10T13:53:07+00:00", "type": "library", "installation-source": "dist", "autoload": { "psr-4": { - "Aura\\Intl\\": "src/" + "Brick\\VarExporter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Aura.Intl Contributors", - "homepage": "https://github.com/auraphp/Aura.Intl/contributors" - } - ], - "description": "The Aura Intl package provides internationalization tools, specifically message translation.", - "homepage": "https://github.com/auraphp/Aura.Intl", + "description": "A powerful alternative to var_export(), which can export closures and objects without __set_state()", "keywords": [ - "g11n", - "globalization", - "i18n", - "internationalization", - "intl", - "l10n", - "localization" + "var_export" ], - "install-path": "../aura/intl" + "support": { + "issues": "https://github.com/brick/varexporter/issues", + "source": "https://github.com/brick/varexporter/tree/0.3.5" + }, + "install-path": "../brick/varexporter" }, { "name": "cakephp/bake", - "version": "2.2.0", - "version_normalized": "2.2.0.0", + "version": "2.5.2", + "version_normalized": "2.5.2.0", "source": { "type": "git", "url": "https://github.com/cakephp/bake.git", - "reference": "f1c297c4e903a15188389011b93ce46119849d01" + "reference": "bfb856afcfbc70c5cf5341669c3036a45ca15d94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/bake/zipball/f1c297c4e903a15188389011b93ce46119849d01", - "reference": "f1c297c4e903a15188389011b93ce46119849d01", + "url": "https://api.github.com/repos/cakephp/bake/zipball/bfb856afcfbc70c5cf5341669c3036a45ca15d94", + "reference": "bfb856afcfbc70c5cf5341669c3036a45ca15d94", "shasum": "" }, "require": { - "cakephp/cakephp": "^4.0", - "cakephp/twig-view": "^1.0", + "brick/varexporter": "^0.3.5", + "cakephp/cakephp": "^4.1", + "cakephp/twig-view": "^1.0.2", "php": ">=7.2" }, "require-dev": { "cakephp/cakephp-codesniffer": "^4.0", "cakephp/debug_kit": "^4.1", + "cakephp/plugin-installer": "^1.3", "phpunit/phpunit": "~8.5.0" }, - "time": "2020-10-23T01:17:19+00:00", + "time": "2021-07-26T14:56:18+00:00", "type": "cakephp-plugin", "installation-source": "dist", "autoload": { @@ -172,21 +171,20 @@ }, { "name": "cakephp/cakephp", - "version": "4.0.10", - "version_normalized": "4.0.10.0", + "version": "4.2.10", + "version_normalized": "4.2.10.0", "source": { "type": "git", "url": "https://github.com/cakephp/cakephp.git", - "reference": "5c6d72bc9bb1be6eec1b00b3930f9e5054ee6d76" + "reference": "ea47e927083e2f4bfaed0c404756d9852b617cad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/cakephp/zipball/5c6d72bc9bb1be6eec1b00b3930f9e5054ee6d76", - "reference": "5c6d72bc9bb1be6eec1b00b3930f9e5054ee6d76", + "url": "https://api.github.com/repos/cakephp/cakephp/zipball/ea47e927083e2f4bfaed0c404756d9852b617cad", + "reference": "ea47e927083e2f4bfaed0c404756d9852b617cad", "shasum": "" }, "require": { - "aura/intl": "^3.0.0", "cakephp/chronos": "^2.0", "composer/ca-bundle": "^1.2", "ext-intl": "*", @@ -194,6 +192,7 @@ "ext-mbstring": "*", "laminas/laminas-diactoros": "^2.2.2", "laminas/laminas-httphandlerrunner": "^1.1", + "league/container": "^3.2", "php": ">=7.2.0", "psr/http-client": "^1.0", "psr/http-server-handler": "^1.0", @@ -222,7 +221,7 @@ "cakephp/cakephp-codesniffer": "^4.0", "mikey179/vfsstream": "^1.6", "paragonie/csp-builder": "^2.3", - "phpunit/phpunit": "~8.5.0" + "phpunit/phpunit": "^8.5 || ^9.3" }, "suggest": { "ext-curl": "To enable more efficient network calls in Http\\Client.", @@ -230,7 +229,7 @@ "lib-ICU": "The intl PHP library, to use Text::transliterate() or Text::slug()", "paragonie/csp-builder": "CSP builder, to use the CSP Middleware" }, - "time": "2020-12-08T03:04:12+00:00", + "time": "2021-10-14T01:50:57+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -241,6 +240,7 @@ "src/Core/functions.php", "src/Collection/functions.php", "src/I18n/functions.php", + "src/Routing/functions.php", "src/Utility/bootstrap.php" ] }, @@ -277,27 +277,28 @@ }, { "name": "cakephp/cakephp-codesniffer", - "version": "3.3.0", - "version_normalized": "3.3.0.0", + "version": "4.2.4", + "version_normalized": "4.2.4.0", "source": { "type": "git", "url": "https://github.com/cakephp/cakephp-codesniffer.git", - "reference": "7998a191e787fd5b68cb635d7050cb0d7b55e1a1" + "reference": "c5bb1faeebf09cd4a3604bdb0c84f7bc92dc5475" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/cakephp-codesniffer/zipball/7998a191e787fd5b68cb635d7050cb0d7b55e1a1", - "reference": "7998a191e787fd5b68cb635d7050cb0d7b55e1a1", + "url": "https://api.github.com/repos/cakephp/cakephp-codesniffer/zipball/c5bb1faeebf09cd4a3604bdb0c84f7bc92dc5475", + "reference": "c5bb1faeebf09cd4a3604bdb0c84f7bc92dc5475", "shasum": "" }, "require": { - "php": ">=5.6", - "squizlabs/php_codesniffer": "^3.0.0" + "php": ">=7.2.0", + "slevomat/coding-standard": "^6.3.6", + "squizlabs/php_codesniffer": "~3.5.5" }, "require-dev": { - "phpunit/phpunit": "<6.0" + "phpunit/phpunit": "^7.1" }, - "time": "2019-12-07T03:02:34+00:00", + "time": "2020-12-03T20:39:38+00:00", "type": "phpcodesniffer-standard", "installation-source": "dist", "autoload": { @@ -393,35 +394,35 @@ }, { "name": "cakephp/debug_kit", - "version": "4.2.0", - "version_normalized": "4.2.0.0", + "version": "4.4.4", + "version_normalized": "4.4.4.0", "source": { "type": "git", "url": "https://github.com/cakephp/debug_kit.git", - "reference": "940a0214947e85bbaa0724acfda852f64831f04f" + "reference": "10d7d9ba36945844211f1d8763e59618917e1784" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/debug_kit/zipball/940a0214947e85bbaa0724acfda852f64831f04f", - "reference": "940a0214947e85bbaa0724acfda852f64831f04f", + "url": "https://api.github.com/repos/cakephp/debug_kit/zipball/10d7d9ba36945844211f1d8763e59618917e1784", + "reference": "10d7d9ba36945844211f1d8763e59618917e1784", "shasum": "" }, "require": { - "cakephp/cakephp": "^4.0", + "cakephp/cakephp": "^4.2.0", "cakephp/chronos": "^2.0", - "composer/composer": "^1.3", + "composer/composer": "^1.3 | ^2.0", "jdorn/sql-formatter": "^1.2", "php": ">=7.2" }, "require-dev": { "cakephp/authorization": "^2.0", "cakephp/cakephp-codesniffer": "^4.0", - "phpunit/phpunit": "^8.0" + "phpunit/phpunit": "~8.5.0 | ^9.3" }, "suggest": { "ext-pdo_sqlite": "DebugKit needs to store panel data in a database. SQLite is simple and easy to use." }, - "time": "2020-06-10T01:37:18+00:00", + "time": "2021-09-12T20:06:14+00:00", "type": "cakephp-plugin", "installation-source": "dist", "autoload": { @@ -577,17 +578,17 @@ }, { "name": "cakephp/twig-view", - "version": "1.2.0", - "version_normalized": "1.2.0.0", + "version": "1.3.0", + "version_normalized": "1.3.0.0", "source": { "type": "git", "url": "https://github.com/cakephp/twig-view.git", - "reference": "668dd6aee43dd616b1e83cb9ba166f094c10fbce" + "reference": "14df50360b809a171d0688020fbdfe513763f89b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/twig-view/zipball/668dd6aee43dd616b1e83cb9ba166f094c10fbce", - "reference": "668dd6aee43dd616b1e83cb9ba166f094c10fbce", + "url": "https://api.github.com/repos/cakephp/twig-view/zipball/14df50360b809a171d0688020fbdfe513763f89b", + "reference": "14df50360b809a171d0688020fbdfe513763f89b", "shasum": "" }, "require": { @@ -608,7 +609,7 @@ "mikey179/vfsstream": "^1.6", "phpunit/phpunit": "^8.5 || ^9.3" }, - "time": "2020-12-13T19:57:31+00:00", + "time": "2021-09-17T14:07:52+00:00", "type": "cakephp-plugin", "installation-source": "dist", "autoload": { @@ -643,17 +644,17 @@ }, { "name": "composer/ca-bundle", - "version": "1.2.10", - "version_normalized": "1.2.10.0", + "version": "1.2.11", + "version_normalized": "1.2.11.0", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "9fdb22c2e97a614657716178093cd1da90a64aa8" + "reference": "0b072d51c5a9c6f3412f7ea3ab043d6603cb2582" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/9fdb22c2e97a614657716178093cd1da90a64aa8", - "reference": "9fdb22c2e97a614657716178093cd1da90a64aa8", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/0b072d51c5a9c6f3412f7ea3ab043d6603cb2582", + "reference": "0b072d51c5a9c6f3412f7ea3ab043d6603cb2582", "shasum": "" }, "require": { @@ -665,9 +666,9 @@ "phpstan/phpstan": "^0.12.55", "psr/log": "^1.0", "symfony/phpunit-bridge": "^4.2 || ^5", - "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" + "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" }, - "time": "2021-06-07T13:58:28+00:00", + "time": "2021-09-25T20:32:43+00:00", "type": "library", "extra": { "branch-alias": { @@ -702,7 +703,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.2.10" + "source": "https://github.com/composer/ca-bundle/tree/1.2.11" }, "funding": [ { @@ -722,54 +723,53 @@ }, { "name": "composer/composer", - "version": "1.10.22", - "version_normalized": "1.10.22.0", + "version": "2.1.9", + "version_normalized": "2.1.9.0", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "28c9dfbe2351635961f670773e8d7b17bc5eda25" + "reference": "e558c88f28d102d497adec4852802c0dc14c7077" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/28c9dfbe2351635961f670773e8d7b17bc5eda25", - "reference": "28c9dfbe2351635961f670773e8d7b17bc5eda25", + "url": "https://api.github.com/repos/composer/composer/zipball/e558c88f28d102d497adec4852802c0dc14c7077", + "reference": "e558c88f28d102d497adec4852802c0dc14c7077", "shasum": "" }, "require": { "composer/ca-bundle": "^1.0", - "composer/semver": "^1.0", + "composer/metadata-minifier": "^1.0", + "composer/semver": "^3.0", "composer/spdx-licenses": "^1.2", - "composer/xdebug-handler": "^1.1", - "justinrainbow/json-schema": "^5.2.10", + "composer/xdebug-handler": "^2.0", + "justinrainbow/json-schema": "^5.2.11", "php": "^5.3.2 || ^7.0 || ^8.0", "psr/log": "^1.0", + "react/promise": "^1.2 || ^2.7", "seld/jsonlint": "^1.4", "seld/phar-utils": "^1.0", - "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/finder": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0" - }, - "conflict": { - "symfony/console": "2.8.38" + "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/filesystem": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0" }, "require-dev": { "phpspec/prophecy": "^1.10", - "symfony/phpunit-bridge": "^4.2" + "symfony/phpunit-bridge": "^4.2 || ^5.0 || ^6.0" }, "suggest": { "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", "ext-zip": "Enabling the zip extension allows you to unzip archives", "ext-zlib": "Allow gzip compression of HTTP requests" }, - "time": "2021-04-27T11:10:45+00:00", + "time": "2021-10-05T07:47:38+00:00", "bin": [ "bin/composer" ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10-dev" + "dev-master": "2.1-dev" } }, "installation-source": "dist", @@ -786,12 +786,12 @@ { "name": "Nils Adermann", "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" + "homepage": "https://www.naderman.de" }, { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "homepage": "https://seld.be" } ], "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.", @@ -802,9 +802,9 @@ "package" ], "support": { - "irc": "irc://irc.freenode.org/composer", + "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/composer/issues", - "source": "https://github.com/composer/composer/tree/1.10.22" + "source": "https://github.com/composer/composer/tree/2.1.9" }, "funding": [ { @@ -822,32 +822,181 @@ ], "install-path": "./composer" }, + { + "name": "composer/metadata-minifier", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/composer/metadata-minifier.git", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/metadata-minifier/zipball/c549d23829536f0d0e984aaabbf02af91f443207", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "composer/composer": "^2", + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "time": "2021-04-07T13:37:33+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Composer\\MetadataMinifier\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Small utility library that handles metadata minification and expansion.", + "keywords": [ + "composer", + "compression" + ], + "support": { + "issues": "https://github.com/composer/metadata-minifier/issues", + "source": "https://github.com/composer/metadata-minifier/tree/1.0.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "install-path": "./metadata-minifier" + }, + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.4", + "version_normalized": "1.11.99.4", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "b174585d1fe49ceed21928a945138948cb394600" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600", + "reference": "b174585d1fe49ceed21928a945138948cb394600", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "time": "2021-09-13T08:41:34+00:00", + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "install-path": "./package-versions-deprecated" + }, { "name": "composer/semver", - "version": "1.7.2", - "version_normalized": "1.7.2.0", + "version": "3.2.5", + "version_normalized": "3.2.5.0", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "647490bbcaf7fc4891c58f47b825eb99d19c377a" + "reference": "31f3ea725711245195f62e54ffa402d8ef2fdba9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/647490bbcaf7fc4891c58f47b825eb99d19c377a", - "reference": "647490bbcaf7fc4891c58f47b825eb99d19c377a", + "url": "https://api.github.com/repos/composer/semver/zipball/31f3ea725711245195f62e54ffa402d8ef2fdba9", + "reference": "31f3ea725711245195f62e54ffa402d8ef2fdba9", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5" + "phpstan/phpstan": "^0.12.54", + "symfony/phpunit-bridge": "^4.2 || ^5" }, - "time": "2020-12-03T15:47:16+00:00", + "time": "2021-05-24T12:41:47+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "3.x-dev" } }, "installation-source": "dist", @@ -887,7 +1036,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/1.7.2" + "source": "https://github.com/composer/semver/tree/3.2.5" }, "funding": [ { @@ -989,28 +1138,28 @@ }, { "name": "composer/xdebug-handler", - "version": "1.4.6", - "version_normalized": "1.4.6.0", + "version": "2.0.2", + "version_normalized": "2.0.2.0", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "f27e06cd9675801df441b3656569b328e04aa37c" + "reference": "84674dd3a7575ba617f5a76d7e9e29a7d3891339" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f27e06cd9675801df441b3656569b328e04aa37c", - "reference": "f27e06cd9675801df441b3656569b328e04aa37c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/84674dd3a7575ba617f5a76d7e9e29a7d3891339", + "reference": "84674dd3a7575ba617f5a76d7e9e29a7d3891339", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1.0" + "psr/log": "^1 || ^2 || ^3" }, "require-dev": { "phpstan/phpstan": "^0.12.55", "symfony/phpunit-bridge": "^4.2 || ^5" }, - "time": "2021-03-25T17:01:18+00:00", + "time": "2021-07-31T17:03:58+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -1036,7 +1185,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/1.4.6" + "source": "https://github.com/composer/xdebug-handler/tree/2.0.2" }, "funding": [ { @@ -1054,6 +1203,79 @@ ], "install-path": "./xdebug-handler" }, + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v0.7.1", + "version_normalized": "0.7.1.0", + "source": { + "type": "git", + "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", + "reference": "fe390591e0241955f22eb9ba327d137e501c771c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/fe390591e0241955f22eb9ba327d137e501c771c", + "reference": "fe390591e0241955f22eb9ba327d137e501c771c", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.0 || ^3.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "phpcompatibility/php-compatibility": "^9.0", + "sensiolabs/security-checker": "^4.1.0" + }, + "time": "2020-12-07T18:04:37+00:00", + "type": "composer-plugin", + "extra": { + "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "support": { + "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + }, + "install-path": "../dealerdirect/phpcodesniffer-composer-installer" + }, { "name": "doctrine/cache", "version": "2.1.1", @@ -1158,41 +1380,42 @@ }, { "name": "doctrine/dbal", - "version": "2.13.3", - "version_normalized": "2.13.3.0", + "version": "3.1.3", + "version_normalized": "3.1.3.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "0d7adf4cadfee6f70850e5b163e6cdd706417838" + "reference": "96b0053775a544b4a6ab47654dac0621be8b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/0d7adf4cadfee6f70850e5b163e6cdd706417838", - "reference": "0d7adf4cadfee6f70850e5b163e6cdd706417838", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/96b0053775a544b4a6ab47654dac0621be8b4cf8", + "reference": "96b0053775a544b4a6ab47654dac0621be8b4cf8", "shasum": "" }, "require": { + "composer/package-versions-deprecated": "^1.11.99", "doctrine/cache": "^1.0|^2.0", "doctrine/deprecations": "^0.5.3", "doctrine/event-manager": "^1.0", - "ext-pdo": "*", - "php": "^7.1 || ^8" + "php": "^7.3 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "9.0.0", "jetbrains/phpstorm-stubs": "2021.1", - "phpstan/phpstan": "0.12.96", - "phpunit/phpunit": "^7.5.20|^8.5|9.5.5", + "phpstan/phpstan": "0.12.99", + "phpstan/phpstan-strict-rules": "^0.12.11", + "phpunit/phpunit": "9.5.10", "psalm/plugin-phpunit": "0.16.1", "squizlabs/php_codesniffer": "3.6.0", - "symfony/cache": "^4.4", - "symfony/console": "^2.0.5|^3.0|^4.0|^5.0", + "symfony/cache": "^5.2|^6.0", + "symfony/console": "^2.0.5|^3.0|^4.0|^5.0|^6.0", "vimeo/psalm": "4.10.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." }, - "time": "2021-09-12T19:11:48+00:00", + "time": "2021-10-02T16:15:05+00:00", "bin": [ "bin/doctrine-dbal" ], @@ -1200,7 +1423,7 @@ "installation-source": "dist", "autoload": { "psr-4": { - "Doctrine\\DBAL\\": "lib/Doctrine/DBAL" + "Doctrine\\DBAL\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1243,14 +1466,13 @@ "queryobject", "sasql", "sql", - "sqlanywhere", "sqlite", "sqlserver", "sqlsrv" ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/2.13.3" + "source": "https://github.com/doctrine/dbal/tree/3.1.3" }, "funding": [ { @@ -1605,28 +1827,29 @@ }, { "name": "josegonzalez/dotenv", - "version": "2.1.0", - "version_normalized": "2.1.0.0", + "version": "3.2.0", + "version_normalized": "3.2.0.0", "source": { "type": "git", "url": "https://github.com/josegonzalez/php-dotenv.git", - "reference": "ff3461f2960737f54054dff4fef3482a2bb9682b" + "reference": "f19174d9d7213a6c20e8e5e268aa7dd042d821ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/josegonzalez/php-dotenv/zipball/ff3461f2960737f54054dff4fef3482a2bb9682b", - "reference": "ff3461f2960737f54054dff4fef3482a2bb9682b", + "url": "https://api.github.com/repos/josegonzalez/php-dotenv/zipball/f19174d9d7213a6c20e8e5e268aa7dd042d821ca", + "reference": "f19174d9d7213a6c20e8e5e268aa7dd042d821ca", "shasum": "" }, "require": { "m1/env": "2.*", - "php": ">=5.3.0" + "php": ">=5.5.0" }, "require-dev": { + "php-mock/php-mock-phpunit": "^1.1", "satooshi/php-coveralls": "1.*", "squizlabs/php_codesniffer": "2.*" }, - "time": "2017-01-03T01:04:05+00:00", + "time": "2017-09-19T15:49:58+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -1656,6 +1879,10 @@ "dotenv", "php" ], + "support": { + "issues": "https://github.com/josegonzalez/php-dotenv/issues", + "source": "https://github.com/josegonzalez/php-dotenv/tree/master" + }, "install-path": "../josegonzalez/dotenv" }, { @@ -1733,17 +1960,17 @@ }, { "name": "laminas/laminas-diactoros", - "version": "2.7.0", - "version_normalized": "2.7.0.0", + "version": "2.8.0", + "version_normalized": "2.8.0.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-diactoros.git", - "reference": "8b5792b7c81465efb14780c2d4787f158bd96183" + "reference": "0c26ef1d95b6d7e6e3943a243ba3dc0797227199" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/8b5792b7c81465efb14780c2d4787f158bd96183", - "reference": "8b5792b7c81465efb14780c2d4787f158bd96183", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/0c26ef1d95b6d7e6e3943a243ba3dc0797227199", + "reference": "0c26ef1d95b6d7e6e3943a243ba3dc0797227199", "shasum": "" }, "require": { @@ -1772,7 +1999,7 @@ "psalm/plugin-phpunit": "^0.14.0", "vimeo/psalm": "^4.3" }, - "time": "2021-09-15T08:41:12+00:00", + "time": "2021-09-22T03:54:36+00:00", "type": "library", "extra": { "laminas": { @@ -1835,22 +2062,22 @@ }, { "name": "laminas/laminas-httphandlerrunner", - "version": "1.4.0", - "version_normalized": "1.4.0.0", + "version": "1.5.0", + "version_normalized": "1.5.0.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-httphandlerrunner.git", - "reference": "6a2dd33e4166469ade07ad1283b45924383b224b" + "reference": "5f94e55d93f756e8ad07b9049aeb3d6d84582d0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/6a2dd33e4166469ade07ad1283b45924383b224b", - "reference": "6a2dd33e4166469ade07ad1283b45924383b224b", + "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/5f94e55d93f756e8ad07b9049aeb3d6d84582d0e", + "reference": "5f94e55d93f756e8ad07b9049aeb3d6d84582d0e", "shasum": "" }, "require": { "laminas/laminas-zendframework-bridge": "^1.0", - "php": "^7.3 || ~8.0.0", + "php": "^7.3 || ~8.0.0 || ~8.1.0", "psr/http-message": "^1.0", "psr/http-message-implementation": "^1.0", "psr/http-server-handler": "^1.0" @@ -1860,12 +2087,12 @@ }, "require-dev": { "laminas/laminas-coding-standard": "~1.0.0", - "laminas/laminas-diactoros": "^2.1.1", - "phpunit/phpunit": "^9.3", - "psalm/plugin-phpunit": "^0.15.1", - "vimeo/psalm": "^4.6" + "laminas/laminas-diactoros": "^2.8.0", + "phpunit/phpunit": "^9.5.9", + "psalm/plugin-phpunit": "^0.16.1", + "vimeo/psalm": "^4.10.0" }, - "time": "2021-04-08T13:52:56+00:00", + "time": "2021-09-22T09:17:54+00:00", "type": "library", "extra": { "laminas": { @@ -1972,6 +2199,88 @@ ], "install-path": "../laminas/laminas-zendframework-bridge" }, + { + "name": "league/container", + "version": "3.4.1", + "version_normalized": "3.4.1.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/container.git", + "reference": "84ecbc2dbecc31bd23faf759a0e329ee49abddbd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/container/zipball/84ecbc2dbecc31bd23faf759a0e329ee49abddbd", + "reference": "84ecbc2dbecc31bd23faf759a0e329ee49abddbd", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/container": "^1.0.0" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "replace": { + "orno/di": "~2.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0 || ^7.0", + "roave/security-advisories": "dev-latest", + "scrutinizer/ocular": "^1.8", + "squizlabs/php_codesniffer": "^3.5" + }, + "time": "2021-07-09T08:23:52+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "League\\Container\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Phil Bennett", + "email": "philipobenito@gmail.com", + "homepage": "http://www.philipobenito.com", + "role": "Developer" + } + ], + "description": "A fast and intuitive dependency injection container.", + "homepage": "https://github.com/thephpleague/container", + "keywords": [ + "container", + "dependency", + "di", + "injection", + "league", + "provider", + "service" + ], + "support": { + "issues": "https://github.com/thephpleague/container/issues", + "source": "https://github.com/thephpleague/container/tree/3.4.1" + }, + "funding": [ + { + "url": "https://github.com/philipobenito", + "type": "github" + } + ], + "install-path": "../league/container" + }, { "name": "m1/env", "version": "2.2.0", @@ -2165,17 +2474,17 @@ }, { "name": "nikic/php-parser", - "version": "v4.12.0", - "version_normalized": "4.12.0.0", + "version": "v4.13.0", + "version_normalized": "4.13.0.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6608f01670c3cc5079e18c1dab1104e002579143" + "reference": "50953a2691a922aa1769461637869a0a2faa3f53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6608f01670c3cc5079e18c1dab1104e002579143", - "reference": "6608f01670c3cc5079e18c1dab1104e002579143", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53", + "reference": "50953a2691a922aa1769461637869a0a2faa3f53", "shasum": "" }, "require": { @@ -2186,7 +2495,7 @@ "ircmaxell/php-yacc": "^0.0.7", "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, - "time": "2021-07-21T10:44:31+00:00", + "time": "2021-09-20T12:20:58+00:00", "bin": [ "bin/php-parse" ], @@ -2218,7 +2527,7 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.12.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0" }, "install-path": "../nikic/php-parser" }, @@ -2456,17 +2765,17 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.5.0", - "version_normalized": "1.5.0.0", + "version": "1.5.1", + "version_normalized": "1.5.1.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f" + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30f38bffc6f24293dadd1823936372dfa9e86e2f", - "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae", + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae", "shasum": "" }, "require": { @@ -2477,7 +2786,7 @@ "ext-tokenizer": "*", "psalm/phar": "^4.8" }, - "time": "2021-09-17T15:28:14+00:00", + "time": "2021-10-02T14:08:47+00:00", "type": "library", "extra": { "branch-alias": { @@ -2503,7 +2812,7 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1" }, "install-path": "../phpdocumentor/type-resolver" }, @@ -2578,18 +2887,74 @@ "install-path": "../phpspec/prophecy" }, { - "name": "phpstan/phpstan", - "version": "0.12.99", - "version_normalized": "0.12.99.0", + "name": "phpstan/phpdoc-parser", + "version": "0.4.9", + "version_normalized": "0.4.9.0", "source": { "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7" + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "98a088b17966bdf6ee25c8a4b634df313d8aa531" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7", - "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/98a088b17966bdf6ee25c8a4b634df313d8aa531", + "reference": "98a088b17966bdf6ee25c8a4b634df313d8aa531", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "consistence/coding-standard": "^3.5", + "ergebnis/composer-normalize": "^2.0.2", + "jakub-onderka/php-parallel-lint": "^0.9.2", + "phing/phing": "^2.16.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.26", + "phpstan/phpstan-strict-rules": "^0.12", + "phpunit/phpunit": "^6.3", + "slevomat/coding-standard": "^4.7.2", + "symfony/process": "^4.0" + }, + "time": "2020-08-03T20:32:43+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/master" + }, + "install-path": "../phpstan/phpdoc-parser" + }, + { + "name": "phpstan/phpstan", + "version": "0.12.99", + "version_normalized": "0.12.99.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7", + "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7", "shasum": "" }, "require": { @@ -2646,42 +3011,46 @@ }, { "name": "phpunit/php-code-coverage", - "version": "7.0.15", - "version_normalized": "7.0.15.0", + "version": "9.2.7", + "version_normalized": "9.2.7.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "819f92bba8b001d4363065928088de22f25a3a48" + "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/819f92bba8b001d4363065928088de22f25a3a48", - "reference": "819f92bba8b001d4363065928088de22f25a3a48", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218", + "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-xmlwriter": "*", - "php": ">=7.2", - "phpunit/php-file-iterator": "^2.0.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.1.3 || ^4.0", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^4.2.2", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1.3" + "nikic/php-parser": "^4.12.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^8.2.2" + "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-xdebug": "^2.7.2" + "ext-pcov": "*", + "ext-xdebug": "*" }, - "time": "2021-07-26T12:20:09+00:00", + "time": "2021-09-17T05:39:03+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "7.0-dev" + "dev-master": "9.2-dev" } }, "installation-source": "dist", @@ -2710,7 +3079,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7" }, "funding": [ { @@ -2722,30 +3091,30 @@ }, { "name": "phpunit/php-file-iterator", - "version": "2.0.4", - "version_normalized": "2.0.4.0", + "version": "3.0.5", + "version_normalized": "3.0.5.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "28af674ff175d0768a5a978e6de83f697d4a7f05" + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/28af674ff175d0768a5a978e6de83f697d4a7f05", - "reference": "28af674ff175d0768a5a978e6de83f697d4a7f05", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, - "time": "2021-07-19T06:46:01+00:00", + "time": "2020-09-28T05:57:25+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev" } }, "installation-source": "dist", @@ -2773,7 +3142,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.4" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5" }, "funding": [ { @@ -2784,25 +3153,37 @@ "install-path": "../phpunit/php-file-iterator" }, { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "version_normalized": "1.2.1.0", + "name": "phpunit/php-invoker", + "version": "3.1.1", + "version_normalized": "3.1.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.3" }, - "time": "2015-06-21T13:50:34+00:00", + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "time": "2020-09-28T05:58:55+00:00", "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, "installation-source": "dist", "autoload": { "classmap": [ @@ -2820,43 +3201,49 @@ "role": "lead" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ - "template" + "process" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" }, - "install-path": "../phpunit/php-text-template" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "install-path": "../phpunit/php-invoker" }, { - "name": "phpunit/php-timer", - "version": "2.1.3", - "version_normalized": "2.1.3.0", + "name": "phpunit/php-text-template", + "version": "2.0.4", + "version_normalized": "2.0.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, - "time": "2020-11-30T08:20:02+00:00", + "time": "2020-10-26T05:33:50+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.0-dev" } }, "installation-source": "dist", @@ -2876,14 +3263,14 @@ "role": "lead" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "timer" + "template" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" }, "funding": [ { @@ -2891,35 +3278,34 @@ "type": "github" } ], - "install-path": "../phpunit/php-timer" + "install-path": "../phpunit/php-text-template" }, { - "name": "phpunit/php-token-stream", - "version": "4.0.4", - "version_normalized": "4.0.4.0", + "name": "phpunit/php-timer", + "version": "5.0.3", + "version_normalized": "5.0.3.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/a853a0e183b9db7eed023d7933a858fa1c8d25a3", - "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": "^7.3 || ^8.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^9.3" }, - "time": "2020-08-04T08:28:15+00:00", + "time": "2020-10-26T13:16:10+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "5.0-dev" } }, "installation-source": "dist", @@ -2935,17 +3321,18 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "tokenizer" + "timer" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master" + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" }, "funding": [ { @@ -2953,22 +3340,21 @@ "type": "github" } ], - "abandoned": true, - "install-path": "../phpunit/php-token-stream" + "install-path": "../phpunit/php-timer" }, { "name": "phpunit/phpunit", - "version": "8.5.20", - "version_normalized": "8.5.20.0", + "version": "9.5.10", + "version_normalized": "9.5.10.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "9deefba183198398a09b927a6ac6bc1feb0b7b70" + "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9deefba183198398a09b927a6ac6bc1feb0b7b70", - "reference": "9deefba183198398a09b927a6ac6bc1feb0b7b70", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a", + "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a", "shasum": "" }, "require": { @@ -2979,47 +3365,53 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.0", + "myclabs/deep-copy": "^1.10.1", "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", - "php": ">=7.2", - "phpspec/prophecy": "^1.10.3", - "phpunit/php-code-coverage": "^7.0.12", - "phpunit/php-file-iterator": "^2.0.4", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1.2", - "sebastian/comparator": "^3.0.2", - "sebastian/diff": "^3.0.2", - "sebastian/environment": "^4.2.3", - "sebastian/exporter": "^3.1.2", - "sebastian/global-state": "^3.0.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0.1", - "sebastian/type": "^1.1.3", - "sebastian/version": "^2.0.1" + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.7", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^2.3.4", + "sebastian/version": "^3.0.2" }, "require-dev": { - "ext-pdo": "*" + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" }, "suggest": { "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0.0" + "ext-xdebug": "*" }, - "time": "2021-08-31T06:44:38+00:00", + "time": "2021-09-25T07:38:51+00:00", "bin": [ "phpunit" ], "type": "library", "extra": { "branch-alias": { - "dev-master": "8.5-dev" + "dev-master": "9.5-dev" } }, "installation-source": "dist", "autoload": { "classmap": [ "src/" + ], + "files": [ + "src/Framework/Assert/Functions.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3042,7 +3434,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.20" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10" }, "funding": [ { @@ -3058,29 +3450,24 @@ }, { "name": "psr/container", - "version": "2.0.1", - "version_normalized": "2.0.1.0", + "version": "1.1.1", + "version_normalized": "1.1.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "2ae37329ee82f91efadc282cc2d527fd6065a5ef" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/2ae37329ee82f91efadc282cc2d527fd6065a5ef", - "reference": "2ae37329ee82f91efadc282cc2d527fd6065a5ef", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { "php": ">=7.2.0" }, - "time": "2021-03-24T13:40:57+00:00", + "time": "2021-03-05T17:36:06+00:00", "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, "installation-source": "dist", "autoload": { "psr-4": { @@ -3108,7 +3495,7 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.1" + "source": "https://github.com/php-fig/container/tree/1.1.1" }, "install-path": "../psr/container" }, @@ -3504,17 +3891,17 @@ }, { "name": "psy/psysh", - "version": "v0.10.8", - "version_normalized": "0.10.8.0", + "version": "v0.10.9", + "version_normalized": "0.10.9.0", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "e4573f47750dd6c92dca5aee543fa77513cbd8d3" + "reference": "01281336c4ae557fe4a994544f30d3a1bc204375" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/e4573f47750dd6c92dca5aee543fa77513cbd8d3", - "reference": "e4573f47750dd6c92dca5aee543fa77513cbd8d3", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/01281336c4ae557fe4a994544f30d3a1bc204375", + "reference": "01281336c4ae557fe4a994544f30d3a1bc204375", "shasum": "" }, "require": { @@ -3536,7 +3923,7 @@ "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history.", "hoa/console": "A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit." }, - "time": "2021-04-10T16:23:39+00:00", + "time": "2021-10-10T13:37:39+00:00", "bin": [ "bin/psysh" ], @@ -3576,23 +3963,76 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.10.8" + "source": "https://github.com/bobthecow/psysh/tree/v0.10.9" }, "install-path": "../psy/psysh" }, + { + "name": "react/promise", + "version": "v2.8.0", + "version_normalized": "2.8.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4", + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36" + }, + "time": "2020-05-12T15:16:56+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v2.8.0" + }, + "install-path": "../react/promise" + }, { "name": "robmorgan/phinx", - "version": "0.12.8", - "version_normalized": "0.12.8.0", + "version": "0.12.9", + "version_normalized": "0.12.9.0", "source": { "type": "git", "url": "https://github.com/cakephp/phinx.git", - "reference": "d2ed1b452cc90f4cae4ea6b5976b94fb9e5ed2a2" + "reference": "5a0146a74c1bc195d1f5da86afa3b68badf7d90e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/phinx/zipball/d2ed1b452cc90f4cae4ea6b5976b94fb9e5ed2a2", - "reference": "d2ed1b452cc90f4cae4ea6b5976b94fb9e5ed2a2", + "url": "https://api.github.com/repos/cakephp/phinx/zipball/5a0146a74c1bc195d1f5da86afa3b68badf7d90e", + "reference": "5a0146a74c1bc195d1f5da86afa3b68badf7d90e", "shasum": "" }, "require": { @@ -3615,7 +4055,7 @@ "ext-pdo": "PDO extension is needed", "symfony/yaml": "Install if using YAML configuration format" }, - "time": "2021-07-28T15:31:39+00:00", + "time": "2021-10-12T10:49:25+00:00", "bin": [ "bin/phinx" ], @@ -3665,36 +4105,154 @@ ], "support": { "issues": "https://github.com/cakephp/phinx/issues", - "source": "https://github.com/cakephp/phinx/tree/0.12.8" + "source": "https://github.com/cakephp/phinx/tree/0.12.9" }, "install-path": "../robmorgan/phinx" }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "time": "2020-09-28T06:08:49+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "install-path": "../sebastian/cli-parser" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "version_normalized": "1.0.8.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "time": "2020-10-26T13:08:54+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "install-path": "../sebastian/code-unit" + }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.2", - "version_normalized": "1.0.2.0", + "version": "2.0.3", + "version_normalized": "2.0.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, - "time": "2020-11-30T08:15:22+00:00", + "time": "2020-09-28T05:30:19+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0-dev" } }, "installation-source": "dist", @@ -3717,7 +4275,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" }, "funding": [ { @@ -3729,32 +4287,32 @@ }, { "name": "sebastian/comparator", - "version": "3.0.3", - "version_normalized": "3.0.3.0", + "version": "4.0.6", + "version_normalized": "4.0.6.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" + "reference": "55f4261989e546dc112258c7a75935a81a7ce382" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382", "shasum": "" }, "require": { - "php": ">=7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, - "time": "2020-11-30T08:04:30+00:00", + "time": "2020-10-26T15:49:45+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "installation-source": "dist", @@ -3794,7 +4352,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" }, "funding": [ { @@ -3804,33 +4362,93 @@ ], "install-path": "../sebastian/comparator" }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "version_normalized": "2.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "time": "2020-10-26T15:52:27+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "install-path": "../sebastian/complexity" + }, { "name": "sebastian/diff", - "version": "3.0.3", - "version_normalized": "3.0.3.0", + "version": "4.0.4", + "version_normalized": "4.0.4.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" }, - "time": "2020-11-30T07:59:04+00:00", + "time": "2020-10-26T13:10:38+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "installation-source": "dist", @@ -3863,7 +4481,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" }, "funding": [ { @@ -3875,33 +4493,33 @@ }, { "name": "sebastian/environment", - "version": "4.2.4", - "version_normalized": "4.2.4.0", + "version": "5.1.3", + "version_normalized": "5.1.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" + "reference": "388b6ced16caa751030f6a69e588299fa09200ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^7.5" + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-posix": "*" }, - "time": "2020-11-30T07:53:42+00:00", + "time": "2020-09-28T05:52:38+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "5.1-dev" } }, "installation-source": "dist", @@ -3929,7 +4547,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" }, "funding": [ { @@ -3941,32 +4559,32 @@ }, { "name": "sebastian/exporter", - "version": "3.1.3", - "version_normalized": "3.1.3.0", + "version": "4.0.3", + "version_normalized": "4.0.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e" + "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e", - "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65", + "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65", "shasum": "" }, "require": { - "php": ">=7.0", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, - "time": "2020-11-30T07:47:53+00:00", + "time": "2020-09-28T05:24:23+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "4.0-dev" } }, "installation-source": "dist", @@ -4009,7 +4627,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3" }, "funding": [ { @@ -4021,36 +4639,36 @@ }, { "name": "sebastian/global-state", - "version": "3.0.1", - "version_normalized": "3.0.1.0", + "version": "5.0.3", + "version_normalized": "5.0.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b" + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/474fb9edb7ab891665d3bfc6317f42a0a150454b", - "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49", + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49", "shasum": "" }, "require": { - "php": ">=7.2", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^8.0" + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-uopz": "*" }, - "time": "2020-11-30T07:43:24+00:00", + "time": "2021-06-11T13:31:12+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "5.0-dev" } }, "installation-source": "dist", @@ -4076,7 +4694,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3" }, "funding": [ { @@ -4086,34 +4704,94 @@ ], "install-path": "../sebastian/global-state" }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "version_normalized": "1.0.3.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "time": "2020-11-28T06:42:11+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "install-path": "../sebastian/lines-of-code" + }, { "name": "sebastian/object-enumerator", - "version": "3.0.4", - "version_normalized": "3.0.4.0", + "version": "4.0.4", + "version_normalized": "4.0.4.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { - "php": ">=7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, - "time": "2020-11-30T07:40:27+00:00", + "time": "2020-10-26T13:12:34+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "installation-source": "dist", @@ -4136,7 +4814,7 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" }, "funding": [ { @@ -4148,30 +4826,30 @@ }, { "name": "sebastian/object-reflector", - "version": "1.1.2", - "version_normalized": "1.1.2.0", + "version": "2.0.4", + "version_normalized": "2.0.4.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, - "time": "2020-11-30T07:37:18+00:00", + "time": "2020-10-26T13:14:26+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "installation-source": "dist", @@ -4194,7 +4872,7 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" }, "funding": [ { @@ -4206,30 +4884,30 @@ }, { "name": "sebastian/recursion-context", - "version": "3.0.1", - "version_normalized": "3.0.1.0", + "version": "4.0.4", + "version_normalized": "4.0.4.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, - "time": "2020-11-30T07:34:24+00:00", + "time": "2020-10-26T13:17:30+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "installation-source": "dist", @@ -4260,7 +4938,7 @@ "homepage": "http://www.github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" }, "funding": [ { @@ -4272,27 +4950,30 @@ }, { "name": "sebastian/resource-operations", - "version": "2.0.2", - "version_normalized": "2.0.2.0", + "version": "3.0.3", + "version_normalized": "3.0.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, - "time": "2020-11-30T07:30:19+00:00", + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "time": "2020-09-28T06:45:17+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "installation-source": "dist", @@ -4315,7 +4996,7 @@ "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" }, "funding": [ { @@ -4327,30 +5008,30 @@ }, { "name": "sebastian/type", - "version": "1.1.4", - "version_normalized": "1.1.4.0", + "version": "2.3.4", + "version_normalized": "2.3.4.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4" + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4", - "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.2" + "phpunit/phpunit": "^9.3" }, - "time": "2020-11-30T07:25:11+00:00", + "time": "2021-06-15T12:49:02+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.3-dev" } }, "installation-source": "dist", @@ -4374,7 +5055,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" + "source": "https://github.com/sebastianbergmann/type/tree/2.3.4" }, "funding": [ { @@ -4386,27 +5067,27 @@ }, { "name": "sebastian/version", - "version": "2.0.1", - "version_normalized": "2.0.1.0", + "version": "3.0.2", + "version_normalized": "3.0.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.3" }, - "time": "2016-10-03T07:35:21+00:00", + "time": "2020-09-28T06:39:44+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev" } }, "installation-source": "dist", @@ -4430,8 +5111,14 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/master" + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "install-path": "../sebastian/version" }, { @@ -4551,19 +5238,83 @@ }, "install-path": "../seld/phar-utils" }, + { + "name": "slevomat/coding-standard", + "version": "6.4.1", + "version_normalized": "6.4.1.0", + "source": { + "type": "git", + "url": "https://github.com/slevomat/coding-standard.git", + "reference": "696dcca217d0c9da2c40d02731526c1e25b65346" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/696dcca217d0c9da2c40d02731526c1e25b65346", + "reference": "696dcca217d0c9da2c40d02731526c1e25b65346", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7", + "php": "^7.1 || ^8.0", + "phpstan/phpdoc-parser": "0.4.5 - 0.4.9", + "squizlabs/php_codesniffer": "^3.5.6" + }, + "require-dev": { + "phing/phing": "2.16.3", + "php-parallel-lint/php-parallel-lint": "1.2.0", + "phpstan/phpstan": "0.12.48", + "phpstan/phpstan-deprecation-rules": "0.12.5", + "phpstan/phpstan-phpunit": "0.12.16", + "phpstan/phpstan-strict-rules": "0.12.5", + "phpunit/phpunit": "7.5.20|8.5.5|9.4.0" + }, + "time": "2020-10-05T12:39:37+00:00", + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-master": "6.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "SlevomatCodingStandard\\": "SlevomatCodingStandard" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "support": { + "issues": "https://github.com/slevomat/coding-standard/issues", + "source": "https://github.com/slevomat/coding-standard/tree/6.4.1" + }, + "funding": [ + { + "url": "https://github.com/kukulich", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard", + "type": "tidelift" + } + ], + "install-path": "../slevomat/coding-standard" + }, { "name": "squizlabs/php_codesniffer", - "version": "3.6.0", - "version_normalized": "3.6.0.0", + "version": "3.5.8", + "version_normalized": "3.5.8.0", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ffced0d2c8fa8e6cdc4d695a743271fab6c38625" + "reference": "9d583721a7157ee997f235f327de038e7ea6dac4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ffced0d2c8fa8e6cdc4d695a743271fab6c38625", - "reference": "ffced0d2c8fa8e6cdc4d695a743271fab6c38625", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4", + "reference": "9d583721a7157ee997f235f327de038e7ea6dac4", "shasum": "" }, "require": { @@ -4575,7 +5326,7 @@ "require-dev": { "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, - "time": "2021-04-09T00:54:41+00:00", + "time": "2020-10-23T02:01:07+00:00", "bin": [ "bin/phpcs", "bin/phpcbf" @@ -5648,31 +6399,35 @@ }, { "name": "symfony/service-contracts", - "version": "v1.1.2", - "version_normalized": "1.1.2.0", + "version": "v2.4.0", + "version_normalized": "2.4.0.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "191afdcb5804db960d26d8566b7e9a2843cab3a0" + "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/191afdcb5804db960d26d8566b7e9a2843cab3a0", - "reference": "191afdcb5804db960d26d8566b7e9a2843cab3a0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", + "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": ">=7.2.5", + "psr/container": "^1.1" }, "suggest": { - "psr/container": "", "symfony/service-implementation": "" }, - "time": "2019-05-28T07:50:59+00:00", + "time": "2021-04-01T10:43:52+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "installation-source": "dist", @@ -5706,8 +6461,22 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v1.1.2" + "source": "https://github.com/symfony/service-contracts/tree/v2.4.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" + } + ], "install-path": "../symfony/service-contracts" }, { @@ -5798,17 +6567,17 @@ }, { "name": "symfony/var-dumper", - "version": "v5.3.7", - "version_normalized": "5.3.7.0", + "version": "v5.3.8", + "version_normalized": "5.3.8.0", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "3ad5af4aed07d0a0201bbcfc42658fe6c5b2fb8f" + "reference": "eaaea4098be1c90c8285543e1356a09c8aa5c8da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/3ad5af4aed07d0a0201bbcfc42658fe6c5b2fb8f", - "reference": "3ad5af4aed07d0a0201bbcfc42658fe6c5b2fb8f", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/eaaea4098be1c90c8285543e1356a09c8aa5c8da", + "reference": "eaaea4098be1c90c8285543e1356a09c8aa5c8da", "shasum": "" }, "require": { @@ -5831,7 +6600,7 @@ "ext-intl": "To show region name in time zone dump", "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" }, - "time": "2021-08-04T23:19:25+00:00", + "time": "2021-09-24T15:59:58+00:00", "bin": [ "Resources/bin/var-dump-server" ], @@ -5869,7 +6638,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.3.7" + "source": "https://github.com/symfony/var-dumper/tree/v5.3.8" }, "funding": [ { @@ -6159,18 +6928,23 @@ ], "dev": true, "dev-package-names": [ + "brick/varexporter", "cakephp/bake", "cakephp/cakephp-codesniffer", "cakephp/debug_kit", "cakephp/twig-view", "composer/composer", + "composer/metadata-minifier", "composer/semver", "composer/spdx-licenses", "composer/xdebug-handler", + "dealerdirect/phpcodesniffer-composer-installer", "doctrine/instantiator", "jasny/twig-extensions", "jdorn/sql-formatter", + "josegonzalez/dotenv", "justinrainbow/json-schema", + "m1/env", "myclabs/deep-copy", "nikic/php-parser", "phar-io/manifest", @@ -6179,20 +6953,26 @@ "phpdocumentor/reflection-docblock", "phpdocumentor/type-resolver", "phpspec/prophecy", + "phpstan/phpdoc-parser", "phpstan/phpstan", "phpunit/php-code-coverage", "phpunit/php-file-iterator", + "phpunit/php-invoker", "phpunit/php-text-template", "phpunit/php-timer", - "phpunit/php-token-stream", "phpunit/phpunit", "psy/psysh", + "react/promise", + "sebastian/cli-parser", + "sebastian/code-unit", "sebastian/code-unit-reverse-lookup", "sebastian/comparator", + "sebastian/complexity", "sebastian/diff", "sebastian/environment", "sebastian/exporter", "sebastian/global-state", + "sebastian/lines-of-code", "sebastian/object-enumerator", "sebastian/object-reflector", "sebastian/recursion-context", @@ -6201,6 +6981,7 @@ "sebastian/version", "seld/jsonlint", "seld/phar-utils", + "slevomat/coding-standard", "squizlabs/php_codesniffer", "symfony/finder", "symfony/process", diff --git a/app/vendor/composer/installed.php b/app/vendor/composer/installed.php index b88132ee9..388cecc25 100644 --- a/app/vendor/composer/installed.php +++ b/app/vendor/composer/installed.php @@ -5,7 +5,7 @@ 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => '682a45c521450a62abe64973b45e96c1d190516b', + 'reference' => '9cabecad7c9472d0821f2e2839dcddb3bf0657e7', 'name' => 'cakephp/app', 'dev' => true, ), @@ -19,14 +19,14 @@ 'reference' => 'aa161c28dfffcab13a3cb9bd407917573987f0f6', 'dev_requirement' => false, ), - 'aura/intl' => array( - 'pretty_version' => '3.0.0', - 'version' => '3.0.0.0', + 'brick/varexporter' => array( + 'pretty_version' => '0.3.5', + 'version' => '0.3.5.0', 'type' => 'library', - 'install_path' => __DIR__ . '/../aura/intl', + 'install_path' => __DIR__ . '/../brick/varexporter', 'aliases' => array(), - 'reference' => '7fce228980b19bf4dee2d7bbd6202a69b0dde926', - 'dev_requirement' => false, + 'reference' => '05241f28dfcba2b51b11e2d750e296316ebbe518', + 'dev_requirement' => true, ), 'cakephp/app' => array( 'pretty_version' => 'dev-develop', @@ -34,40 +34,40 @@ 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => '682a45c521450a62abe64973b45e96c1d190516b', + 'reference' => '9cabecad7c9472d0821f2e2839dcddb3bf0657e7', 'dev_requirement' => false, ), 'cakephp/bake' => array( - 'pretty_version' => '2.2.0', - 'version' => '2.2.0.0', + 'pretty_version' => '2.5.2', + 'version' => '2.5.2.0', 'type' => 'cakephp-plugin', 'install_path' => __DIR__ . '/../cakephp/bake', 'aliases' => array(), - 'reference' => 'f1c297c4e903a15188389011b93ce46119849d01', + 'reference' => 'bfb856afcfbc70c5cf5341669c3036a45ca15d94', 'dev_requirement' => true, ), 'cakephp/cache' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'cakephp/cakephp' => array( - 'pretty_version' => '4.0.10', - 'version' => '4.0.10.0', + 'pretty_version' => '4.2.10', + 'version' => '4.2.10.0', 'type' => 'library', 'install_path' => __DIR__ . '/../cakephp/cakephp', 'aliases' => array(), - 'reference' => '5c6d72bc9bb1be6eec1b00b3930f9e5054ee6d76', + 'reference' => 'ea47e927083e2f4bfaed0c404756d9852b617cad', 'dev_requirement' => false, ), 'cakephp/cakephp-codesniffer' => array( - 'pretty_version' => '3.3.0', - 'version' => '3.3.0.0', + 'pretty_version' => '4.2.4', + 'version' => '4.2.4.0', 'type' => 'phpcodesniffer-standard', 'install_path' => __DIR__ . '/../cakephp/cakephp-codesniffer', 'aliases' => array(), - 'reference' => '7998a191e787fd5b68cb635d7050cb0d7b55e1a1', + 'reference' => 'c5bb1faeebf09cd4a3604bdb0c84f7bc92dc5475', 'dev_requirement' => true, ), 'cakephp/chronos' => array( @@ -82,76 +82,76 @@ 'cakephp/collection' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'cakephp/console' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'cakephp/core' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'cakephp/database' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'cakephp/datasource' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'cakephp/debug_kit' => array( - 'pretty_version' => '4.2.0', - 'version' => '4.2.0.0', + 'pretty_version' => '4.4.4', + 'version' => '4.4.4.0', 'type' => 'cakephp-plugin', 'install_path' => __DIR__ . '/../cakephp/debug_kit', 'aliases' => array(), - 'reference' => '940a0214947e85bbaa0724acfda852f64831f04f', + 'reference' => '10d7d9ba36945844211f1d8763e59618917e1784', 'dev_requirement' => true, ), 'cakephp/event' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'cakephp/filesystem' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'cakephp/form' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'cakephp/http' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'cakephp/i18n' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'cakephp/log' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'cakephp/migrations' => array( @@ -166,7 +166,7 @@ 'cakephp/orm' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'cakephp/plugin-installer' => array( @@ -179,51 +179,69 @@ 'dev_requirement' => false, ), 'cakephp/twig-view' => array( - 'pretty_version' => '1.2.0', - 'version' => '1.2.0.0', + 'pretty_version' => '1.3.0', + 'version' => '1.3.0.0', 'type' => 'cakephp-plugin', 'install_path' => __DIR__ . '/../cakephp/twig-view', 'aliases' => array(), - 'reference' => '668dd6aee43dd616b1e83cb9ba166f094c10fbce', + 'reference' => '14df50360b809a171d0688020fbdfe513763f89b', 'dev_requirement' => true, ), 'cakephp/utility' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'cakephp/validation' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '4.0.10', + 0 => '4.2.10', ), ), 'composer/ca-bundle' => array( - 'pretty_version' => '1.2.10', - 'version' => '1.2.10.0', + 'pretty_version' => '1.2.11', + 'version' => '1.2.11.0', 'type' => 'library', 'install_path' => __DIR__ . '/./ca-bundle', 'aliases' => array(), - 'reference' => '9fdb22c2e97a614657716178093cd1da90a64aa8', + 'reference' => '0b072d51c5a9c6f3412f7ea3ab043d6603cb2582', 'dev_requirement' => false, ), 'composer/composer' => array( - 'pretty_version' => '1.10.22', - 'version' => '1.10.22.0', + 'pretty_version' => '2.1.9', + 'version' => '2.1.9.0', 'type' => 'library', 'install_path' => __DIR__ . '/./composer', 'aliases' => array(), - 'reference' => '28c9dfbe2351635961f670773e8d7b17bc5eda25', + 'reference' => 'e558c88f28d102d497adec4852802c0dc14c7077', 'dev_requirement' => true, ), + 'composer/metadata-minifier' => array( + 'pretty_version' => '1.0.0', + 'version' => '1.0.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/./metadata-minifier', + 'aliases' => array(), + 'reference' => 'c549d23829536f0d0e984aaabbf02af91f443207', + 'dev_requirement' => true, + ), + 'composer/package-versions-deprecated' => array( + 'pretty_version' => '1.11.99.4', + 'version' => '1.11.99.4', + 'type' => 'composer-plugin', + 'install_path' => __DIR__ . '/./package-versions-deprecated', + 'aliases' => array(), + 'reference' => 'b174585d1fe49ceed21928a945138948cb394600', + 'dev_requirement' => false, + ), 'composer/semver' => array( - 'pretty_version' => '1.7.2', - 'version' => '1.7.2.0', + 'pretty_version' => '3.2.5', + 'version' => '3.2.5.0', 'type' => 'library', 'install_path' => __DIR__ . '/./semver', 'aliases' => array(), - 'reference' => '647490bbcaf7fc4891c58f47b825eb99d19c377a', + 'reference' => '31f3ea725711245195f62e54ffa402d8ef2fdba9', 'dev_requirement' => true, ), 'composer/spdx-licenses' => array( @@ -236,12 +254,21 @@ 'dev_requirement' => true, ), 'composer/xdebug-handler' => array( - 'pretty_version' => '1.4.6', - 'version' => '1.4.6.0', + 'pretty_version' => '2.0.2', + 'version' => '2.0.2.0', 'type' => 'library', 'install_path' => __DIR__ . '/./xdebug-handler', 'aliases' => array(), - 'reference' => 'f27e06cd9675801df441b3656569b328e04aa37c', + 'reference' => '84674dd3a7575ba617f5a76d7e9e29a7d3891339', + 'dev_requirement' => true, + ), + 'dealerdirect/phpcodesniffer-composer-installer' => array( + 'pretty_version' => 'v0.7.1', + 'version' => '0.7.1.0', + 'type' => 'composer-plugin', + 'install_path' => __DIR__ . '/../dealerdirect/phpcodesniffer-composer-installer', + 'aliases' => array(), + 'reference' => 'fe390591e0241955f22eb9ba327d137e501c771c', 'dev_requirement' => true, ), 'doctrine/cache' => array( @@ -254,12 +281,12 @@ 'dev_requirement' => false, ), 'doctrine/dbal' => array( - 'pretty_version' => '2.13.3', - 'version' => '2.13.3.0', + 'pretty_version' => '3.1.3', + 'version' => '3.1.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/dbal', 'aliases' => array(), - 'reference' => '0d7adf4cadfee6f70850e5b163e6cdd706417838', + 'reference' => '96b0053775a544b4a6ab47654dac0621be8b4cf8', 'dev_requirement' => false, ), 'doctrine/deprecations' => array( @@ -308,13 +335,13 @@ 'dev_requirement' => true, ), 'josegonzalez/dotenv' => array( - 'pretty_version' => '2.1.0', - 'version' => '2.1.0.0', + 'pretty_version' => '3.2.0', + 'version' => '3.2.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../josegonzalez/dotenv', 'aliases' => array(), - 'reference' => 'ff3461f2960737f54054dff4fef3482a2bb9682b', - 'dev_requirement' => false, + 'reference' => 'f19174d9d7213a6c20e8e5e268aa7dd042d821ca', + 'dev_requirement' => true, ), 'justinrainbow/json-schema' => array( 'pretty_version' => '5.2.11', @@ -326,21 +353,21 @@ 'dev_requirement' => true, ), 'laminas/laminas-diactoros' => array( - 'pretty_version' => '2.7.0', - 'version' => '2.7.0.0', + 'pretty_version' => '2.8.0', + 'version' => '2.8.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../laminas/laminas-diactoros', 'aliases' => array(), - 'reference' => '8b5792b7c81465efb14780c2d4787f158bd96183', + 'reference' => '0c26ef1d95b6d7e6e3943a243ba3dc0797227199', 'dev_requirement' => false, ), 'laminas/laminas-httphandlerrunner' => array( - 'pretty_version' => '1.4.0', - 'version' => '1.4.0.0', + 'pretty_version' => '1.5.0', + 'version' => '1.5.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../laminas/laminas-httphandlerrunner', 'aliases' => array(), - 'reference' => '6a2dd33e4166469ade07ad1283b45924383b224b', + 'reference' => '5f94e55d93f756e8ad07b9049aeb3d6d84582d0e', 'dev_requirement' => false, ), 'laminas/laminas-zendframework-bridge' => array( @@ -352,6 +379,15 @@ 'reference' => 'bf180a382393e7db5c1e8d0f2ec0c4af9c724baf', 'dev_requirement' => false, ), + 'league/container' => array( + 'pretty_version' => '3.4.1', + 'version' => '3.4.1.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../league/container', + 'aliases' => array(), + 'reference' => '84ecbc2dbecc31bd23faf759a0e329ee49abddbd', + 'dev_requirement' => false, + ), 'm1/env' => array( 'pretty_version' => '2.2.0', 'version' => '2.2.0.0', @@ -359,7 +395,7 @@ 'install_path' => __DIR__ . '/../m1/env', 'aliases' => array(), 'reference' => '5c296e3e13450a207e12b343f3af1d7ab569f6f3', - 'dev_requirement' => false, + 'dev_requirement' => true, ), 'mobiledetect/mobiledetectlib' => array( 'pretty_version' => '2.8.37', @@ -383,14 +419,26 @@ ), ), 'nikic/php-parser' => array( - 'pretty_version' => 'v4.12.0', - 'version' => '4.12.0.0', + 'pretty_version' => 'v4.13.0', + 'version' => '4.13.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../nikic/php-parser', 'aliases' => array(), - 'reference' => '6608f01670c3cc5079e18c1dab1104e002579143', + 'reference' => '50953a2691a922aa1769461637869a0a2faa3f53', 'dev_requirement' => true, ), + 'ocramius/package-versions' => array( + 'dev_requirement' => false, + 'replaced' => array( + 0 => '1.11.99', + ), + ), + 'orno/di' => array( + 'dev_requirement' => false, + 'replaced' => array( + 0 => '~2.0', + ), + ), 'phar-io/manifest' => array( 'pretty_version' => '2.0.3', 'version' => '2.0.3.0', @@ -428,12 +476,12 @@ 'dev_requirement' => true, ), 'phpdocumentor/type-resolver' => array( - 'pretty_version' => '1.5.0', - 'version' => '1.5.0.0', + 'pretty_version' => '1.5.1', + 'version' => '1.5.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpdocumentor/type-resolver', 'aliases' => array(), - 'reference' => '30f38bffc6f24293dadd1823936372dfa9e86e2f', + 'reference' => 'a12f7e301eb7258bb68acd89d4aefa05c2906cae', 'dev_requirement' => true, ), 'phpspec/prophecy' => array( @@ -445,6 +493,15 @@ 'reference' => 'd86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e', 'dev_requirement' => true, ), + 'phpstan/phpdoc-parser' => array( + 'pretty_version' => '0.4.9', + 'version' => '0.4.9.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../phpstan/phpdoc-parser', + 'aliases' => array(), + 'reference' => '98a088b17966bdf6ee25c8a4b634df313d8aa531', + 'dev_requirement' => true, + ), 'phpstan/phpstan' => array( 'pretty_version' => '0.12.99', 'version' => '0.12.99.0', @@ -455,68 +512,74 @@ 'dev_requirement' => true, ), 'phpunit/php-code-coverage' => array( - 'pretty_version' => '7.0.15', - 'version' => '7.0.15.0', + 'pretty_version' => '9.2.7', + 'version' => '9.2.7.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/php-code-coverage', 'aliases' => array(), - 'reference' => '819f92bba8b001d4363065928088de22f25a3a48', + 'reference' => 'd4c798ed8d51506800b441f7a13ecb0f76f12218', 'dev_requirement' => true, ), 'phpunit/php-file-iterator' => array( - 'pretty_version' => '2.0.4', - 'version' => '2.0.4.0', + 'pretty_version' => '3.0.5', + 'version' => '3.0.5.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/php-file-iterator', 'aliases' => array(), - 'reference' => '28af674ff175d0768a5a978e6de83f697d4a7f05', + 'reference' => 'aa4be8575f26070b100fccb67faabb28f21f66f8', 'dev_requirement' => true, ), - 'phpunit/php-text-template' => array( - 'pretty_version' => '1.2.1', - 'version' => '1.2.1.0', + 'phpunit/php-invoker' => array( + 'pretty_version' => '3.1.1', + 'version' => '3.1.1.0', 'type' => 'library', - 'install_path' => __DIR__ . '/../phpunit/php-text-template', + 'install_path' => __DIR__ . '/../phpunit/php-invoker', 'aliases' => array(), - 'reference' => '31f8b717e51d9a2afca6c9f046f5d69fc27c8686', + 'reference' => '5a10147d0aaf65b58940a0b72f71c9ac0423cc67', 'dev_requirement' => true, ), - 'phpunit/php-timer' => array( - 'pretty_version' => '2.1.3', - 'version' => '2.1.3.0', + 'phpunit/php-text-template' => array( + 'pretty_version' => '2.0.4', + 'version' => '2.0.4.0', 'type' => 'library', - 'install_path' => __DIR__ . '/../phpunit/php-timer', + 'install_path' => __DIR__ . '/../phpunit/php-text-template', 'aliases' => array(), - 'reference' => '2454ae1765516d20c4ffe103d85a58a9a3bd5662', + 'reference' => '5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28', 'dev_requirement' => true, ), - 'phpunit/php-token-stream' => array( - 'pretty_version' => '4.0.4', - 'version' => '4.0.4.0', + 'phpunit/php-timer' => array( + 'pretty_version' => '5.0.3', + 'version' => '5.0.3.0', 'type' => 'library', - 'install_path' => __DIR__ . '/../phpunit/php-token-stream', + 'install_path' => __DIR__ . '/../phpunit/php-timer', 'aliases' => array(), - 'reference' => 'a853a0e183b9db7eed023d7933a858fa1c8d25a3', + 'reference' => '5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2', 'dev_requirement' => true, ), 'phpunit/phpunit' => array( - 'pretty_version' => '8.5.20', - 'version' => '8.5.20.0', + 'pretty_version' => '9.5.10', + 'version' => '9.5.10.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/phpunit', 'aliases' => array(), - 'reference' => '9deefba183198398a09b927a6ac6bc1feb0b7b70', + 'reference' => 'c814a05837f2edb0d1471d6e3f4ab3501ca3899a', 'dev_requirement' => true, ), 'psr/container' => array( - 'pretty_version' => '2.0.1', - 'version' => '2.0.1.0', + 'pretty_version' => '1.1.1', + 'version' => '1.1.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/container', 'aliases' => array(), - 'reference' => '2ae37329ee82f91efadc282cc2d527fd6065a5ef', + 'reference' => '8622567409010282b7aeebe4bb841fe98b58dcaf', 'dev_requirement' => false, ), + 'psr/container-implementation' => array( + 'dev_requirement' => false, + 'provided' => array( + 0 => '^1.0', + ), + ), 'psr/http-client' => array( 'pretty_version' => '1.0.1', 'version' => '1.0.1.0', @@ -599,129 +662,174 @@ 'dev_requirement' => false, ), 'psy/psysh' => array( - 'pretty_version' => 'v0.10.8', - 'version' => '0.10.8.0', + 'pretty_version' => 'v0.10.9', + 'version' => '0.10.9.0', 'type' => 'library', 'install_path' => __DIR__ . '/../psy/psysh', 'aliases' => array(), - 'reference' => 'e4573f47750dd6c92dca5aee543fa77513cbd8d3', + 'reference' => '01281336c4ae557fe4a994544f30d3a1bc204375', + 'dev_requirement' => true, + ), + 'react/promise' => array( + 'pretty_version' => 'v2.8.0', + 'version' => '2.8.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../react/promise', + 'aliases' => array(), + 'reference' => 'f3cff96a19736714524ca0dd1d4130de73dbbbc4', 'dev_requirement' => true, ), 'robmorgan/phinx' => array( - 'pretty_version' => '0.12.8', - 'version' => '0.12.8.0', + 'pretty_version' => '0.12.9', + 'version' => '0.12.9.0', 'type' => 'library', 'install_path' => __DIR__ . '/../robmorgan/phinx', 'aliases' => array(), - 'reference' => 'd2ed1b452cc90f4cae4ea6b5976b94fb9e5ed2a2', + 'reference' => '5a0146a74c1bc195d1f5da86afa3b68badf7d90e', 'dev_requirement' => false, ), + 'sebastian/cli-parser' => array( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../sebastian/cli-parser', + 'aliases' => array(), + 'reference' => '442e7c7e687e42adc03470c7b668bc4b2402c0b2', + 'dev_requirement' => true, + ), + 'sebastian/code-unit' => array( + 'pretty_version' => '1.0.8', + 'version' => '1.0.8.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../sebastian/code-unit', + 'aliases' => array(), + 'reference' => '1fc9f64c0927627ef78ba436c9b17d967e68e120', + 'dev_requirement' => true, + ), 'sebastian/code-unit-reverse-lookup' => array( - 'pretty_version' => '1.0.2', - 'version' => '1.0.2.0', + 'pretty_version' => '2.0.3', + 'version' => '2.0.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/code-unit-reverse-lookup', 'aliases' => array(), - 'reference' => '1de8cd5c010cb153fcd68b8d0f64606f523f7619', + 'reference' => 'ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5', 'dev_requirement' => true, ), 'sebastian/comparator' => array( - 'pretty_version' => '3.0.3', - 'version' => '3.0.3.0', + 'pretty_version' => '4.0.6', + 'version' => '4.0.6.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/comparator', 'aliases' => array(), - 'reference' => '1071dfcef776a57013124ff35e1fc41ccd294758', + 'reference' => '55f4261989e546dc112258c7a75935a81a7ce382', + 'dev_requirement' => true, + ), + 'sebastian/complexity' => array( + 'pretty_version' => '2.0.2', + 'version' => '2.0.2.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../sebastian/complexity', + 'aliases' => array(), + 'reference' => '739b35e53379900cc9ac327b2147867b8b6efd88', 'dev_requirement' => true, ), 'sebastian/diff' => array( - 'pretty_version' => '3.0.3', - 'version' => '3.0.3.0', + 'pretty_version' => '4.0.4', + 'version' => '4.0.4.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/diff', 'aliases' => array(), - 'reference' => '14f72dd46eaf2f2293cbe79c93cc0bc43161a211', + 'reference' => '3461e3fccc7cfdfc2720be910d3bd73c69be590d', 'dev_requirement' => true, ), 'sebastian/environment' => array( - 'pretty_version' => '4.2.4', - 'version' => '4.2.4.0', + 'pretty_version' => '5.1.3', + 'version' => '5.1.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/environment', 'aliases' => array(), - 'reference' => 'd47bbbad83711771f167c72d4e3f25f7fcc1f8b0', + 'reference' => '388b6ced16caa751030f6a69e588299fa09200ac', 'dev_requirement' => true, ), 'sebastian/exporter' => array( - 'pretty_version' => '3.1.3', - 'version' => '3.1.3.0', + 'pretty_version' => '4.0.3', + 'version' => '4.0.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/exporter', 'aliases' => array(), - 'reference' => '6b853149eab67d4da22291d36f5b0631c0fd856e', + 'reference' => 'd89cc98761b8cb5a1a235a6b703ae50d34080e65', 'dev_requirement' => true, ), 'sebastian/global-state' => array( - 'pretty_version' => '3.0.1', - 'version' => '3.0.1.0', + 'pretty_version' => '5.0.3', + 'version' => '5.0.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/global-state', 'aliases' => array(), - 'reference' => '474fb9edb7ab891665d3bfc6317f42a0a150454b', + 'reference' => '23bd5951f7ff26f12d4e3242864df3e08dec4e49', + 'dev_requirement' => true, + ), + 'sebastian/lines-of-code' => array( + 'pretty_version' => '1.0.3', + 'version' => '1.0.3.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../sebastian/lines-of-code', + 'aliases' => array(), + 'reference' => 'c1c2e997aa3146983ed888ad08b15470a2e22ecc', 'dev_requirement' => true, ), 'sebastian/object-enumerator' => array( - 'pretty_version' => '3.0.4', - 'version' => '3.0.4.0', + 'pretty_version' => '4.0.4', + 'version' => '4.0.4.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/object-enumerator', 'aliases' => array(), - 'reference' => 'e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2', + 'reference' => '5c9eeac41b290a3712d88851518825ad78f45c71', 'dev_requirement' => true, ), 'sebastian/object-reflector' => array( - 'pretty_version' => '1.1.2', - 'version' => '1.1.2.0', + 'pretty_version' => '2.0.4', + 'version' => '2.0.4.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/object-reflector', 'aliases' => array(), - 'reference' => '9b8772b9cbd456ab45d4a598d2dd1a1bced6363d', + 'reference' => 'b4f479ebdbf63ac605d183ece17d8d7fe49c15c7', 'dev_requirement' => true, ), 'sebastian/recursion-context' => array( - 'pretty_version' => '3.0.1', - 'version' => '3.0.1.0', + 'pretty_version' => '4.0.4', + 'version' => '4.0.4.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/recursion-context', 'aliases' => array(), - 'reference' => '367dcba38d6e1977be014dc4b22f47a484dac7fb', + 'reference' => 'cd9d8cf3c5804de4341c283ed787f099f5506172', 'dev_requirement' => true, ), 'sebastian/resource-operations' => array( - 'pretty_version' => '2.0.2', - 'version' => '2.0.2.0', + 'pretty_version' => '3.0.3', + 'version' => '3.0.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/resource-operations', 'aliases' => array(), - 'reference' => '31d35ca87926450c44eae7e2611d45a7a65ea8b3', + 'reference' => '0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8', 'dev_requirement' => true, ), 'sebastian/type' => array( - 'pretty_version' => '1.1.4', - 'version' => '1.1.4.0', + 'pretty_version' => '2.3.4', + 'version' => '2.3.4.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/type', 'aliases' => array(), - 'reference' => '0150cfbc4495ed2df3872fb31b26781e4e077eb4', + 'reference' => 'b8cd8a1c753c90bc1a0f5372170e3e489136f914', 'dev_requirement' => true, ), 'sebastian/version' => array( - 'pretty_version' => '2.0.1', - 'version' => '2.0.1.0', + 'pretty_version' => '3.0.2', + 'version' => '3.0.2.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/version', 'aliases' => array(), - 'reference' => '99732be0ddb3361e16ad77b68ba41efc8e979019', + 'reference' => 'c6c1022351a901512170118436c764e473f6de8c', 'dev_requirement' => true, ), 'seld/jsonlint' => array( @@ -742,13 +850,22 @@ 'reference' => '749042a2315705d2dfbbc59234dd9ceb22bf3ff0', 'dev_requirement' => true, ), + 'slevomat/coding-standard' => array( + 'pretty_version' => '6.4.1', + 'version' => '6.4.1.0', + 'type' => 'phpcodesniffer-standard', + 'install_path' => __DIR__ . '/../slevomat/coding-standard', + 'aliases' => array(), + 'reference' => '696dcca217d0c9da2c40d02731526c1e25b65346', + 'dev_requirement' => true, + ), 'squizlabs/php_codesniffer' => array( - 'pretty_version' => '3.6.0', - 'version' => '3.6.0.0', + 'pretty_version' => '3.5.8', + 'version' => '3.5.8.0', 'type' => 'library', 'install_path' => __DIR__ . '/../squizlabs/php_codesniffer', 'aliases' => array(), - 'reference' => 'ffced0d2c8fa8e6cdc4d695a743271fab6c38625', + 'reference' => '9d583721a7157ee997f235f327de038e7ea6dac4', 'dev_requirement' => true, ), 'symfony/config' => array( @@ -869,12 +986,12 @@ 'dev_requirement' => true, ), 'symfony/service-contracts' => array( - 'pretty_version' => 'v1.1.2', - 'version' => '1.1.2.0', + 'pretty_version' => 'v2.4.0', + 'version' => '2.4.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/service-contracts', 'aliases' => array(), - 'reference' => '191afdcb5804db960d26d8566b7e9a2843cab3a0', + 'reference' => 'f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb', 'dev_requirement' => false, ), 'symfony/string' => array( @@ -887,12 +1004,12 @@ 'dev_requirement' => false, ), 'symfony/var-dumper' => array( - 'pretty_version' => 'v5.3.7', - 'version' => '5.3.7.0', + 'pretty_version' => 'v5.3.8', + 'version' => '5.3.8.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/var-dumper', 'aliases' => array(), - 'reference' => '3ad5af4aed07d0a0201bbcfc42658fe6c5b2fb8f', + 'reference' => 'eaaea4098be1c90c8285543e1356a09c8aa5c8da', 'dev_requirement' => true, ), 'theseer/tokenizer' => array( diff --git a/app/vendor/composer/metadata-minifier/LICENSE b/app/vendor/composer/metadata-minifier/LICENSE new file mode 100644 index 000000000..c5a282ff4 --- /dev/null +++ b/app/vendor/composer/metadata-minifier/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2021 Composer + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/app/vendor/composer/metadata-minifier/README.md b/app/vendor/composer/metadata-minifier/README.md new file mode 100644 index 000000000..75accd454 --- /dev/null +++ b/app/vendor/composer/metadata-minifier/README.md @@ -0,0 +1,37 @@ +composer/metadata-minifier +========================== + +Small utility library that handles metadata minification and expansion. + +This is used by [Composer](https://github.com/composer/composer)'s 2.x repository metadata protocol. + + +Installation +------------ + +Install the latest version with: + +```bash +$ composer require composer/metadata-minifier +``` + + +Requirements +------------ + +* PHP 5.3.2 is required but using the latest version of PHP is highly recommended. + + +Basic usage +----------- + +### `Composer\MetadataMinifier\MetadataMinifier` + +- `MetadataMinifier::expand()`: Expands an array of minified versions back to their original format +- `MetadataMinifier::minify()`: Minifies an array of versions into a set of version diffs + + +License +------- + +composer/metadata-minifier is licensed under the MIT License, see the LICENSE file for details. diff --git a/app/vendor/composer/metadata-minifier/composer.json b/app/vendor/composer/metadata-minifier/composer.json new file mode 100644 index 000000000..d70a9bebc --- /dev/null +++ b/app/vendor/composer/metadata-minifier/composer.json @@ -0,0 +1,47 @@ +{ + "name": "composer/metadata-minifier", + "description": "Small utility library that handles metadata minification and expansion.", + "type": "library", + "license": "MIT", + "keywords": [ + "compression", + "composer" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "support": { + "issues": "https://github.com/composer/metadata-minifier/issues" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.2 || ^5", + "phpstan/phpstan": "^0.12.55", + "composer/composer": "^2" + }, + "autoload": { + "psr-4": { + "Composer\\MetadataMinifier\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Composer\\Test\\MetadataMinifier\\": "tests" + } + }, + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "scripts": { + "test": "SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT=1 vendor/bin/simple-phpunit", + "phpstan": "vendor/bin/phpstan analyse" + } +} diff --git a/app/vendor/composer/xdebug-handler/phpstan.neon.dist b/app/vendor/composer/metadata-minifier/phpstan.neon.dist similarity index 80% rename from app/vendor/composer/xdebug-handler/phpstan.neon.dist rename to app/vendor/composer/metadata-minifier/phpstan.neon.dist index 46b0f4776..1cd333bd1 100644 --- a/app/vendor/composer/xdebug-handler/phpstan.neon.dist +++ b/app/vendor/composer/metadata-minifier/phpstan.neon.dist @@ -1,5 +1,5 @@ parameters: - level: 5 + level: 8 paths: - src - tests diff --git a/app/vendor/composer/metadata-minifier/src/MetadataMinifier.php b/app/vendor/composer/metadata-minifier/src/MetadataMinifier.php new file mode 100644 index 000000000..312ddbaca --- /dev/null +++ b/app/vendor/composer/metadata-minifier/src/MetadataMinifier.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\MetadataMinifier; + +class MetadataMinifier +{ + /** + * Expands an array of minified versions back to their original format + * + * @param array[] $versions A list of minified version arrays + * @return array[] A list of version arrays + */ + public static function expand(array $versions) + { + $expanded = array(); + $expandedVersion = null; + foreach ($versions as $versionData) { + if (!$expandedVersion) { + $expandedVersion = $versionData; + $expanded[] = $expandedVersion; + continue; + } + + // add any changes from the previous version to the expanded one + foreach ($versionData as $key => $val) { + if ($val === '__unset') { + unset($expandedVersion[$key]); + } else { + $expandedVersion[$key] = $val; + } + } + + $expanded[] = $expandedVersion; + } + + return $expanded; + } + + /** + * Minifies an array of versions into a set of version diffs + * + * @param array[] $versions A list of version arrays + * @return array[] A list of versions minified with each array only containing the differences to the previous one + */ + public static function minify(array $versions) + { + $minifiedVersions = array(); + + $lastKnownVersionData = null; + foreach ($versions as $version) { + if (!$lastKnownVersionData) { + $lastKnownVersionData = $version; + $minifiedVersions[] = $version; + continue; + } + + $minifiedVersion = array(); + + // add any changes from the previous version + foreach ($version as $key => $val) { + if (!isset($lastKnownVersionData[$key]) || $lastKnownVersionData[$key] !== $val) { + $minifiedVersion[$key] = $val; + $lastKnownVersionData[$key] = $val; + } + } + + // store any deletions from the previous version for keys missing in current one + foreach ($lastKnownVersionData as $key => $val) { + if (!isset($version[$key])) { + $minifiedVersion[$key] = "__unset"; + unset($lastKnownVersionData[$key]); + } + } + + $minifiedVersions[] = $minifiedVersion; + } + + return $minifiedVersions; + } +} diff --git a/app/vendor/composer/package-versions-deprecated/CHANGELOG.md b/app/vendor/composer/package-versions-deprecated/CHANGELOG.md new file mode 100644 index 000000000..a838c56ad --- /dev/null +++ b/app/vendor/composer/package-versions-deprecated/CHANGELOG.md @@ -0,0 +1,120 @@ +# CHANGELOG + +## 1.1.3 - 2017-09-06 + +This release fixes a bug that caused PackageVersions to prevent +the `composer remove` and `composer update` commands to fail when +this package is removed. + +In addition to that, mutation testing has been added to the suite, +ensuring that the package is accurately and extensively tested. + +Total issues resolved: **3** + +- [40: Mutation testing, PHP 7.1 testing](https://github.com/Ocramius/PackageVersions/pull/40) thanks to @Ocramius +- [41: Removing this package on install results in file access error](https://github.com/Ocramius/PackageVersions/issues/41) thanks to @Xerkus +- [46: #41 Avoid issues when the package is scheduled for removal](https://github.com/Ocramius/PackageVersions/pull/46) thanks to @Jean85 + +## 1.1.2 - 2016-12-30 + +This release fixes a bug that caused PackageVersions to be enabled +even when it was part of a globally installed package. + +Total issues resolved: **3** + +- [35: remove all temp directories](https://github.com/Ocramius/PackageVersions/pull/35) +- [38: Interferes with other projects when installed globally](https://github.com/Ocramius/PackageVersions/issues/38) +- [39: Ignore the global plugin when updating local projects](https://github.com/Ocramius/PackageVersions/pull/39) + +## 1.1.1 - 2016-07-25 + +This release removes the [`"files"`](https://getcomposer.org/doc/04-schema.md#files) directive from +[`composer.json`](https://github.com/Ocramius/PackageVersions/commit/86f2636f7c5e7b56fa035fa3826d5fcf80b6dc72), +as it is no longer needed for `composer install --classmap-authoritative`. +Also, that directive was causing issues with HHVM installations, since +PackageVersions is not compatible with it. + +Total issues resolved: **1** + +- [34: Fatal error during travis build after update to 1.1.0](https://github.com/Ocramius/PackageVersions/issues/34) + +## 1.1.0 - 2016-07-22 + +This release introduces support for running `composer install --classmap-authoritative` +and `composer install --no-scripts`. Please note that performance +while using these modes may be degraded, but the package will +still work. + +Additionally, the package was tuned to prevent the plugin from +running twice at installation. + +Total issues resolved: **10** + +- [18: Fails when using composer install --no-scripts](https://github.com/Ocramius/PackageVersions/issues/18) +- [20: CS (spacing)](https://github.com/Ocramius/PackageVersions/pull/20) +- [22: Document the way the require-dev section is treated](https://github.com/Ocramius/PackageVersions/issues/22) +- [23: Underline that composer.lock is used as source of information](https://github.com/Ocramius/PackageVersions/pull/23) +- [27: Fix incompatibility with --classmap-authoritative](https://github.com/Ocramius/PackageVersions/pull/27) +- [29: mention optimize-autoloader composer.json config option in README](https://github.com/Ocramius/PackageVersions/pull/29) +- [30: The version class is generated twice during composer update](https://github.com/Ocramius/PackageVersions/issues/30) +- [31: Remove double registration of the event listeners](https://github.com/Ocramius/PackageVersions/pull/31) +- [32: Update the usage of mock APIs to use the new API](https://github.com/Ocramius/PackageVersions/pull/32) +- [33: Fix for #18 - support running with --no-scripts flag](https://github.com/Ocramius/PackageVersions/pull/33) + +## 1.0.4 - 2016-04-23 + +This release includes a fix/workaround for composer/composer#5237, +which causes `ocramius/package-versions` to sometimes generate a +`Versions` class with malformed name (something like +`Versions_composer_tmp0`) when running `composer require `. + +Total issues resolved: **2** + +- [16: Workaround for composer/composer#5237 - class parsing](https://github.com/Ocramius/PackageVersions/pull/16) +- [17: Weird Class name being generated](https://github.com/Ocramius/PackageVersions/issues/17) + +## 1.0.3 - 2016-02-26 + +This release fixes an issue related to concurrent autoloader +re-generation caused by multiple composer plugins being installed. +The issue was solved by removing autoloader re-generation from this +package, but it may still affect other packages. + +It is now recommended that you run `composer dump-autoload --optimize` +after installation when using this particular package. +Please note that `composer (install|update) -o` is not sufficient +to avoid autoload overhead when using this particular package. + +Total issues resolved: **1** + +- [15: Remove autoload re-dump optimization](https://github.com/Ocramius/PackageVersions/pull/15) + +## 1.0.2 - 2016-02-24 + +This release fixes issues related to installing the component without +any dev dependencies or with packages that don't have a source or dist +reference, which is usual with packages defined directly in the +`composer.json`. + +Total issues resolved: **3** + +- [11: fix composer install --no-dev PHP7](https://github.com/Ocramius/PackageVersions/pull/11) +- [12: Packages don't always have a source/reference](https://github.com/Ocramius/PackageVersions/issues/12) +- [13: Fix #12 - support dist and missing package version references](https://github.com/Ocramius/PackageVersions/pull/13) + +## 1.0.1 - 2016-02-01 + +This release fixes an issue related with composer updates to +already installed versions. +Using `composer require` within a package that already used +`ocramius/package-versions` caused the installation to be unable +to write the `PackageVersions\Versions` class to a file. + +Total issues resolved: **6** + +- [2: remove unused use statement](https://github.com/Ocramius/PackageVersions/pull/2) +- [3: Remove useless files from dist package](https://github.com/Ocramius/PackageVersions/pull/3) +- [5: failed to open stream: phar error: write operations disabled by the php.ini setting phar.readonly](https://github.com/Ocramius/PackageVersions/issues/5) +- [6: Fix/#5 use composer vendor dir](https://github.com/Ocramius/PackageVersions/pull/6) +- [7: Hotfix - #5 generate package versions also when in phar context](https://github.com/Ocramius/PackageVersions/pull/7) +- [8: Versions class should be ignored by VCS, as it is an install-time artifact](https://github.com/Ocramius/PackageVersions/pull/8) diff --git a/app/vendor/composer/package-versions-deprecated/CONTRIBUTING.md b/app/vendor/composer/package-versions-deprecated/CONTRIBUTING.md new file mode 100644 index 000000000..718061758 --- /dev/null +++ b/app/vendor/composer/package-versions-deprecated/CONTRIBUTING.md @@ -0,0 +1,39 @@ +--- +title: Contributing +--- + +# Contributing + + * Coding standard for the project is [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) + * The project will follow strict [object calisthenics](http://www.slideshare.net/guilhermeblanco/object-calisthenics-applied-to-php) + * Any contribution must provide tests for additional introduced conditions + * Any un-confirmed issue needs a failing test case before being accepted + * Pull requests must be sent from a new hotfix/feature branch, not from `master`. + +## Installation + +To install the project and run the tests, you need to clone it first: + +```sh +$ git clone git://github.com/Ocramius/PackageVersions.git +``` + +You will then need to run a composer installation: + +```sh +$ cd PackageVersions +$ curl -s https://getcomposer.org/installer | php +$ php composer.phar update +``` + +## Testing + +The PHPUnit version to be used is the one installed as a dev- dependency via composer: + +```sh +$ ./vendor/bin/phpunit +``` + +Accepted coverage for new contributions is 80%. Any contribution not satisfying this requirement +won't be merged. + diff --git a/app/vendor/composer/package-versions-deprecated/LICENSE b/app/vendor/composer/package-versions-deprecated/LICENSE new file mode 100644 index 000000000..a90b0792c --- /dev/null +++ b/app/vendor/composer/package-versions-deprecated/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 Marco Pivetta + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/app/vendor/composer/package-versions-deprecated/README.md b/app/vendor/composer/package-versions-deprecated/README.md new file mode 100644 index 000000000..7fe2097d4 --- /dev/null +++ b/app/vendor/composer/package-versions-deprecated/README.md @@ -0,0 +1,5 @@ +# Package Versions + +**`composer/package-versions-deprecated` is a fully-compatible fork of [`ocramius/package-versions`](https://github.com/Ocramius/PackageVersions)** which provides compatibility with Composer 1 and 2 on PHP 7+. It replaces ocramius/package-versions so if you have a dependency requiring it and you want to use Composer v2 but can not upgrade to PHP 7.4 just yet, you can require this package instead. + +If you have a direct dependency on ocramius/package-versions, we recommend instead that once you migrated to Composer 2 you also migrate to use the `Composer\InstalledVersions` class which offers the functionality present here out of the box. diff --git a/app/vendor/composer/package-versions-deprecated/SECURITY.md b/app/vendor/composer/package-versions-deprecated/SECURITY.md new file mode 100644 index 000000000..da9c516dd --- /dev/null +++ b/app/vendor/composer/package-versions-deprecated/SECURITY.md @@ -0,0 +1,5 @@ +## Security contact information + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure. diff --git a/app/vendor/composer/package-versions-deprecated/composer.json b/app/vendor/composer/package-versions-deprecated/composer.json new file mode 100644 index 000000000..d5a40daac --- /dev/null +++ b/app/vendor/composer/package-versions-deprecated/composer.json @@ -0,0 +1,48 @@ +{ + "name": "composer/package-versions-deprecated", + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "type": "composer-plugin", + "license": "MIT", + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "require": { + "php": "^7 || ^8", + "composer-plugin-api": "^1.1.0 || ^2.0" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "phpunit/phpunit": "^6.5 || ^7", + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13" + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "autoload-dev": { + "psr-4": { + "PackageVersionsTest\\": "test/PackageVersionsTest" + } + }, + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "scripts": { + "post-update-cmd": "PackageVersions\\Installer::dumpVersionsClass", + "post-install-cmd": "PackageVersions\\Installer::dumpVersionsClass" + } +} diff --git a/app/vendor/composer/package-versions-deprecated/composer.lock b/app/vendor/composer/package-versions-deprecated/composer.lock new file mode 100644 index 000000000..b711f6b13 --- /dev/null +++ b/app/vendor/composer/package-versions-deprecated/composer.lock @@ -0,0 +1,2603 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "6bfe0a7d7a51c4bdf14a2d7ea1d22d11", + "packages": [], + "packages-dev": [ + { + "name": "composer/ca-bundle", + "version": "1.2.7", + "source": { + "type": "git", + "url": "https://github.com/composer/ca-bundle.git", + "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/95c63ab2117a72f48f5a55da9740a3273d45b7fd", + "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8", + "psr/log": "^1.0", + "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\CaBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "keywords": [ + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" + ], + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2020-04-08T08:27:21+00:00" + }, + { + "name": "composer/composer", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/composer/composer.git", + "reference": "a8c105da344dd84ebd5d11be7943a45b09dc076f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/composer/zipball/a8c105da344dd84ebd5d11be7943a45b09dc076f", + "reference": "a8c105da344dd84ebd5d11be7943a45b09dc076f", + "shasum": "" + }, + "require": { + "composer/ca-bundle": "^1.0", + "composer/semver": "^1.0", + "composer/spdx-licenses": "^1.2", + "composer/xdebug-handler": "^1.1", + "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0", + "php": "^5.3.2 || ^7.0", + "psr/log": "^1.0", + "seld/jsonlint": "^1.4", + "seld/phar-utils": "^1.0", + "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0", + "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0", + "symfony/finder": "^2.7 || ^3.0 || ^4.0 || ^5.0", + "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0" + }, + "conflict": { + "symfony/console": "2.8.38" + }, + "require-dev": { + "phpspec/prophecy": "^1.10", + "symfony/phpunit-bridge": "^3.4" + }, + "suggest": { + "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", + "ext-zip": "Enabling the zip extension allows you to unzip archives", + "ext-zlib": "Allow gzip compression of HTTP requests" + }, + "bin": [ + "bin/composer" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\": "src/Composer" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.", + "homepage": "https://getcomposer.org/", + "keywords": [ + "autoload", + "dependency", + "package" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/composer/issues", + "source": "https://github.com/composer/composer/tree/master" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2020-03-29T14:59:26+00:00" + }, + { + "name": "composer/semver", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/c6bea70230ef4dd483e6bbcab6005f682ed3a8de", + "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.5 || ^5.0.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/1.5.1" + }, + "time": "2020-01-13T12:06:48+00:00" + }, + { + "name": "composer/spdx-licenses", + "version": "1.5.3", + "source": { + "type": "git", + "url": "https://github.com/composer/spdx-licenses.git", + "reference": "0c3e51e1880ca149682332770e25977c70cf9dae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/0c3e51e1880ca149682332770e25977c70cf9dae", + "reference": "0c3e51e1880ca149682332770e25977c70cf9dae", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Spdx\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "SPDX licenses list and validation library.", + "keywords": [ + "license", + "spdx", + "validator" + ], + "time": "2020-02-14T07:44:31+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7", + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0", + "psr/log": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/master" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + } + ], + "time": "2020-03-01T12:26:26+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "doctrine/coding-standard": "^6.0", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.13", + "phpstan/phpstan-phpunit": "^0.11", + "phpstan/phpstan-shim": "^0.11", + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/master" + }, + "time": "2019-10-21T16:45:58+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "5.2.9", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "44c6787311242a979fa15c704327c20e7221a0e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/44c6787311242a979fa15c704327c20e7221a0e4", + "reference": "44c6787311242a979fa15c704327c20e7221a0e4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/5.2.9" + }, + "time": "2019-09-25T14:49:45+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.9.5", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef", + "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.9.5" + }, + "time": "2020-01-17T21:11:47+00:00" + }, + { + "name": "phar-io/manifest", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^2.0", + "php": "^5.6 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2018-07-08T19:23:20+00:00" + }, + { + "name": "phar-io/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2018-07-08T19:19:57+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", + "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "time": "2018-08-07T13:53:10+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "shasum": "" + }, + "require": { + "ext-filter": "^7.1", + "php": "^7.2", + "phpdocumentor/reflection-common": "^2.0", + "phpdocumentor/type-resolver": "^1.0", + "webmozart/assert": "^1" + }, + "require-dev": { + "doctrine/instantiator": "^1", + "mockery/mockery": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2020-02-22T12:28:44+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95", + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95", + "shasum": "" + }, + "require": { + "php": "^7.2", + "phpdocumentor/reflection-common": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "^7.2", + "mockery/mockery": "~1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/master" + }, + "time": "2020-02-18T18:59:58+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.10.3", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "451c3cd1418cf640de218914901e51b064abb093" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", + "reference": "451c3cd1418cf640de218914901e51b064abb093", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", + "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5 || ^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" + }, + "time": "2020-03-05T15:02:03+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "6.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.1", + "phpunit/php-file-iterator": "^2.0", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^3.0", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^3.1 || ^4.0", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "suggest": { + "ext-xdebug": "^2.6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2018-10-31T16:06:48+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "050bedf145a257b1ff02746c31894800e5122946" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", + "reference": "050bedf145a257b1ff02746c31894800e5122946", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2018-09-13T20:33:42+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "2.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", + "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2019-06-07T04:22:29+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2019-09-17T06:23:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "7.5.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "9467db479d1b0487c99733bb1e7944d32deded2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c", + "reference": "9467db479d1b0487c99733bb1e7944d32deded2c", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "^1.7", + "phar-io/manifest": "^1.0.2", + "phar-io/version": "^2.0", + "php": "^7.1", + "phpspec/prophecy": "^1.7", + "phpunit/php-code-coverage": "^6.0.7", + "phpunit/php-file-iterator": "^2.0.1", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^2.1", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0", + "sebastian/environment": "^4.0", + "sebastian/exporter": "^3.1", + "sebastian/global-state": "^2.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^2.0", + "sebastian/version": "^2.0.1" + }, + "conflict": { + "phpunit/phpunit-mock-objects": "*" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*", + "phpunit/php-invoker": "^2.0" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.5-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2020-01-08T08:45:45+00:00" + }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2017-02-14T16:28:37+00:00" + }, + { + "name": "psr/log", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2020-03-23T09:12:05+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2017-03-04T06:30:41+00:00" + }, + { + "name": "sebastian/comparator", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "shasum": "" + }, + "require": { + "php": "^7.1", + "sebastian/diff": "^3.0", + "sebastian/exporter": "^3.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2018-07-12T15:12:46+00:00" + }, + { + "name": "sebastian/diff", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^2 || ^3.3 || ^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "time": "2019-02-04T06:01:07+00:00" + }, + { + "name": "sebastian/environment", + "version": "4.2.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368", + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.5" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2019-11-20T08:46:58+00:00" + }, + { + "name": "sebastian/exporter", + "version": "3.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2019-09-14T09:02:43+00:00" + }, + { + "name": "sebastian/global-state", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2017-04-27T15:39:26+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2017-08-03T12:35:26+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "773f97c67f28de00d397be301821b06708fca0be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", + "reference": "773f97c67f28de00d397be301821b06708fca0be", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "time": "2017-03-29T09:07:27+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2017-03-03T06:23:57+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2018-10-04T04:07:39+00:00" + }, + { + "name": "sebastian/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2016-10-03T07:35:21+00:00" + }, + { + "name": "seld/jsonlint", + "version": "1.7.2", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19", + "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.7.2" + }, + "time": "2019-10-24T14:27:39+00:00" + }, + { + "name": "seld/phar-utils", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/phar-utils.git", + "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8800503d56b9867d43d9c303b9cbcc26016e82f0", + "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\PharUtils\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "PHAR file format utilities, for when PHP phars you up", + "keywords": [ + "phar" + ], + "support": { + "issues": "https://github.com/Seldaek/phar-utils/issues", + "source": "https://github.com/Seldaek/phar-utils/tree/1.1.0" + }, + "time": "2020-02-14T15:25:33+00:00" + }, + { + "name": "symfony/console", + "version": "v5.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "5fa1caadc8cdaa17bcfb25219f3b53fe294a9935" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/5fa1caadc8cdaa17bcfb25219f3b53fe294a9935", + "reference": "5fa1caadc8cdaa17bcfb25219f3b53fe294a9935", + "shasum": "" + }, + "require": { + "php": "^7.2.5", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/service-contracts": "^1.1|^2" + }, + "conflict": { + "symfony/dependency-injection": "<4.4", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "^4.4|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/event-dispatcher": "^4.4|^5.0", + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^4.4|^5.0", + "symfony/var-dumper": "^4.4|^5.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/console/tree/v5.0.7" + }, + "time": "2020-03-30T11:42:42+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v5.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "ca3b87dd09fff9b771731637f5379965fbfab420" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ca3b87dd09fff9b771731637f5379965fbfab420", + "reference": "ca3b87dd09fff9b771731637f5379965fbfab420", + "shasum": "" + }, + "require": { + "php": "^7.2.5", + "symfony/polyfill-ctype": "~1.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.0.7" + }, + "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": "2020-03-27T16:56:45+00:00" + }, + { + "name": "symfony/finder", + "version": "v5.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "600a52c29afc0d1caa74acbec8d3095ca7e9910d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/600a52c29afc0d1caa74acbec8d3095ca7e9910d", + "reference": "600a52c29afc0d1caa74acbec8d3095ca7e9910d", + "shasum": "" + }, + "require": { + "php": "^7.2.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/5.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": "2020-03-27T16:56:45+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.15.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": "2020-02-27T09:26:54+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "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 for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.15.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": "2020-03-09T19:04:49+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", + "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "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 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.15.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": "2020-02-27T09:26:54+00:00" + }, + { + "name": "symfony/process", + "version": "v5.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "c5ca4a0fc16a0c888067d43fbcfe1f8a53d8e70e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/c5ca4a0fc16a0c888067d43fbcfe1f8a53d8e70e", + "reference": "c5ca4a0fc16a0c888067d43fbcfe1f8a53d8e70e", + "shasum": "" + }, + "require": { + "php": "^7.2.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.0.7" + }, + "time": "2020-03-27T16:56:45+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "144c5e51266b281231e947b51223ba14acf1a749" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/144c5e51266b281231e947b51223ba14acf1a749", + "reference": "144c5e51266b281231e947b51223ba14acf1a749", + "shasum": "" + }, + "require": { + "php": "^7.2.5", + "psr/container": "^1.0" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "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": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.0.1" + }, + "time": "2019-11-18T17:27:11+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2019-06-13T22:48:21+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/ab2cb0b3b559010b75981b1bdce728da3ee90ad6", + "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "vimeo/psalm": "<3.9.1" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36 || ^7.5.13" + }, + "type": "library", + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozart/assert/issues", + "source": "https://github.com/webmozart/assert/tree/master" + }, + "time": "2020-04-18T12:12:48+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "composer/composer": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^7", + "composer-plugin-api": "^1.1.0 || ^2.0" + }, + "platform-dev": { + "ext-zip": "^1.13" + }, + "plugin-api-version": "1.1.0" +} diff --git a/app/vendor/composer/package-versions-deprecated/src/PackageVersions/FallbackVersions.php b/app/vendor/composer/package-versions-deprecated/src/PackageVersions/FallbackVersions.php new file mode 100644 index 000000000..18e5fe64f --- /dev/null +++ b/app/vendor/composer/package-versions-deprecated/src/PackageVersions/FallbackVersions.php @@ -0,0 +1,128 @@ + + */ + private static function getVersions(array $packageData): Generator + { + foreach ($packageData as $package) { + yield $package['name'] => $package['version'] . '@' . ( + $package['source']['reference'] ?? $package['dist']['reference'] ?? '' + ); + } + + yield self::ROOT_PACKAGE_NAME => self::ROOT_PACKAGE_NAME; + } +} diff --git a/app/vendor/composer/package-versions-deprecated/src/PackageVersions/Installer.php b/app/vendor/composer/package-versions-deprecated/src/PackageVersions/Installer.php new file mode 100644 index 000000000..05bdac9a7 --- /dev/null +++ b/app/vendor/composer/package-versions-deprecated/src/PackageVersions/Installer.php @@ -0,0 +1,290 @@ + + * @internal + */ + const VERSIONS = %s; + + private function __construct() + { + } + + /** + * @psalm-pure + * + * @psalm-suppress ImpureMethodCall we know that {@see InstalledVersions} interaction does not + * cause any side effects here. + */ + public static function rootPackageName() : string + { + if (!self::composer2ApiUsable()) { + return self::ROOT_PACKAGE_NAME; + } + + return InstalledVersions::getRootPackage()['name']; + } + + /** + * @throws OutOfBoundsException If a version cannot be located. + * + * @psalm-param key-of $packageName + * @psalm-pure + * + * @psalm-suppress ImpureMethodCall we know that {@see InstalledVersions} interaction does not + * cause any side effects here. + */ + public static function getVersion(string $packageName): string + { + if (self::composer2ApiUsable()) { + return InstalledVersions::getPrettyVersion($packageName) + . '@' . InstalledVersions::getReference($packageName); + } + + if (isset(self::VERSIONS[$packageName])) { + return self::VERSIONS[$packageName]; + } + + throw new OutOfBoundsException( + 'Required package "' . $packageName . '" is not installed: check your ./vendor/composer/installed.json and/or ./composer.lock files' + ); + } + + private static function composer2ApiUsable(): bool + { + if (!class_exists(InstalledVersions::class, false)) { + return false; + } + + if (method_exists(InstalledVersions::class, 'getAllRawData')) { + $rawData = InstalledVersions::getAllRawData(); + if (count($rawData) === 1 && count($rawData[0]) === 0) { + return false; + } + } else { + $rawData = InstalledVersions::getRawData(); + if ($rawData === null || $rawData === []) { + return false; + } + } + + return true; + } +} + +PHP; + + public function activate(Composer $composer, IOInterface $io) + { + // Nothing to do here, as all features are provided through event listeners + } + + public function deactivate(Composer $composer, IOInterface $io) + { + // Nothing to do here, as all features are provided through event listeners + } + + public function uninstall(Composer $composer, IOInterface $io) + { + // Nothing to do here, as all features are provided through event listeners + } + + /** + * {@inheritDoc} + */ + public static function getSubscribedEvents(): array + { + return [ScriptEvents::POST_AUTOLOAD_DUMP => 'dumpVersionsClass']; + } + + /** + * @throws RuntimeException + */ + public static function dumpVersionsClass(Event $composerEvent) + { + $composer = $composerEvent->getComposer(); + $rootPackage = $composer->getPackage(); + $versions = iterator_to_array(self::getVersions($composer->getLocker(), $rootPackage)); + + if (! array_key_exists('composer/package-versions-deprecated', $versions)) { + //plugin must be globally installed - we only want to generate versions for projects which specifically + //require composer/package-versions-deprecated + return; + } + + $versionClass = self::generateVersionsClass($rootPackage->getName(), $versions); + + self::writeVersionClassToFile($versionClass, $composer, $composerEvent->getIO()); + } + + /** + * @param string[] $versions + */ + private static function generateVersionsClass(string $rootPackageName, array $versions): string + { + return sprintf( + self::$generatedClassTemplate, + 'fin' . 'al ' . 'cla' . 'ss ' . 'Versions', // note: workaround for regex-based code parsers :-( + $rootPackageName, + var_export($versions, true) + ); + } + + /** + * @throws RuntimeException + */ + private static function writeVersionClassToFile(string $versionClassSource, Composer $composer, IOInterface $io) + { + $installPath = self::locateRootPackageInstallPath($composer->getConfig(), $composer->getPackage()) + . '/src/PackageVersions/Versions.php'; + + $installDir = dirname($installPath); + if (! file_exists($installDir)) { + $io->write('composer/package-versions-deprecated: Package not found (probably scheduled for removal); generation of version class skipped.'); + + return; + } + + if (! is_writable($installDir)) { + $io->write( + sprintf( + 'composer/package-versions-deprecated: %s is not writable; generation of version class skipped.', + $installDir + ) + ); + + return; + } + + $io->write('composer/package-versions-deprecated: Generating version class...'); + + $installPathTmp = $installPath . '_' . uniqid('tmp', true); + file_put_contents($installPathTmp, $versionClassSource); + chmod($installPathTmp, 0664); + rename($installPathTmp, $installPath); + + $io->write('composer/package-versions-deprecated: ...done generating version class'); + } + + /** + * @throws RuntimeException + */ + private static function locateRootPackageInstallPath( + Config $composerConfig, + RootPackageInterface $rootPackage + ): string { + if (self::getRootPackageAlias($rootPackage)->getName() === 'composer/package-versions-deprecated') { + return dirname($composerConfig->get('vendor-dir')); + } + + return $composerConfig->get('vendor-dir') . '/composer/package-versions-deprecated'; + } + + private static function getRootPackageAlias(RootPackageInterface $rootPackage): PackageInterface + { + $package = $rootPackage; + + while ($package instanceof AliasPackage) { + $package = $package->getAliasOf(); + } + + return $package; + } + + /** + * @return Generator&string[] + * + * @psalm-return Generator + */ + private static function getVersions(Locker $locker, RootPackageInterface $rootPackage): Generator + { + $lockData = $locker->getLockData(); + + $lockData['packages-dev'] = $lockData['packages-dev'] ?? []; + + $packages = $lockData['packages']; + if (getenv('COMPOSER_DEV_MODE') !== '0') { + $packages = array_merge($packages, $lockData['packages-dev']); + } + foreach ($packages as $package) { + yield $package['name'] => $package['version'] . '@' . ( + $package['source']['reference'] ?? $package['dist']['reference'] ?? '' + ); + } + + foreach ($rootPackage->getReplaces() as $replace) { + $version = $replace->getPrettyConstraint(); + if ($version === 'self.version') { + $version = $rootPackage->getPrettyVersion(); + } + + yield $replace->getTarget() => $version . '@' . $rootPackage->getSourceReference(); + } + + yield $rootPackage->getName() => $rootPackage->getPrettyVersion() . '@' . $rootPackage->getSourceReference(); + } +} diff --git a/app/vendor/composer/package-versions-deprecated/src/PackageVersions/Versions.php b/app/vendor/composer/package-versions-deprecated/src/PackageVersions/Versions.php new file mode 100644 index 000000000..ac9bb764d --- /dev/null +++ b/app/vendor/composer/package-versions-deprecated/src/PackageVersions/Versions.php @@ -0,0 +1,203 @@ + + * @internal + */ + const VERSIONS = array ( + 'adodb/adodb-php' => 'v5.21.2@aa161c28dfffcab13a3cb9bd407917573987f0f6', + 'cakephp/cakephp' => '4.2.10@ea47e927083e2f4bfaed0c404756d9852b617cad', + 'cakephp/chronos' => '2.2.0@556e14da67307ffc2e68beeb7df0694dc8d1207d', + 'cakephp/migrations' => '3.1.0@d22737c31243db89774abfe6a077d254c0eae75a', + 'cakephp/plugin-installer' => '1.3.1@e27027aa2d3d8ab64452c6817629558685a064cb', + 'composer/ca-bundle' => '1.2.11@0b072d51c5a9c6f3412f7ea3ab043d6603cb2582', + 'composer/package-versions-deprecated' => '1.11.99.4@b174585d1fe49ceed21928a945138948cb394600', + 'doctrine/cache' => '2.1.1@331b4d5dbaeab3827976273e9356b3b453c300ce', + 'doctrine/dbal' => '3.1.3@96b0053775a544b4a6ab47654dac0621be8b4cf8', + 'doctrine/deprecations' => 'v0.5.3@9504165960a1f83cc1480e2be1dd0a0478561314', + 'doctrine/event-manager' => '1.1.1@41370af6a30faa9dc0368c4a6814d596e81aba7f', + 'laminas/laminas-diactoros' => '2.8.0@0c26ef1d95b6d7e6e3943a243ba3dc0797227199', + 'laminas/laminas-httphandlerrunner' => '1.5.0@5f94e55d93f756e8ad07b9049aeb3d6d84582d0e', + 'laminas/laminas-zendframework-bridge' => '1.4.0@bf180a382393e7db5c1e8d0f2ec0c4af9c724baf', + 'league/container' => '3.4.1@84ecbc2dbecc31bd23faf759a0e329ee49abddbd', + 'mobiledetect/mobiledetectlib' => '2.8.37@9841e3c46f5bd0739b53aed8ac677fa712943df7', + 'psr/container' => '1.1.1@8622567409010282b7aeebe4bb841fe98b58dcaf', + 'psr/http-client' => '1.0.1@2dfb5f6c5eff0e91e20e913f8c5452ed95b86621', + 'psr/http-factory' => '1.0.1@12ac7fcd07e5b077433f5f2bee95b3a771bf61be', + 'psr/http-message' => '1.0.1@f6561bf28d520154e4b0ec72be95418abe6d9363', + 'psr/http-server-handler' => '1.0.1@aff2f80e33b7f026ec96bb42f63242dc50ffcae7', + 'psr/http-server-middleware' => '1.0.1@2296f45510945530b9dceb8bcedb5cb84d40c5f5', + 'psr/log' => '1.1.4@d49695b909c3b7628b6289db5479a1c204601f11', + 'psr/simple-cache' => '1.0.1@408d5eafb83c57f6365a3ca330ff23aa4a5fa39b', + 'robmorgan/phinx' => '0.12.9@5a0146a74c1bc195d1f5da86afa3b68badf7d90e', + 'symfony/config' => 'v5.3.4@4268f3059c904c61636275182707f81645517a37', + 'symfony/console' => 'v5.3.7@8b1008344647462ae6ec57559da166c2bfa5e16a', + 'symfony/deprecation-contracts' => 'v2.4.0@5f38c8804a9e97d23e0c8d63341088cd8a22d627', + 'symfony/filesystem' => 'v5.3.4@343f4fe324383ca46792cae728a3b6e2f708fb32', + 'symfony/polyfill-ctype' => 'v1.23.0@46cd95797e9df938fdd2b03693b5fca5e64b01ce', + 'symfony/polyfill-intl-grapheme' => 'v1.23.1@16880ba9c5ebe3642d1995ab866db29270b36535', + 'symfony/polyfill-intl-normalizer' => 'v1.23.0@8590a5f561694770bdcd3f9b5c69dde6945028e8', + 'symfony/polyfill-mbstring' => 'v1.23.1@9174a3d80210dca8daa7f31fec659150bbeabfc6', + 'symfony/polyfill-php73' => 'v1.23.0@fba8933c384d6476ab14fb7b8526e5287ca7e010', + 'symfony/polyfill-php80' => 'v1.23.1@1100343ed1a92e3a38f9ae122fc0eb21602547be', + 'symfony/polyfill-php81' => 'v1.23.0@e66119f3de95efc359483f810c4c3e6436279436', + 'symfony/service-contracts' => 'v2.4.0@f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb', + 'symfony/string' => 'v5.3.7@8d224396e28d30f81969f083a58763b8b9ceb0a5', + 'brick/varexporter' => '0.3.5@05241f28dfcba2b51b11e2d750e296316ebbe518', + 'cakephp/bake' => '2.5.2@bfb856afcfbc70c5cf5341669c3036a45ca15d94', + 'cakephp/cakephp-codesniffer' => '4.2.4@c5bb1faeebf09cd4a3604bdb0c84f7bc92dc5475', + 'cakephp/debug_kit' => '4.4.4@10d7d9ba36945844211f1d8763e59618917e1784', + 'cakephp/twig-view' => '1.3.0@14df50360b809a171d0688020fbdfe513763f89b', + 'composer/composer' => '2.1.9@e558c88f28d102d497adec4852802c0dc14c7077', + 'composer/metadata-minifier' => '1.0.0@c549d23829536f0d0e984aaabbf02af91f443207', + 'composer/semver' => '3.2.5@31f3ea725711245195f62e54ffa402d8ef2fdba9', + 'composer/spdx-licenses' => '1.5.5@de30328a7af8680efdc03e396aad24befd513200', + 'composer/xdebug-handler' => '2.0.2@84674dd3a7575ba617f5a76d7e9e29a7d3891339', + 'dealerdirect/phpcodesniffer-composer-installer' => 'v0.7.1@fe390591e0241955f22eb9ba327d137e501c771c', + 'doctrine/instantiator' => '1.4.0@d56bf6102915de5702778fe20f2de3b2fe570b5b', + 'jasny/twig-extensions' => 'v1.3.0@a694eb02f6fc14ff8e2fceb8b80882c0c926102b', + 'jdorn/sql-formatter' => 'v1.2.17@64990d96e0959dff8e059dfcdc1af130728d92bc', + 'josegonzalez/dotenv' => '3.2.0@f19174d9d7213a6c20e8e5e268aa7dd042d821ca', + 'justinrainbow/json-schema' => '5.2.11@2ab6744b7296ded80f8cc4f9509abbff393399aa', + 'm1/env' => '2.2.0@5c296e3e13450a207e12b343f3af1d7ab569f6f3', + 'myclabs/deep-copy' => '1.10.2@776f831124e9c62e1a2c601ecc52e776d8bb7220', + 'nikic/php-parser' => 'v4.13.0@50953a2691a922aa1769461637869a0a2faa3f53', + 'phar-io/manifest' => '2.0.3@97803eca37d319dfa7826cc2437fc020857acb53', + 'phar-io/version' => '3.1.0@bae7c545bef187884426f042434e561ab1ddb182', + 'phpdocumentor/reflection-common' => '2.2.0@1d01c49d4ed62f25aa84a747ad35d5a16924662b', + 'phpdocumentor/reflection-docblock' => '5.2.2@069a785b2141f5bcf49f3e353548dc1cce6df556', + 'phpdocumentor/type-resolver' => '1.5.1@a12f7e301eb7258bb68acd89d4aefa05c2906cae', + 'phpspec/prophecy' => '1.14.0@d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e', + 'phpstan/phpdoc-parser' => '0.4.9@98a088b17966bdf6ee25c8a4b634df313d8aa531', + 'phpstan/phpstan' => '0.12.99@b4d40f1d759942f523be267a1bab6884f46ca3f7', + 'phpunit/php-code-coverage' => '9.2.7@d4c798ed8d51506800b441f7a13ecb0f76f12218', + 'phpunit/php-file-iterator' => '3.0.5@aa4be8575f26070b100fccb67faabb28f21f66f8', + 'phpunit/php-invoker' => '3.1.1@5a10147d0aaf65b58940a0b72f71c9ac0423cc67', + 'phpunit/php-text-template' => '2.0.4@5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28', + 'phpunit/php-timer' => '5.0.3@5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2', + 'phpunit/phpunit' => '9.5.10@c814a05837f2edb0d1471d6e3f4ab3501ca3899a', + 'psy/psysh' => 'v0.10.9@01281336c4ae557fe4a994544f30d3a1bc204375', + 'react/promise' => 'v2.8.0@f3cff96a19736714524ca0dd1d4130de73dbbbc4', + 'sebastian/cli-parser' => '1.0.1@442e7c7e687e42adc03470c7b668bc4b2402c0b2', + 'sebastian/code-unit' => '1.0.8@1fc9f64c0927627ef78ba436c9b17d967e68e120', + 'sebastian/code-unit-reverse-lookup' => '2.0.3@ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5', + 'sebastian/comparator' => '4.0.6@55f4261989e546dc112258c7a75935a81a7ce382', + 'sebastian/complexity' => '2.0.2@739b35e53379900cc9ac327b2147867b8b6efd88', + 'sebastian/diff' => '4.0.4@3461e3fccc7cfdfc2720be910d3bd73c69be590d', + 'sebastian/environment' => '5.1.3@388b6ced16caa751030f6a69e588299fa09200ac', + 'sebastian/exporter' => '4.0.3@d89cc98761b8cb5a1a235a6b703ae50d34080e65', + 'sebastian/global-state' => '5.0.3@23bd5951f7ff26f12d4e3242864df3e08dec4e49', + 'sebastian/lines-of-code' => '1.0.3@c1c2e997aa3146983ed888ad08b15470a2e22ecc', + 'sebastian/object-enumerator' => '4.0.4@5c9eeac41b290a3712d88851518825ad78f45c71', + 'sebastian/object-reflector' => '2.0.4@b4f479ebdbf63ac605d183ece17d8d7fe49c15c7', + 'sebastian/recursion-context' => '4.0.4@cd9d8cf3c5804de4341c283ed787f099f5506172', + 'sebastian/resource-operations' => '3.0.3@0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8', + 'sebastian/type' => '2.3.4@b8cd8a1c753c90bc1a0f5372170e3e489136f914', + 'sebastian/version' => '3.0.2@c6c1022351a901512170118436c764e473f6de8c', + 'seld/jsonlint' => '1.8.3@9ad6ce79c342fbd44df10ea95511a1b24dee5b57', + 'seld/phar-utils' => '1.1.2@749042a2315705d2dfbbc59234dd9ceb22bf3ff0', + 'slevomat/coding-standard' => '6.4.1@696dcca217d0c9da2c40d02731526c1e25b65346', + 'squizlabs/php_codesniffer' => '3.5.8@9d583721a7157ee997f235f327de038e7ea6dac4', + 'symfony/finder' => 'v5.3.7@a10000ada1e600d109a6c7632e9ac42e8bf2fb93', + 'symfony/process' => 'v5.3.7@38f26c7d6ed535217ea393e05634cb0b244a1967', + 'symfony/var-dumper' => 'v5.3.8@eaaea4098be1c90c8285543e1356a09c8aa5c8da', + 'theseer/tokenizer' => '1.2.1@34a41e998c2183e22995f158c581e7b5e755ab9e', + 'twig/markdown-extra' => 'v3.3.3@725a4ef89d93bb80fc63c67cf261bf7512649290', + 'twig/twig' => 'v3.3.3@a27fa056df8a6384316288ca8b0fa3a35fdeb569', + 'webmozart/assert' => '1.10.0@6964c76c7804814a842473e0c8fd15bab0f18e25', + 'cakephp/app' => 'dev-develop@9cabecad7c9472d0821f2e2839dcddb3bf0657e7', +); + + private function __construct() + { + } + + /** + * @psalm-pure + * + * @psalm-suppress ImpureMethodCall we know that {@see InstalledVersions} interaction does not + * cause any side effects here. + */ + public static function rootPackageName() : string + { + if (!self::composer2ApiUsable()) { + return self::ROOT_PACKAGE_NAME; + } + + return InstalledVersions::getRootPackage()['name']; + } + + /** + * @throws OutOfBoundsException If a version cannot be located. + * + * @psalm-param key-of $packageName + * @psalm-pure + * + * @psalm-suppress ImpureMethodCall we know that {@see InstalledVersions} interaction does not + * cause any side effects here. + */ + public static function getVersion(string $packageName): string + { + if (self::composer2ApiUsable()) { + return InstalledVersions::getPrettyVersion($packageName) + . '@' . InstalledVersions::getReference($packageName); + } + + if (isset(self::VERSIONS[$packageName])) { + return self::VERSIONS[$packageName]; + } + + throw new OutOfBoundsException( + 'Required package "' . $packageName . '" is not installed: check your ./vendor/composer/installed.json and/or ./composer.lock files' + ); + } + + private static function composer2ApiUsable(): bool + { + if (!class_exists(InstalledVersions::class, false)) { + return false; + } + + if (method_exists(InstalledVersions::class, 'getAllRawData')) { + $rawData = InstalledVersions::getAllRawData(); + if (count($rawData) === 1 && count($rawData[0]) === 0) { + return false; + } + } else { + $rawData = InstalledVersions::getRawData(); + if ($rawData === null || $rawData === []) { + return false; + } + } + + return true; + } +} diff --git a/app/vendor/composer/semver/CHANGELOG.md b/app/vendor/composer/semver/CHANGELOG.md index ba272c48a..67785e4d3 100644 --- a/app/vendor/composer/semver/CHANGELOG.md +++ b/app/vendor/composer/semver/CHANGELOG.md @@ -3,6 +3,61 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +### [3.2.5] 2021-05-24 + + * Fixed: issue comparing disjunctive MultiConstraints to conjunctive ones (#127) + * Fixed: added complete type information using phpstan annotations + +### [3.2.4] 2020-11-13 + + * Fixed: code clean-up + +### [3.2.3] 2020-11-12 + + * Fixed: constraints in the form of `X || Y, >=Y.1` and other such complex constructs were in some cases being optimized into a more restrictive constraint + +### [3.2.2] 2020-10-14 + + * Fixed: internal code cleanups + +### [3.2.1] 2020-09-27 + + * Fixed: accidental validation of broken constraints combining ^/~ and wildcards, and -dev suffix allowing weird cases + * Fixed: normalization of beta0 and such which was dropping the 0 + +### [3.2.0] 2020-09-09 + + * Added: support for `x || @dev`, not very useful but seen in the wild and failed to validate with 1.5.2/1.6.0 + * Added: support for `foobar-dev` being equal to `dev-foobar`, dev-foobar is the official way to write it but we need to support the other for BC and convenience + +### [3.1.0] 2020-09-08 + + * Added: support for constraints like `^2.x-dev` and `~2.x-dev`, not very useful but seen in the wild and failed to validate with 3.0.1 + * Fixed: invalid aliases will no longer throw, unless explicitly validated by Composer in the root package + +### [3.0.1] 2020-09-08 + + * Fixed: handling of some invalid -dev versions which were seen as valid + +### [3.0.0] 2020-05-26 + + * Break: Renamed `EmptyConstraint`, replace it with `MatchAllConstraint` + * Break: Unlikely to affect anyone but strictly speaking a breaking change, `*.*` and such variants will not match all `dev-*` versions anymore, only `*` does + * Break: ConstraintInterface is now considered internal/private and not meant to be implemented by third parties anymore + * Added `Intervals` class to check if a constraint is a subsets of another one, and allow compacting complex MultiConstraints into simpler ones + * Added `CompilingMatcher` class to speed up constraint matching against simple Constraint instances + * Added `MatchAllConstraint` and `MatchNoneConstraint` which match everything and nothing + * Added more advanced optimization of contiguous constraints inside MultiConstraint + * Added tentative support for PHP 8 + * Fixed ConstraintInterface::matches to be commutative in all cases + +### [2.0.0] 2020-04-21 + + * Break: `dev-master`, `dev-trunk` and `dev-default` now normalize to `dev-master`, `dev-trunk` and `dev-default` instead of `9999999-dev` in 1.x + * Break: Removed the deprecated `AbstractConstraint` + * Added `getUpperBound` and `getLowerBound` to ConstraintInterface. They return `Composer\Semver\Constraint\Bound` instances + * Added `MultiConstraint::create` to create the most-optimal form of ConstraintInterface from an array of constraint strings + ### [1.7.2] 2020-12-03 * Fixed: Allow installing on php 8 @@ -90,6 +145,16 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Namespace: `Composer\Test\Package\LinkConstraint` -> `Composer\Test\Semver\Constraint` * Changed: code style using php-cs-fixer. +[3.2.5]: https://github.com/composer/semver/compare/3.2.4...3.2.5 +[3.2.4]: https://github.com/composer/semver/compare/3.2.3...3.2.4 +[3.2.3]: https://github.com/composer/semver/compare/3.2.2...3.2.3 +[3.2.2]: https://github.com/composer/semver/compare/3.2.1...3.2.2 +[3.2.1]: https://github.com/composer/semver/compare/3.2.0...3.2.1 +[3.2.0]: https://github.com/composer/semver/compare/3.1.0...3.2.0 +[3.1.0]: https://github.com/composer/semver/compare/3.0.1...3.1.0 +[3.0.1]: https://github.com/composer/semver/compare/3.0.0...3.0.1 +[3.0.0]: https://github.com/composer/semver/compare/2.0.0...3.0.0 +[2.0.0]: https://github.com/composer/semver/compare/1.5.1...2.0.0 [1.7.2]: https://github.com/composer/semver/compare/1.7.1...1.7.2 [1.7.1]: https://github.com/composer/semver/compare/1.7.0...1.7.1 [1.7.0]: https://github.com/composer/semver/compare/1.6.0...1.7.0 diff --git a/app/vendor/composer/semver/README.md b/app/vendor/composer/semver/README.md index 409b9dcba..99bc46151 100644 --- a/app/vendor/composer/semver/README.md +++ b/app/vendor/composer/semver/README.md @@ -6,7 +6,7 @@ Semver library that offers utilities, version constraint parsing and validation. Originally written as part of [composer/composer](https://github.com/composer/composer), now extracted and made available as a stand-alone library. -[![Build Status](https://travis-ci.org/composer/semver.svg?branch=master)](https://travis-ci.org/composer/semver) +[![Continuous Integration](https://github.com/composer/semver/workflows/Continuous%20Integration/badge.svg?branch=main)](https://github.com/composer/semver/actions) Installation @@ -37,7 +37,7 @@ Basic usage ### Comparator -The `Composer\Semver\Comparator` class provides the following methods for comparing versions: +The [`Composer\Semver\Comparator`](https://github.com/composer/semver/blob/main/src/Comparator.php) class provides the following methods for comparing versions: * greaterThan($v1, $v2) * greaterThanOrEqualTo($v1, $v2) @@ -56,13 +56,41 @@ Comparator::greaterThan('1.25.0', '1.24.0'); // 1.25.0 > 1.24.0 ### Semver -The `Composer\Semver\Semver` class provides the following methods: +The [`Composer\Semver\Semver`](https://github.com/composer/semver/blob/main/src/Semver.php) class provides the following methods: * satisfies($version, $constraints) * satisfiedBy(array $versions, $constraint) * sort($versions) * rsort($versions) +### Intervals + +The [`Composer\Semver\Intervals`](https://github.com/composer/semver/blob/main/src/Intervals.php) static class provides +a few utilities to work with complex constraints or read version intervals from a constraint: + +```php +use Composer\Semver\Intervals; + +// Checks whether $candidate is a subset of $constraint +Intervals::isSubsetOf(ConstraintInterface $candidate, ConstraintInterface $constraint); + +// Checks whether $a and $b have any intersection, equivalent to $a->matches($b) +Intervals::haveIntersections(ConstraintInterface $a, ConstraintInterface $b); + +// Optimizes a complex multi constraint by merging all intervals down to the smallest +// possible multi constraint. The drawbacks are this is not very fast, and the resulting +// multi constraint will have no human readable prettyConstraint configured on it +Intervals::compactConstraint(ConstraintInterface $constraint); + +// Creates an array of numeric intervals and branch constraints representing a given constraint +Intervals::get(ConstraintInterface $constraint); + +// Clears the memoization cache when you are done processing constraints +Intervals::clear() +``` + +See the class docblocks for more details. + License ------- diff --git a/app/vendor/composer/semver/composer.json b/app/vendor/composer/semver/composer.json index 0d9559f16..030df2e32 100644 --- a/app/vendor/composer/semver/composer.json +++ b/app/vendor/composer/semver/composer.json @@ -34,7 +34,8 @@ "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5" + "symfony/phpunit-bridge": "^4.2 || ^5", + "phpstan/phpstan": "^0.12.54" }, "autoload": { "psr-4": { @@ -48,10 +49,11 @@ }, "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "3.x-dev" } }, "scripts": { - "test": "phpunit" + "test": "SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT=1 vendor/bin/simple-phpunit", + "phpstan": "phpstan analyse" } } diff --git a/app/vendor/composer/semver/src/Comparator.php b/app/vendor/composer/semver/src/Comparator.php index a9d758f1c..38f483aa6 100644 --- a/app/vendor/composer/semver/src/Comparator.php +++ b/app/vendor/composer/semver/src/Comparator.php @@ -101,11 +101,13 @@ public static function notEqualTo($version1, $version2) * @param string $version2 * * @return bool + * + * @phpstan-param Constraint::STR_OP_* $operator */ public static function compare($version1, $operator, $version2) { $constraint = new Constraint($operator, $version2); - return $constraint->matches(new Constraint('==', $version1)); + return $constraint->matchSpecific(new Constraint('==', $version1), true); } } diff --git a/app/vendor/composer/semver/src/CompilingMatcher.php b/app/vendor/composer/semver/src/CompilingMatcher.php new file mode 100644 index 000000000..f1186670d --- /dev/null +++ b/app/vendor/composer/semver/src/CompilingMatcher.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver; + +use Composer\Semver\Constraint\Constraint; +use Composer\Semver\Constraint\ConstraintInterface; + +/** + * Helper class to evaluate constraint by compiling and reusing the code to evaluate + */ +class CompilingMatcher +{ + /** + * @var array + * @phpstan-var array + */ + private static $compiledCheckerCache = array(); + /** @var bool */ + private static $enabled; + + /** + * @phpstan-var array + */ + private static $transOpInt = array( + Constraint::OP_EQ => Constraint::STR_OP_EQ, + Constraint::OP_LT => Constraint::STR_OP_LT, + Constraint::OP_LE => Constraint::STR_OP_LE, + Constraint::OP_GT => Constraint::STR_OP_GT, + Constraint::OP_GE => Constraint::STR_OP_GE, + Constraint::OP_NE => Constraint::STR_OP_NE, + ); + + /** + * Evaluates the expression: $constraint match $operator $version + * + * @param ConstraintInterface $constraint + * @param int $operator + * @phpstan-param Constraint::OP_* $operator + * @param string $version + * + * @return mixed + */ + public static function match(ConstraintInterface $constraint, $operator, $version) + { + if (self::$enabled === null) { + self::$enabled = !\in_array('eval', explode(',', (string) ini_get('disable_functions')), true); + } + if (!self::$enabled) { + return $constraint->matches(new Constraint(self::$transOpInt[$operator], $version)); + } + + $cacheKey = $operator.$constraint; + if (!isset(self::$compiledCheckerCache[$cacheKey])) { + $code = $constraint->compile($operator); + self::$compiledCheckerCache[$cacheKey] = $function = eval('return function($v, $b){return '.$code.';};'); + } else { + $function = self::$compiledCheckerCache[$cacheKey]; + } + + return $function($version, strpos($version, 'dev-') === 0); + } +} diff --git a/app/vendor/composer/semver/src/Constraint/AbstractConstraint.php b/app/vendor/composer/semver/src/Constraint/AbstractConstraint.php deleted file mode 100644 index 7b5270fa3..000000000 --- a/app/vendor/composer/semver/src/Constraint/AbstractConstraint.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Composer\Semver\Constraint; - -trigger_error('The ' . __NAMESPACE__ . '\AbstractConstraint abstract class is deprecated, there is no replacement for it, it will be removed in the next major version.', E_USER_DEPRECATED); - -/** - * Base constraint class. - */ -abstract class AbstractConstraint implements ConstraintInterface -{ - /** @var string */ - protected $prettyString; - - /** - * @param ConstraintInterface $provider - * - * @return bool - */ - public function matches(ConstraintInterface $provider) - { - if ($provider instanceof $this) { - // see note at bottom of this class declaration - return $this->matchSpecific($provider); - } - - // turn matching around to find a match - return $provider->matches($this); - } - - /** - * @param string $prettyString - */ - public function setPrettyString($prettyString) - { - $this->prettyString = $prettyString; - } - - /** - * @return string - */ - public function getPrettyString() - { - if ($this->prettyString) { - return $this->prettyString; - } - - return $this->__toString(); - } - - // implementations must implement a method of this format: - // not declared abstract here because type hinting violates parameter coherence (TODO right word?) - // public function matchSpecific( $provider); -} diff --git a/app/vendor/composer/semver/src/Constraint/Bound.php b/app/vendor/composer/semver/src/Constraint/Bound.php new file mode 100644 index 000000000..7effb11a8 --- /dev/null +++ b/app/vendor/composer/semver/src/Constraint/Bound.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver\Constraint; + +class Bound +{ + /** + * @var string + */ + private $version; + + /** + * @var bool + */ + private $isInclusive; + + /** + * @param string $version + * @param bool $isInclusive + */ + public function __construct($version, $isInclusive) + { + $this->version = $version; + $this->isInclusive = $isInclusive; + } + + /** + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * @return bool + */ + public function isInclusive() + { + return $this->isInclusive; + } + + /** + * @return bool + */ + public function isZero() + { + return $this->getVersion() === '0.0.0.0-dev' && $this->isInclusive(); + } + + /** + * @return bool + */ + public function isPositiveInfinity() + { + return $this->getVersion() === PHP_INT_MAX.'.0.0.0' && !$this->isInclusive(); + } + + /** + * Compares a bound to another with a given operator. + * + * @param Bound $other + * @param string $operator + * + * @return bool + */ + public function compareTo(Bound $other, $operator) + { + if (!\in_array($operator, array('<', '>'), true)) { + throw new \InvalidArgumentException('Does not support any other operator other than > or <.'); + } + + // If they are the same it doesn't matter + if ($this == $other) { + return false; + } + + $compareResult = version_compare($this->getVersion(), $other->getVersion()); + + // Not the same version means we don't need to check if the bounds are inclusive or not + if (0 !== $compareResult) { + return (('>' === $operator) ? 1 : -1) === $compareResult; + } + + // Question we're answering here is "am I higher than $other?" + return '>' === $operator ? $other->isInclusive() : !$other->isInclusive(); + } + + public function __toString() + { + return sprintf( + '%s [%s]', + $this->getVersion(), + $this->isInclusive() ? 'inclusive' : 'exclusive' + ); + } + + /** + * @return self + */ + public static function zero() + { + return new Bound('0.0.0.0-dev', true); + } + + /** + * @return self + */ + public static function positiveInfinity() + { + return new Bound(PHP_INT_MAX.'.0.0.0', false); + } +} diff --git a/app/vendor/composer/semver/src/Constraint/Constraint.php b/app/vendor/composer/semver/src/Constraint/Constraint.php index 0f28d6435..dc394829e 100644 --- a/app/vendor/composer/semver/src/Constraint/Constraint.php +++ b/app/vendor/composer/semver/src/Constraint/Constraint.php @@ -24,10 +24,21 @@ class Constraint implements ConstraintInterface const OP_GE = 4; const OP_NE = 5; + /* operator string values */ + const STR_OP_EQ = '=='; + const STR_OP_EQ_ALT = '='; + const STR_OP_LT = '<'; + const STR_OP_LE = '<='; + const STR_OP_GT = '>'; + const STR_OP_GE = '>='; + const STR_OP_NE = '!='; + const STR_OP_NE_ALT = '<>'; + /** * Operator to integer translation table. * * @var array + * @phpstan-var array */ private static $transOpStr = array( '=' => self::OP_EQ, @@ -44,6 +55,7 @@ class Constraint implements ConstraintInterface * Integer to operator translation table. * * @var array + * @phpstan-var array */ private static $transOpInt = array( self::OP_EQ => '==', @@ -54,15 +66,66 @@ class Constraint implements ConstraintInterface self::OP_NE => '!=', ); - /** @var int */ + /** + * @var int + * @phpstan-var self::OP_* + */ protected $operator; /** @var string */ protected $version; - /** @var string */ + /** @var string|null */ protected $prettyString; + /** @var Bound */ + protected $lowerBound; + + /** @var Bound */ + protected $upperBound; + + /** + * Sets operator and version to compare with. + * + * @param string $operator + * @param string $version + * + * @throws \InvalidArgumentException if invalid operator is given. + * + * @phpstan-param self::STR_OP_* $operator + */ + public function __construct($operator, $version) + { + if (!isset(self::$transOpStr[$operator])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid operator "%s" given, expected one of: %s', + $operator, + implode(', ', self::getSupportedOperators()) + )); + } + + $this->operator = self::$transOpStr[$operator]; + $this->version = $version; + } + + /** + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * @return string + * + * @phpstan-return self::STR_OP_* + */ + public function getOperator() + { + return self::$transOpInt[$this->operator]; + } + /** * @param ConstraintInterface $provider * @@ -70,7 +133,7 @@ class Constraint implements ConstraintInterface */ public function matches(ConstraintInterface $provider) { - if ($provider instanceof $this) { + if ($provider instanceof self) { return $this->matchSpecific($provider); } @@ -79,7 +142,7 @@ public function matches(ConstraintInterface $provider) } /** - * @param string $prettyString + * {@inheritDoc} */ public function setPrettyString($prettyString) { @@ -87,7 +150,7 @@ public function setPrettyString($prettyString) } /** - * @return string + * {@inheritDoc} */ public function getPrettyString() { @@ -102,6 +165,8 @@ public function getPrettyString() * Get all supported comparison operators. * * @return array + * + * @phpstan-return list */ public static function getSupportedOperators() { @@ -109,25 +174,15 @@ public static function getSupportedOperators() } /** - * Sets operator and version to compare with. + * @param string $operator + * @return int * - * @param string $operator - * @param string $version - * - * @throws \InvalidArgumentException if invalid operator is given. + * @phpstan-param self::STR_OP_* $operator + * @phpstan-return self::OP_* */ - public function __construct($operator, $version) + public static function getOperatorConstant($operator) { - if (!isset(self::$transOpStr[$operator])) { - throw new \InvalidArgumentException(sprintf( - 'Invalid operator "%s" given, expected one of: %s', - $operator, - implode(', ', self::getSupportedOperators()) - )); - } - - $this->operator = self::$transOpStr[$operator]; - $this->version = $version; + return self::$transOpStr[$operator]; } /** @@ -139,6 +194,8 @@ public function __construct($operator, $version) * @throws \InvalidArgumentException if invalid operator is given. * * @return bool + * + * @phpstan-param self::STR_OP_* $operator */ public function versionCompare($a, $b, $operator, $compareBranches = false) { @@ -150,8 +207,12 @@ public function versionCompare($a, $b, $operator, $compareBranches = false) )); } - $aIsBranch = 'dev-' === substr($a, 0, 4); - $bIsBranch = 'dev-' === substr($b, 0, 4); + $aIsBranch = strpos($a, 'dev-') === 0; + $bIsBranch = strpos($b, 'dev-') === 0; + + if ($operator === '!=' && ($aIsBranch || $bIsBranch)) { + return $a !== $b; + } if ($aIsBranch && $bIsBranch) { return $operator === '==' && $a === $b; @@ -162,7 +223,86 @@ public function versionCompare($a, $b, $operator, $compareBranches = false) return false; } - return version_compare($a, $b, $operator); + return \version_compare($a, $b, $operator); + } + + /** + * {@inheritDoc} + */ + public function compile($otherOperator) + { + if (strpos($this->version, 'dev-') === 0) { + if (self::OP_EQ === $this->operator) { + if (self::OP_EQ === $otherOperator) { + return sprintf('$b && $v === %s', \var_export($this->version, true)); + } + if (self::OP_NE === $otherOperator) { + return sprintf('!$b || $v !== %s', \var_export($this->version, true)); + } + return 'false'; + } + + if (self::OP_NE === $this->operator) { + if (self::OP_EQ === $otherOperator) { + return sprintf('!$b || $v !== %s', \var_export($this->version, true)); + } + if (self::OP_NE === $otherOperator) { + return 'true'; + } + return '!$b'; + } + + return 'false'; + } + + if (self::OP_EQ === $this->operator) { + if (self::OP_EQ === $otherOperator) { + return sprintf('\version_compare($v, %s, \'==\')', \var_export($this->version, true)); + } + if (self::OP_NE === $otherOperator) { + return sprintf('$b || \version_compare($v, %s, \'!=\')', \var_export($this->version, true)); + } + + return sprintf('!$b && \version_compare(%s, $v, \'%s\')', \var_export($this->version, true), self::$transOpInt[$otherOperator]); + } + + if (self::OP_NE === $this->operator) { + if (self::OP_EQ === $otherOperator) { + return sprintf('$b || (!$b && \version_compare($v, %s, \'!=\'))', \var_export($this->version, true)); + } + + if (self::OP_NE === $otherOperator) { + return 'true'; + } + return '!$b'; + } + + if (self::OP_LT === $this->operator || self::OP_LE === $this->operator) { + if (self::OP_LT === $otherOperator || self::OP_LE === $otherOperator) { + return '!$b'; + } + } else { // $this->operator must be self::OP_GT || self::OP_GE here + if (self::OP_GT === $otherOperator || self::OP_GE === $otherOperator) { + return '!$b'; + } + } + + if (self::OP_NE === $otherOperator) { + return 'true'; + } + + $codeComparison = sprintf('\version_compare($v, %s, \'%s\')', \var_export($this->version, true), self::$transOpInt[$this->operator]); + if ($this->operator === self::OP_LE) { + if ($otherOperator === self::OP_GT) { + return sprintf('!$b && \version_compare($v, %s, \'!=\') && ', \var_export($this->version, true)) . $codeComparison; + } + } elseif ($this->operator === self::OP_GE) { + if ($otherOperator === self::OP_LT) { + return sprintf('!$b && \version_compare($v, %s, \'!=\') && ', \var_export($this->version, true)) . $codeComparison; + } + } + + return sprintf('!$b && %s', $codeComparison); } /** @@ -184,22 +324,37 @@ public function matchSpecific(Constraint $provider, $compareBranches = false) // '!=' operator is match when other operator is not '==' operator or version is not match // these kinds of comparisons always have a solution if ($isNonEqualOp || $isProviderNonEqualOp) { - return (!$isEqualOp && !$isProviderEqualOp) - || $this->versionCompare($provider->version, $this->version, '!=', $compareBranches); + if ($isNonEqualOp && !$isProviderNonEqualOp && !$isProviderEqualOp && strpos($provider->version, 'dev-') === 0) { + return false; + } + + if ($isProviderNonEqualOp && !$isNonEqualOp && !$isEqualOp && strpos($this->version, 'dev-') === 0) { + return false; + } + + if (!$isEqualOp && !$isProviderEqualOp) { + return true; + } + return $this->versionCompare($provider->version, $this->version, '!=', $compareBranches); } // an example for the condition is <= 2.0 & < 1.0 // these kinds of comparisons always have a solution if ($this->operator !== self::OP_EQ && $noEqualOp === $providerNoEqualOp) { - return true; + return !(strpos($this->version, 'dev-') === 0 || strpos($provider->version, 'dev-') === 0); } - if ($this->versionCompare($provider->version, $this->version, self::$transOpInt[$this->operator], $compareBranches)) { + $version1 = $isEqualOp ? $this->version : $provider->version; + $version2 = $isEqualOp ? $provider->version : $this->version; + $operator = $isEqualOp ? $provider->operator : $this->operator; + + if ($this->versionCompare($version1, $version2, self::$transOpInt[$operator], $compareBranches)) { // special case, e.g. require >= 1.0 and provide < 1.0 // 1.0 >= 1.0 but 1.0 is outside of the provided interval - return !($provider->version === $this->version - && self::$transOpInt[$provider->operator] === $providerNoEqualOp - && self::$transOpInt[$this->operator] !== $noEqualOp); + + return !(self::$transOpInt[$provider->operator] === $providerNoEqualOp + && self::$transOpInt[$this->operator] !== $noEqualOp + && \version_compare($provider->version, $this->version, '==')); } return false; @@ -212,4 +367,69 @@ public function __toString() { return self::$transOpInt[$this->operator] . ' ' . $this->version; } + + /** + * {@inheritDoc} + */ + public function getLowerBound() + { + $this->extractBounds(); + + return $this->lowerBound; + } + + /** + * {@inheritDoc} + */ + public function getUpperBound() + { + $this->extractBounds(); + + return $this->upperBound; + } + + /** + * @return void + */ + private function extractBounds() + { + if (null !== $this->lowerBound) { + return; + } + + // Branches + if (strpos($this->version, 'dev-') === 0) { + $this->lowerBound = Bound::zero(); + $this->upperBound = Bound::positiveInfinity(); + + return; + } + + switch ($this->operator) { + case self::OP_EQ: + $this->lowerBound = new Bound($this->version, true); + $this->upperBound = new Bound($this->version, true); + break; + case self::OP_LT: + $this->lowerBound = Bound::zero(); + $this->upperBound = new Bound($this->version, false); + break; + case self::OP_LE: + $this->lowerBound = Bound::zero(); + $this->upperBound = new Bound($this->version, true); + break; + case self::OP_GT: + $this->lowerBound = new Bound($this->version, false); + $this->upperBound = Bound::positiveInfinity(); + break; + case self::OP_GE: + $this->lowerBound = new Bound($this->version, true); + $this->upperBound = Bound::positiveInfinity(); + break; + case self::OP_NE: + $this->lowerBound = Bound::zero(); + $this->upperBound = Bound::positiveInfinity(); + break; + } + } } diff --git a/app/vendor/composer/semver/src/Constraint/ConstraintInterface.php b/app/vendor/composer/semver/src/Constraint/ConstraintInterface.php index 7cb13b6a8..389b935b5 100644 --- a/app/vendor/composer/semver/src/Constraint/ConstraintInterface.php +++ b/app/vendor/composer/semver/src/Constraint/ConstraintInterface.php @@ -11,20 +11,63 @@ namespace Composer\Semver\Constraint; +/** + * DO NOT IMPLEMENT this interface. It is only meant for usage as a type hint + * in libraries relying on composer/semver but creating your own constraint class + * that implements this interface is not a supported use case and will cause the + * composer/semver components to return unexpected results. + */ interface ConstraintInterface { /** + * Checks whether the given constraint intersects in any way with this constraint + * * @param ConstraintInterface $provider * * @return bool */ public function matches(ConstraintInterface $provider); + /** + * Provides a compiled version of the constraint for the given operator + * The compiled version must be a PHP expression. + * Executor of compile version must provide 2 variables: + * - $v = the string version to compare with + * - $b = whether or not the version is a non-comparable branch (starts with "dev-") + * + * @see Constraint::OP_* for the list of available operators. + * @example return '!$b && version_compare($v, '1.0', '>')'; + * + * @param int $otherOperator one Constraint::OP_* + * + * @return string + * + * @phpstan-param Constraint::OP_* $otherOperator + */ + public function compile($otherOperator); + + /** + * @return Bound + */ + public function getUpperBound(); + + /** + * @return Bound + */ + public function getLowerBound(); + /** * @return string */ public function getPrettyString(); + /** + * @param string|null $prettyString + * + * @return void + */ + public function setPrettyString($prettyString); + /** * @return string */ diff --git a/app/vendor/composer/semver/src/Constraint/EmptyConstraint.php b/app/vendor/composer/semver/src/Constraint/EmptyConstraint.php deleted file mode 100644 index a082b8092..000000000 --- a/app/vendor/composer/semver/src/Constraint/EmptyConstraint.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Composer\Semver\Constraint; - -/** - * Defines the absence of a constraint. - */ -class EmptyConstraint implements ConstraintInterface -{ - /** @var string */ - protected $prettyString; - - /** - * @param ConstraintInterface $provider - * - * @return bool - */ - public function matches(ConstraintInterface $provider) - { - return true; - } - - /** - * @param string $prettyString - */ - public function setPrettyString($prettyString) - { - $this->prettyString = $prettyString; - } - - /** - * @return string - */ - public function getPrettyString() - { - if ($this->prettyString) { - return $this->prettyString; - } - - return (string) $this; - } - - /** - * @return string - */ - public function __toString() - { - return '[]'; - } -} diff --git a/app/vendor/composer/semver/src/Constraint/MatchAllConstraint.php b/app/vendor/composer/semver/src/Constraint/MatchAllConstraint.php new file mode 100644 index 000000000..5e51af950 --- /dev/null +++ b/app/vendor/composer/semver/src/Constraint/MatchAllConstraint.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver\Constraint; + +/** + * Defines the absence of a constraint. + * + * This constraint matches everything. + */ +class MatchAllConstraint implements ConstraintInterface +{ + /** @var string|null */ + protected $prettyString; + + /** + * @param ConstraintInterface $provider + * + * @return bool + */ + public function matches(ConstraintInterface $provider) + { + return true; + } + + /** + * {@inheritDoc} + */ + public function compile($otherOperator) + { + return 'true'; + } + + /** + * {@inheritDoc} + */ + public function setPrettyString($prettyString) + { + $this->prettyString = $prettyString; + } + + /** + * {@inheritDoc} + */ + public function getPrettyString() + { + if ($this->prettyString) { + return $this->prettyString; + } + + return (string) $this; + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + return '*'; + } + + /** + * {@inheritDoc} + */ + public function getUpperBound() + { + return Bound::positiveInfinity(); + } + + /** + * {@inheritDoc} + */ + public function getLowerBound() + { + return Bound::zero(); + } +} diff --git a/app/vendor/composer/semver/src/Constraint/MatchNoneConstraint.php b/app/vendor/composer/semver/src/Constraint/MatchNoneConstraint.php new file mode 100644 index 000000000..dadcf6228 --- /dev/null +++ b/app/vendor/composer/semver/src/Constraint/MatchNoneConstraint.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver\Constraint; + +/** + * Blackhole of constraints, nothing escapes it + */ +class MatchNoneConstraint implements ConstraintInterface +{ + /** @var string|null */ + protected $prettyString; + + /** + * @param ConstraintInterface $provider + * + * @return bool + */ + public function matches(ConstraintInterface $provider) + { + return false; + } + + /** + * {@inheritDoc} + */ + public function compile($otherOperator) + { + return 'false'; + } + + /** + * {@inheritDoc} + */ + public function setPrettyString($prettyString) + { + $this->prettyString = $prettyString; + } + + /** + * {@inheritDoc} + */ + public function getPrettyString() + { + if ($this->prettyString) { + return $this->prettyString; + } + + return (string) $this; + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + return '[]'; + } + + /** + * {@inheritDoc} + */ + public function getUpperBound() + { + return new Bound('0.0.0.0-dev', false); + } + + /** + * {@inheritDoc} + */ + public function getLowerBound() + { + return new Bound('0.0.0.0-dev', false); + } +} diff --git a/app/vendor/composer/semver/src/Constraint/MultiConstraint.php b/app/vendor/composer/semver/src/Constraint/MultiConstraint.php index 911285302..1f4c00616 100644 --- a/app/vendor/composer/semver/src/Constraint/MultiConstraint.php +++ b/app/vendor/composer/semver/src/Constraint/MultiConstraint.php @@ -16,21 +16,43 @@ */ class MultiConstraint implements ConstraintInterface { - /** @var ConstraintInterface[] */ + /** + * @var ConstraintInterface[] + * @phpstan-var non-empty-array + */ protected $constraints; /** @var string|null */ protected $prettyString; + /** @var string|null */ + protected $string; + /** @var bool */ protected $conjunctive; + /** @var Bound|null */ + protected $lowerBound; + + /** @var Bound|null */ + protected $upperBound; + /** * @param ConstraintInterface[] $constraints A set of constraints * @param bool $conjunctive Whether the constraints should be treated as conjunctive or disjunctive + * + * @throws \InvalidArgumentException If less than 2 constraints are passed */ public function __construct(array $constraints, $conjunctive = true) { + if (\count($constraints) < 2) { + throw new \InvalidArgumentException( + 'Must provide at least two constraints for a MultiConstraint. Use '. + 'the regular Constraint class for one constraint only or MatchAllConstraint for none. You may use '. + 'MultiConstraint::create() which optimizes and handles those cases automatically.' + ); + } + $this->constraints = $constraints; $this->conjunctive = $conjunctive; } @@ -59,6 +81,34 @@ public function isDisjunctive() return !$this->conjunctive; } + /** + * {@inheritDoc} + */ + public function compile($otherOperator) + { + $parts = array(); + foreach ($this->constraints as $constraint) { + $code = $constraint->compile($otherOperator); + if ($code === 'true') { + if (!$this->conjunctive) { + return 'true'; + } + } elseif ($code === 'false') { + if ($this->conjunctive) { + return 'false'; + } + } else { + $parts[] = '('.$code.')'; + } + } + + if (!$parts) { + return $this->conjunctive ? 'true' : 'false'; + } + + return $this->conjunctive ? implode('&&', $parts) : implode('||', $parts); + } + /** * @param ConstraintInterface $provider * @@ -68,7 +118,7 @@ public function matches(ConstraintInterface $provider) { if (false === $this->conjunctive) { foreach ($this->constraints as $constraint) { - if ($constraint->matches($provider)) { + if ($provider->matches($constraint)) { return true; } } @@ -76,8 +126,15 @@ public function matches(ConstraintInterface $provider) return false; } + // when matching a conjunctive and a disjunctive multi constraint we have to iterate over the disjunctive one + // otherwise we'd return true if different parts of the disjunctive constraint match the conjunctive one + // which would lead to incorrect results, e.g. [>1 and <2] would match [<1 or >2] although they do not intersect + if ($provider instanceof MultiConstraint && $provider->isDisjunctive()) { + return $provider->matches($this); + } + foreach ($this->constraints as $constraint) { - if (!$constraint->matches($provider)) { + if (!$provider->matches($constraint)) { return false; } } @@ -86,7 +143,7 @@ public function matches(ConstraintInterface $provider) } /** - * @param string|null $prettyString + * {@inheritDoc} */ public function setPrettyString($prettyString) { @@ -94,7 +151,7 @@ public function setPrettyString($prettyString) } /** - * @return string + * {@inheritDoc} */ public function getPrettyString() { @@ -106,15 +163,163 @@ public function getPrettyString() } /** - * @return string + * {@inheritDoc} */ public function __toString() { + if ($this->string !== null) { + return $this->string; + } + $constraints = array(); foreach ($this->constraints as $constraint) { $constraints[] = (string) $constraint; } - return '[' . implode($this->conjunctive ? ' ' : ' || ', $constraints) . ']'; + return $this->string = '[' . implode($this->conjunctive ? ' ' : ' || ', $constraints) . ']'; + } + + /** + * {@inheritDoc} + */ + public function getLowerBound() + { + $this->extractBounds(); + + if (null === $this->lowerBound) { + throw new \LogicException('extractBounds should have populated the lowerBound property'); + } + + return $this->lowerBound; + } + + /** + * {@inheritDoc} + */ + public function getUpperBound() + { + $this->extractBounds(); + + if (null === $this->upperBound) { + throw new \LogicException('extractBounds should have populated the upperBound property'); + } + + return $this->upperBound; + } + + /** + * Tries to optimize the constraints as much as possible, meaning + * reducing/collapsing congruent constraints etc. + * Does not necessarily return a MultiConstraint instance if + * things can be reduced to a simple constraint + * + * @param ConstraintInterface[] $constraints A set of constraints + * @param bool $conjunctive Whether the constraints should be treated as conjunctive or disjunctive + * + * @return ConstraintInterface + */ + public static function create(array $constraints, $conjunctive = true) + { + if (0 === \count($constraints)) { + return new MatchAllConstraint(); + } + + if (1 === \count($constraints)) { + return $constraints[0]; + } + + $optimized = self::optimizeConstraints($constraints, $conjunctive); + if ($optimized !== null) { + list($constraints, $conjunctive) = $optimized; + if (\count($constraints) === 1) { + return $constraints[0]; + } + } + + return new self($constraints, $conjunctive); + } + + /** + * @param ConstraintInterface[] $constraints + * @param bool $conjunctive + * @return ?array + * + * @phpstan-return array{0: list, 1: bool}|null + */ + private static function optimizeConstraints(array $constraints, $conjunctive) + { + // parse the two OR groups and if they are contiguous we collapse + // them into one constraint + // [>= 1 < 2] || [>= 2 < 3] || [>= 3 < 4] => [>= 1 < 4] + if (!$conjunctive) { + $left = $constraints[0]; + $mergedConstraints = array(); + $optimized = false; + for ($i = 1, $l = \count($constraints); $i < $l; $i++) { + $right = $constraints[$i]; + if ( + $left instanceof self + && $left->conjunctive + && $right instanceof self + && $right->conjunctive + && \count($left->constraints) === 2 + && \count($right->constraints) === 2 + && ($left0 = (string) $left->constraints[0]) + && $left0[0] === '>' && $left0[1] === '=' + && ($left1 = (string) $left->constraints[1]) + && $left1[0] === '<' + && ($right0 = (string) $right->constraints[0]) + && $right0[0] === '>' && $right0[1] === '=' + && ($right1 = (string) $right->constraints[1]) + && $right1[0] === '<' + && substr($left1, 2) === substr($right0, 3) + ) { + $optimized = true; + $left = new MultiConstraint( + array( + $left->constraints[0], + $right->constraints[1], + ), + true); + } else { + $mergedConstraints[] = $left; + $left = $right; + } + } + if ($optimized) { + $mergedConstraints[] = $left; + return array($mergedConstraints, false); + } + } + + // TODO: Here's the place to put more optimizations + + return null; + } + + /** + * @return void + */ + private function extractBounds() + { + if (null !== $this->lowerBound) { + return; + } + + foreach ($this->constraints as $constraint) { + if (null === $this->lowerBound || null === $this->upperBound) { + $this->lowerBound = $constraint->getLowerBound(); + $this->upperBound = $constraint->getUpperBound(); + continue; + } + + if ($constraint->getLowerBound()->compareTo($this->lowerBound, $this->isConjunctive() ? '>' : '<')) { + $this->lowerBound = $constraint->getLowerBound(); + } + + if ($constraint->getUpperBound()->compareTo($this->upperBound, $this->isConjunctive() ? '<' : '>')) { + $this->upperBound = $constraint->getUpperBound(); + } + } } } diff --git a/app/vendor/composer/semver/src/Interval.php b/app/vendor/composer/semver/src/Interval.php new file mode 100644 index 000000000..43d5a4f5c --- /dev/null +++ b/app/vendor/composer/semver/src/Interval.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver; + +use Composer\Semver\Constraint\Constraint; + +class Interval +{ + /** @var Constraint */ + private $start; + /** @var Constraint */ + private $end; + + public function __construct(Constraint $start, Constraint $end) + { + $this->start = $start; + $this->end = $end; + } + + /** + * @return Constraint + */ + public function getStart() + { + return $this->start; + } + + /** + * @return Constraint + */ + public function getEnd() + { + return $this->end; + } + + /** + * @return Constraint + */ + public static function fromZero() + { + static $zero; + + if (null === $zero) { + $zero = new Constraint('>=', '0.0.0.0-dev'); + } + + return $zero; + } + + /** + * @return Constraint + */ + public static function untilPositiveInfinity() + { + static $positiveInfinity; + + if (null === $positiveInfinity) { + $positiveInfinity = new Constraint('<', PHP_INT_MAX.'.0.0.0'); + } + + return $positiveInfinity; + } + + /** + * @return self + */ + public static function any() + { + return new self(self::fromZero(), self::untilPositiveInfinity()); + } + + /** + * @return array{'names': string[], 'exclude': bool} + */ + public static function anyDev() + { + // any == exclude nothing + return array('names' => array(), 'exclude' => true); + } + + /** + * @return array{'names': string[], 'exclude': bool} + */ + public static function noDev() + { + // nothing == no names included + return array('names' => array(), 'exclude' => false); + } +} diff --git a/app/vendor/composer/semver/src/Intervals.php b/app/vendor/composer/semver/src/Intervals.php new file mode 100644 index 000000000..f2e4c8c81 --- /dev/null +++ b/app/vendor/composer/semver/src/Intervals.php @@ -0,0 +1,478 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver; + +use Composer\Semver\Constraint\Constraint; +use Composer\Semver\Constraint\ConstraintInterface; +use Composer\Semver\Constraint\MatchAllConstraint; +use Composer\Semver\Constraint\MatchNoneConstraint; +use Composer\Semver\Constraint\MultiConstraint; + +/** + * Helper class generating intervals from constraints + * + * This contains utilities for: + * + * - compacting an existing constraint which can be used to combine several into one + * by creating a MultiConstraint out of the many constraints you have. + * + * - checking whether one subset is a subset of another. + * + * Note: You should call clear to free memoization memory usage when you are done using this class + */ +class Intervals +{ + /** + * @phpstan-var array + */ + private static $intervalsCache = array(); + + /** + * @phpstan-var array + */ + private static $opSortOrder = array( + '>=' => -3, + '<' => -2, + '>' => 2, + '<=' => 3, + ); + + /** + * Clears the memoization cache once you are done + * + * @return void + */ + public static function clear() + { + self::$intervalsCache = array(); + } + + /** + * Checks whether $candidate is a subset of $constraint + * + * @return bool + */ + public static function isSubsetOf(ConstraintInterface $candidate, ConstraintInterface $constraint) + { + if ($constraint instanceof MatchAllConstraint) { + return true; + } + + if ($candidate instanceof MatchNoneConstraint || $constraint instanceof MatchNoneConstraint) { + return false; + } + + $intersectionIntervals = self::get(new MultiConstraint(array($candidate, $constraint), true)); + $candidateIntervals = self::get($candidate); + if (\count($intersectionIntervals['numeric']) !== \count($candidateIntervals['numeric'])) { + return false; + } + + foreach ($intersectionIntervals['numeric'] as $index => $interval) { + if (!isset($candidateIntervals['numeric'][$index])) { + return false; + } + + if ((string) $candidateIntervals['numeric'][$index]->getStart() !== (string) $interval->getStart()) { + return false; + } + + if ((string) $candidateIntervals['numeric'][$index]->getEnd() !== (string) $interval->getEnd()) { + return false; + } + } + + if ($intersectionIntervals['branches']['exclude'] !== $candidateIntervals['branches']['exclude']) { + return false; + } + if (\count($intersectionIntervals['branches']['names']) !== \count($candidateIntervals['branches']['names'])) { + return false; + } + foreach ($intersectionIntervals['branches']['names'] as $index => $name) { + if ($name !== $candidateIntervals['branches']['names'][$index]) { + return false; + } + } + + return true; + } + + /** + * Checks whether $a and $b have any intersection, equivalent to $a->matches($b) + * + * @return bool + */ + public static function haveIntersections(ConstraintInterface $a, ConstraintInterface $b) + { + if ($a instanceof MatchAllConstraint || $b instanceof MatchAllConstraint) { + return true; + } + + if ($a instanceof MatchNoneConstraint || $b instanceof MatchNoneConstraint) { + return false; + } + + $intersectionIntervals = self::generateIntervals(new MultiConstraint(array($a, $b), true), true); + + return \count($intersectionIntervals['numeric']) > 0 || $intersectionIntervals['branches']['exclude'] || \count($intersectionIntervals['branches']['names']) > 0; + } + + /** + * Attempts to optimize a MultiConstraint + * + * When merging MultiConstraints together they can get very large, this will + * compact it by looking at the real intervals covered by all the constraints + * and then creates a new constraint containing only the smallest amount of rules + * to match the same intervals. + * + * @return ConstraintInterface + */ + public static function compactConstraint(ConstraintInterface $constraint) + { + if (!$constraint instanceof MultiConstraint) { + return $constraint; + } + + $intervals = self::generateIntervals($constraint); + $constraints = array(); + $hasNumericMatchAll = false; + + if (\count($intervals['numeric']) === 1 && (string) $intervals['numeric'][0]->getStart() === (string) Interval::fromZero() && (string) $intervals['numeric'][0]->getEnd() === (string) Interval::untilPositiveInfinity()) { + $constraints[] = $intervals['numeric'][0]->getStart(); + $hasNumericMatchAll = true; + } else { + $unEqualConstraints = array(); + for ($i = 0, $count = \count($intervals['numeric']); $i < $count; $i++) { + $interval = $intervals['numeric'][$i]; + + // if current interval ends with < N and next interval begins with > N we can swap this out for != N + // but this needs to happen as a conjunctive expression together with the start of the current interval + // and end of next interval, so [>=M, N, [>=M, !=N, getEnd()->getOperator() === '<' && $i+1 < $count) { + $nextInterval = $intervals['numeric'][$i+1]; + if ($interval->getEnd()->getVersion() === $nextInterval->getStart()->getVersion() && $nextInterval->getStart()->getOperator() === '>') { + // only add a start if we didn't already do so, can be skipped if we're looking at second + // interval in [>=M, N, P, =M, !=N] already and we only want to add !=P right now + if (\count($unEqualConstraints) === 0 && (string) $interval->getStart() !== (string) Interval::fromZero()) { + $unEqualConstraints[] = $interval->getStart(); + } + $unEqualConstraints[] = new Constraint('!=', $interval->getEnd()->getVersion()); + continue; + } + } + + if (\count($unEqualConstraints) > 0) { + // this is where the end of the following interval of a != constraint is added as explained above + if ((string) $interval->getEnd() !== (string) Interval::untilPositiveInfinity()) { + $unEqualConstraints[] = $interval->getEnd(); + } + + // count is 1 if entire constraint is just one != expression + if (\count($unEqualConstraints) > 1) { + $constraints[] = new MultiConstraint($unEqualConstraints, true); + } else { + $constraints[] = $unEqualConstraints[0]; + } + + $unEqualConstraints = array(); + continue; + } + + // convert back >= x - <= x intervals to == x + if ($interval->getStart()->getVersion() === $interval->getEnd()->getVersion() && $interval->getStart()->getOperator() === '>=' && $interval->getEnd()->getOperator() === '<=') { + $constraints[] = new Constraint('==', $interval->getStart()->getVersion()); + continue; + } + + if ((string) $interval->getStart() === (string) Interval::fromZero()) { + $constraints[] = $interval->getEnd(); + } elseif ((string) $interval->getEnd() === (string) Interval::untilPositiveInfinity()) { + $constraints[] = $interval->getStart(); + } else { + $constraints[] = new MultiConstraint(array($interval->getStart(), $interval->getEnd()), true); + } + } + } + + $devConstraints = array(); + + if (0 === \count($intervals['branches']['names'])) { + if ($intervals['branches']['exclude']) { + if ($hasNumericMatchAll) { + return new MatchAllConstraint; + } + // otherwise constraint should contain a != operator and already cover this + } + } else { + foreach ($intervals['branches']['names'] as $branchName) { + if ($intervals['branches']['exclude']) { + $devConstraints[] = new Constraint('!=', $branchName); + } else { + $devConstraints[] = new Constraint('==', $branchName); + } + } + + // excluded branches, e.g. != dev-foo are conjunctive with the interval, so + // > 2.0 != dev-foo must return a conjunctive constraint + if ($intervals['branches']['exclude']) { + if (\count($constraints) > 1) { + return new MultiConstraint(array_merge( + array(new MultiConstraint($constraints, false)), + $devConstraints + ), true); + } + + if (\count($constraints) === 1 && (string)$constraints[0] === (string)Interval::fromZero()) { + if (\count($devConstraints) > 1) { + return new MultiConstraint($devConstraints, true); + } + return $devConstraints[0]; + } + + return new MultiConstraint(array_merge($constraints, $devConstraints), true); + } + + // otherwise devConstraints contains a list of == operators for branches which are disjunctive with the + // rest of the constraint + $constraints = array_merge($constraints, $devConstraints); + } + + if (\count($constraints) > 1) { + return new MultiConstraint($constraints, false); + } + + if (\count($constraints) === 1) { + return $constraints[0]; + } + + return new MatchNoneConstraint; + } + + /** + * Creates an array of numeric intervals and branch constraints representing a given constraint + * + * if the returned numeric array is empty it means the constraint matches nothing in the numeric range (0 - +inf) + * if the returned branches array is empty it means no dev-* versions are matched + * if a constraint matches all possible dev-* versions, branches will contain Interval::anyDev() + * + * @return array + * @phpstan-return array{'numeric': Interval[], 'branches': array{'names': string[], 'exclude': bool}} + */ + public static function get(ConstraintInterface $constraint) + { + $key = (string) $constraint; + + if (!isset(self::$intervalsCache[$key])) { + self::$intervalsCache[$key] = self::generateIntervals($constraint); + } + + return self::$intervalsCache[$key]; + } + + /** + * @param bool $stopOnFirstValidInterval + * + * @phpstan-return array{'numeric': Interval[], 'branches': array{'names': string[], 'exclude': bool}} + */ + private static function generateIntervals(ConstraintInterface $constraint, $stopOnFirstValidInterval = false) + { + if ($constraint instanceof MatchAllConstraint) { + return array('numeric' => array(new Interval(Interval::fromZero(), Interval::untilPositiveInfinity())), 'branches' => Interval::anyDev()); + } + + if ($constraint instanceof MatchNoneConstraint) { + return array('numeric' => array(), 'branches' => array('names' => array(), 'exclude' => false)); + } + + if ($constraint instanceof Constraint) { + return self::generateSingleConstraintIntervals($constraint); + } + + if (!$constraint instanceof MultiConstraint) { + throw new \UnexpectedValueException('The constraint passed in should be an MatchAllConstraint, Constraint or MultiConstraint instance, got '.\get_class($constraint).'.'); + } + + $constraints = $constraint->getConstraints(); + + $numericGroups = array(); + $constraintBranches = array(); + foreach ($constraints as $c) { + $res = self::get($c); + $numericGroups[] = $res['numeric']; + $constraintBranches[] = $res['branches']; + } + + if ($constraint->isDisjunctive()) { + $branches = Interval::noDev(); + foreach ($constraintBranches as $b) { + if ($b['exclude']) { + if ($branches['exclude']) { + // disjunctive constraint, so only exclude what's excluded in all constraints + // !=a,!=b || !=b,!=c => !=b + $branches['names'] = array_intersect($branches['names'], $b['names']); + } else { + // disjunctive constraint so exclude all names which are not explicitly included in the alternative + // (==b || ==c) || !=a,!=b => !=a + $branches['exclude'] = true; + $branches['names'] = array_diff($b['names'], $branches['names']); + } + } else { + if ($branches['exclude']) { + // disjunctive constraint so exclude all names which are not explicitly included in the alternative + // !=a,!=b || (==b || ==c) => !=a + $branches['names'] = array_diff($branches['names'], $b['names']); + } else { + // disjunctive constraint, so just add all the other branches + // (==a || ==b) || ==c => ==a || ==b || ==c + $branches['names'] = array_merge($branches['names'], $b['names']); + } + } + } + } else { + $branches = Interval::anyDev(); + foreach ($constraintBranches as $b) { + if ($b['exclude']) { + if ($branches['exclude']) { + // conjunctive, so just add all branch names to be excluded + // !=a && !=b => !=a,!=b + $branches['names'] = array_merge($branches['names'], $b['names']); + } else { + // conjunctive, so only keep included names which are not excluded + // (==a||==c) && !=a,!=b => ==c + $branches['names'] = array_diff($branches['names'], $b['names']); + } + } else { + if ($branches['exclude']) { + // conjunctive, so only keep included names which are not excluded + // !=a,!=b && (==a||==c) => ==c + $branches['names'] = array_diff($b['names'], $branches['names']); + $branches['exclude'] = false; + } else { + // conjunctive, so only keep names that are included in both + // (==a||==b) && (==a||==c) => ==a + $branches['names'] = array_intersect($branches['names'], $b['names']); + } + } + } + } + + $branches['names'] = array_unique($branches['names']); + + if (\count($numericGroups) === 1) { + return array('numeric' => $numericGroups[0], 'branches' => $branches); + } + + $borders = array(); + foreach ($numericGroups as $group) { + foreach ($group as $interval) { + $borders[] = array('version' => $interval->getStart()->getVersion(), 'operator' => $interval->getStart()->getOperator(), 'side' => 'start'); + $borders[] = array('version' => $interval->getEnd()->getVersion(), 'operator' => $interval->getEnd()->getOperator(), 'side' => 'end'); + } + } + + $opSortOrder = self::$opSortOrder; + usort($borders, function ($a, $b) use ($opSortOrder) { + $order = version_compare($a['version'], $b['version']); + if ($order === 0) { + return $opSortOrder[$a['operator']] - $opSortOrder[$b['operator']]; + } + + return $order; + }); + + $activeIntervals = 0; + $intervals = array(); + $index = 0; + $activationThreshold = $constraint->isConjunctive() ? \count($numericGroups) : 1; + $start = null; + foreach ($borders as $border) { + if ($border['side'] === 'start') { + $activeIntervals++; + } else { + $activeIntervals--; + } + if (!$start && $activeIntervals >= $activationThreshold) { + $start = new Constraint($border['operator'], $border['version']); + } elseif ($start && $activeIntervals < $activationThreshold) { + // filter out invalid intervals like > x - <= x, or >= x - < x + if ( + version_compare($start->getVersion(), $border['version'], '=') + && ( + ($start->getOperator() === '>' && $border['operator'] === '<=') + || ($start->getOperator() === '>=' && $border['operator'] === '<') + ) + ) { + unset($intervals[$index]); + } else { + $intervals[$index] = new Interval($start, new Constraint($border['operator'], $border['version'])); + $index++; + + if ($stopOnFirstValidInterval) { + break; + } + } + + $start = null; + } + } + + return array('numeric' => $intervals, 'branches' => $branches); + } + + /** + * @phpstan-return array{'numeric': Interval[], 'branches': array{'names': string[], 'exclude': bool}}} + */ + private static function generateSingleConstraintIntervals(Constraint $constraint) + { + $op = $constraint->getOperator(); + + // handle branch constraints first + if (strpos($constraint->getVersion(), 'dev-') === 0) { + $intervals = array(); + $branches = array('names' => array(), 'exclude' => false); + + // != dev-foo means any numeric version may match, we treat >/< like != they are not really defined for branches + if ($op === '!=') { + $intervals[] = new Interval(Interval::fromZero(), Interval::untilPositiveInfinity()); + $branches = array('names' => array($constraint->getVersion()), 'exclude' => true); + } elseif ($op === '==') { + $branches['names'][] = $constraint->getVersion(); + } + + return array( + 'numeric' => $intervals, + 'branches' => $branches, + ); + } + + if ($op[0] === '>') { // > & >= + return array('numeric' => array(new Interval($constraint, Interval::untilPositiveInfinity())), 'branches' => Interval::noDev()); + } + if ($op[0] === '<') { // < & <= + return array('numeric' => array(new Interval(Interval::fromZero(), $constraint)), 'branches' => Interval::noDev()); + } + if ($op === '!=') { + // convert !=x to intervals of 0 - x - +inf + dev* + return array('numeric' => array( + new Interval(Interval::fromZero(), new Constraint('<', $constraint->getVersion())), + new Interval(new Constraint('>', $constraint->getVersion()), Interval::untilPositiveInfinity()), + ), 'branches' => Interval::anyDev()); + } + + // convert ==x to an interval of >=x - <=x + return array('numeric' => array( + new Interval(new Constraint('>=', $constraint->getVersion()), new Constraint('<=', $constraint->getVersion())), + ), 'branches' => Interval::noDev()); + } +} diff --git a/app/vendor/composer/semver/src/Semver.php b/app/vendor/composer/semver/src/Semver.php index 4f312d73d..4d6de3c2a 100644 --- a/app/vendor/composer/semver/src/Semver.php +++ b/app/vendor/composer/semver/src/Semver.php @@ -45,10 +45,10 @@ public static function satisfies($version, $constraints) /** * Return all versions that satisfy given constraints. * - * @param array $versions - * @param string $constraints + * @param string[] $versions + * @param string $constraints * - * @return array + * @return string[] */ public static function satisfiedBy(array $versions, $constraints) { @@ -62,9 +62,9 @@ public static function satisfiedBy(array $versions, $constraints) /** * Sort given array of versions. * - * @param array $versions + * @param string[] $versions * - * @return array + * @return string[] */ public static function sort(array $versions) { @@ -74,9 +74,9 @@ public static function sort(array $versions) /** * Sort given array of versions in reverse. * - * @param array $versions + * @param string[] $versions * - * @return array + * @return string[] */ public static function rsort(array $versions) { @@ -84,10 +84,10 @@ public static function rsort(array $versions) } /** - * @param array $versions - * @param int $direction + * @param string[] $versions + * @param int $direction * - * @return array + * @return string[] */ private static function usort(array $versions, $direction) { @@ -101,7 +101,9 @@ private static function usort(array $versions, $direction) // Normalize outside of usort() scope for minor performance increase. // Creates an array of arrays: [[normalized, key], ...] foreach ($versions as $key => $version) { - $normalized[] = array($versionParser->normalize($version), $key); + $normalizedVersion = $versionParser->normalize($version); + $normalizedVersion = $versionParser->normalizeDefaultBranch($normalizedVersion); + $normalized[] = array($normalizedVersion, $key); } usort($normalized, function (array $left, array $right) use ($direction) { diff --git a/app/vendor/composer/semver/src/VersionParser.php b/app/vendor/composer/semver/src/VersionParser.php index 5af0fa307..ee4420ae4 100644 --- a/app/vendor/composer/semver/src/VersionParser.php +++ b/app/vendor/composer/semver/src/VersionParser.php @@ -12,7 +12,7 @@ namespace Composer\Semver; use Composer\Semver\Constraint\ConstraintInterface; -use Composer\Semver\Constraint\EmptyConstraint; +use Composer\Semver\Constraint\MatchAllConstraint; use Composer\Semver\Constraint\MultiConstraint; use Composer\Semver\Constraint\Constraint; @@ -50,7 +50,7 @@ class VersionParser */ public static function parseStability($version) { - $version = preg_replace('{#.+$}i', '', $version); + $version = (string) preg_replace('{#.+$}', '', $version); if (strpos($version, 'dev-') === 0 || '-dev' === substr($version, -4)) { return 'dev'; @@ -117,9 +117,9 @@ public function normalize($version, $fullVersion = null) $version = substr($version, 0, strlen($version) - strlen($match[0])); } - // match master-like branches - if (preg_match('{^(?:dev-)?(?:master|trunk|default)$}i', $version)) { - return '9999999-dev'; + // normalize master/trunk/default branches to dev-name for BC with 1.x as these used to be valid constraints + if (\in_array($version, array('master', 'trunk', 'default'), true)) { + $version = 'dev-' . $version; } // if requirement is branch-like, use full name @@ -212,10 +212,6 @@ public function normalizeBranch($name) { $name = trim($name); - if (in_array($name, array('master', 'trunk', 'default'))) { - return $this->normalize($name); - } - if (preg_match('{^v?(\d++)(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?$}i', $name, $matches)) { $version = ''; for ($i = 1; $i < 5; ++$i) { @@ -228,6 +224,22 @@ public function normalizeBranch($name) return 'dev-' . $name; } + /** + * Normalizes a default branch name (i.e. master on git) to 9999999-dev. + * + * @param string $name + * + * @return string + */ + public function normalizeDefaultBranch($name) + { + if ($name === 'dev-master' || $name === 'dev-default' || $name === 'dev-trunk') { + return '9999999-dev'; + } + + return $name; + } + /** * Parses a constraint string into MultiConstraint and/or Constraint objects. * @@ -240,11 +252,17 @@ public function parseConstraints($constraints) $prettyConstraint = $constraints; $orConstraints = preg_split('{\s*\|\|?\s*}', trim($constraints)); + if (false === $orConstraints) { + throw new \RuntimeException('Failed to preg_split string: '.$constraints); + } $orGroups = array(); foreach ($orConstraints as $constraints) { $andConstraints = preg_split('{(?< ,]) *(? 1) { + if (false === $andConstraints) { + throw new \RuntimeException('Failed to preg_split string: '.$constraints); + } + if (\count($andConstraints) > 1) { $constraintObjects = array(); foreach ($andConstraints as $constraint) { foreach ($this->parseConstraint($constraint) as $parsedConstraint) { @@ -255,7 +273,7 @@ public function parseConstraints($constraints) $constraintObjects = $this->parseConstraint($andConstraints[0]); } - if (1 === count($constraintObjects)) { + if (1 === \count($constraintObjects)) { $constraint = $constraintObjects[0]; } else { $constraint = new MultiConstraint($constraintObjects); @@ -264,28 +282,7 @@ public function parseConstraints($constraints) $orGroups[] = $constraint; } - if (1 === count($orGroups)) { - $constraint = $orGroups[0]; - } elseif (2 === count($orGroups) - // parse the two OR groups and if they are contiguous we collapse - // them into one constraint - && $orGroups[0] instanceof MultiConstraint - && $orGroups[1] instanceof MultiConstraint - && 2 === count($orGroups[0]->getConstraints()) - && 2 === count($orGroups[1]->getConstraints()) - && ($a = (string) $orGroups[0]) - && strpos($a, '[>=') === 0 && (false !== ($posA = strpos($a, '<', 4))) - && ($b = (string) $orGroups[1]) - && strpos($b, '[>=') === 0 && (false !== ($posB = strpos($b, '<', 4))) - && substr($a, $posA + 2, -1) === substr($b, 4, $posB - 5) - ) { - $constraint = new MultiConstraint(array( - new Constraint('>=', substr($a, 4, $posA - 5)), - new Constraint('<', substr($b, $posB + 2, -1)), - )); - } else { - $constraint = new MultiConstraint($orGroups, false); - } + $constraint = MultiConstraint::create($orGroups, false); $constraint->setPrettyString($prettyConstraint); @@ -298,6 +295,8 @@ public function parseConstraints($constraints) * @throws \UnexpectedValueException * * @return array + * + * @phpstan-return non-empty-array */ private function parseConstraint($constraint) { @@ -319,8 +318,12 @@ private function parseConstraint($constraint) $constraint = $match[1]; } - if (preg_match('{^v?[xX*](\.[xX*])*$}i', $constraint)) { - return array(new EmptyConstraint()); + if (preg_match('{^(v)?[xX*](\.[xX*])*$}i', $constraint, $match)) { + if (!empty($match[1]) || !empty($match[2])) { + return array(new Constraint('>=', '0.0.0.0-dev')); + } + + return array(new MatchAllConstraint()); } $versionRegex = 'v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.(\d++))?(?:' . self::$modifierRegex . '|\.([xX*][.-]?dev))(?:\+[^\s]+)?'; @@ -526,8 +529,10 @@ private function parseConstraint($constraint) * @param string $pad The string to pad version parts after $position * * @return string|null The new version + * + * @phpstan-param string[] $matches */ - private function manipulateVersionString($matches, $position, $increment = 0, $pad = '0') + private function manipulateVersionString(array $matches, $position, $increment = 0, $pad = '0') { for ($i = 4; $i > 0; --$i) { if ($i > $position) { diff --git a/app/vendor/composer/xdebug-handler/CHANGELOG.md b/app/vendor/composer/xdebug-handler/CHANGELOG.md index 38fa75c7e..d7d46f9c5 100644 --- a/app/vendor/composer/xdebug-handler/CHANGELOG.md +++ b/app/vendor/composer/xdebug-handler/CHANGELOG.md @@ -1,5 +1,23 @@ ## [Unreleased] +## [2.0.2] - 2021-07-31 + * Added: support for `xdebug_info('mode')` in Xdebug 3.1. + * Added: support for Psr\Log versions 2 and 3. + * Fixed: remove ini directives from non-cli HOST/PATH sections. + +## [2.0.1] - 2021-05-05 + * Fixed: don't restart if the cwd is a UNC path and cmd.exe will be invoked. + +## [2.0.0] - 2021-04-09 + * Break: this is a major release, see [UPGRADE.md](UPGRADE.md) for more information. + * Break: removed optional `$colorOption` constructor param and passthru fallback. + * Break: renamed `requiresRestart` param from `$isLoaded` to `$default`. + * Break: changed `restart` param `$command` from a string to an array. + * Added: support for Xdebug3 to only restart if Xdebug is not running with `xdebug.mode=off`. + * Added: `isXdebugActive()` method to determine if Xdebug is still running in the restart. + * Added: feature to bypass the shell in PHP-7.4+ by giving `proc_open` an array of arguments. + * Added: Process utility class to the API. + ## [1.4.6] - 2021-03-25 * Fixed: fail restart if `proc_open` has been disabled in `disable_functions`. * Fixed: enable Windows CTRL event handling in the restarted process. @@ -8,7 +26,7 @@ * Fixed: use `proc_open` when available for correct FD forwarding to the restarted process. ## [1.4.4] - 2020-10-24 - * Fix: exception if 'pcntl_signal' is disabled. + * Fixed: exception if 'pcntl_signal' is disabled. ## [1.4.3] - 2020-08-19 * Fixed: restore SIGINT to default handler in restarted process if no other handler exists. @@ -73,7 +91,10 @@ * Break: the following class was renamed: - `Composer\XdebugHandler` -> `Composer\XdebugHandler\XdebugHandler` -[Unreleased]: https://github.com/composer/xdebug-handler/compare/1.4.6...HEAD +[Unreleased]: https://github.com/composer/xdebug-handler/compare/2.0.2...HEAD +[2.0.2]: https://github.com/composer/xdebug-handler/compare/2.0.1...2.0.2 +[2.0.1]: https://github.com/composer/xdebug-handler/compare/2.0.0...2.0.1 +[2.0.0]: https://github.com/composer/xdebug-handler/compare/1.4.6...2.0.0 [1.4.6]: https://github.com/composer/xdebug-handler/compare/1.4.5...1.4.6 [1.4.5]: https://github.com/composer/xdebug-handler/compare/1.4.4...1.4.5 [1.4.4]: https://github.com/composer/xdebug-handler/compare/1.4.3...1.4.4 diff --git a/app/vendor/composer/xdebug-handler/README.md b/app/vendor/composer/xdebug-handler/README.md index 734d95847..020cf486e 100644 --- a/app/vendor/composer/xdebug-handler/README.md +++ b/app/vendor/composer/xdebug-handler/README.md @@ -5,11 +5,15 @@ ![license](https://img.shields.io/github/license/composer/xdebug-handler.svg) ![php](https://img.shields.io/packagist/php-v/composer/xdebug-handler.svg?colorB=8892BF&label=php) -Restart a CLI process without loading the Xdebug extension. +Restart a CLI process without loading the Xdebug extension, unless `xdebug.mode=off`. Originally written as part of [composer/composer](https://github.com/composer/composer), now extracted and made available as a stand-alone library. +### Version 2 + +Support added for Xdebug3. See [UPGRADE](UPGRADE.md) for more information. + ## Installation Install the latest version with: @@ -31,21 +35,11 @@ $xdebug->check(); unset($xdebug); ``` -The constructor takes two parameters: - -#### _$envPrefix_ -This is used to create distinct environment variables and is upper-cased and prepended to default base values. The above example enables the use of: +The constructor takes a single parameter, `$envPrefix`, which is upper-cased and prepended to default base values to create two distinct environment variables. The above example enables the use of: - `MYAPP_ALLOW_XDEBUG=1` to override automatic restart and allow Xdebug - `MYAPP_ORIGINAL_INIS` to obtain ini file locations in a restarted process -#### _$colorOption_ -This optional value is added to the restart command-line and is needed to force color output in a piped child process. Only long-options are supported, for example `--ansi` or `--colors=always` etc. - -If the original command-line contains an argument that pattern matches this value (for example `--no-ansi`, `--colors=never`) then _$colorOption_ is ignored. - -If the pattern match ends with `=auto` (for example `--colors=auto`), the argument is replaced by _$colorOption_. Otherwise it is added at either the end of the command-line, or preceding the first double-dash `--` delimiter. - ## Advanced Usage * [How it works](#how-it-works) @@ -129,6 +123,9 @@ $version = XdebugHandler::getSkippedVersion(); # $version: '2.6.0' (for example), or an empty string ``` +#### _isXdebugActive()_ +Returns true if Xdebug is loaded and is running in an active mode (if it supports modes). Returns false if Xdebug is not loaded, or it is running with `xdebug.mode=off`. + ### Setter methods These methods implement a fluent interface and must be called before the main `check()` method. @@ -237,15 +234,16 @@ The following environment settings can be used to troubleshoot unexpected behavi ### Extending the library The API is defined by classes and their accessible elements that are not annotated as @internal. The main class has two protected methods that can be overridden to provide additional functionality: -#### _requiresRestart($isLoaded)_ -By default the process will restart if Xdebug is loaded. Extending this method allows an application to decide, by returning a boolean (or equivalent) value. It is only called if `MYAPP_ALLOW_XDEBUG` is empty, so it will not be called in the restarted process (where this variable contains internal data), or if the restart has been overridden. +#### _requiresRestart($default)_ +By default the process will restart if Xdebug is loaded and not running with `xdebug.mode=off`. Extending this method allows an application to decide, by returning a boolean (or equivalent) value. +It is only called if `MYAPP_ALLOW_XDEBUG` is empty, so it will not be called in the restarted process (where this variable contains internal data), or if the restart has been overridden. Note that the [setMainScript()](#setmainscriptscript) and [setPersistent()](#setpersistent) setters can be used here, if required. #### _restart($command)_ An application can extend this to modify the temporary ini file, its location given in the `tmpIni` property. New settings can be safely appended to the end of the data, which is `PHP_EOL` terminated. -Note that the `$command` parameter is the escaped command-line string that will be used for the new process and must be treated accordingly. +The `$command` parameter is an array of unescaped command-line arguments that will be used for the new process. Remember to finish with `parent::restart($command)`. @@ -264,7 +262,7 @@ class MyRestarter extends XdebugHandler { private $required; - protected function requiresRestart($isLoaded) + protected function requiresRestart($default) { if (Command::isHelp()) { # No need to disable Xdebug for this @@ -272,7 +270,7 @@ class MyRestarter extends XdebugHandler } $this->required = (bool) ini_get('phar.readonly'); - return $isLoaded || $this->required; + return $this->required || $default; } protected function restart($command) diff --git a/app/vendor/composer/xdebug-handler/UPGRADE.md b/app/vendor/composer/xdebug-handler/UPGRADE.md new file mode 100644 index 000000000..3d62e17af --- /dev/null +++ b/app/vendor/composer/xdebug-handler/UPGRADE.md @@ -0,0 +1,30 @@ +## Upgrading from 1.x + +The default behaviour has changed from always restarting if Xdebug is loaded, to only restarting if +Xdebug is _active_. This has been introduced to support Xdebug3 +[modes](https://xdebug.org/docs/all_settings#mode). Xdebug is considered active if it is loaded, and +for Xdebug3, if it is running in a mode other than `xdebug.mode=off`. + +* Break: removed optional `$colorOption` constructor param and passthru fallback. + + Just use `new XdebugHandler('myapp')` to instantiate the class and color support will be +detectable in the restarted process. + +* Added: `isXdebugActive()` method to determine if Xdebug is still running in the restart. + + Returns true if Xdebug is loaded and is running in an active mode (if it supports modes). +Returns false if Xdebug is not loaded, or it is running with `xdebug.mode=off`. + +### Extending classes + +* Break: renamed `requiresRestart` param from `$isLoaded` to `$default`. + + This reflects the new default behaviour which is to restart only if Xdebug is active. + +* Break: changed `restart` param `$command` from a string to an array. + + Only important if you modified the string. `$command` is now an array of unescaped arguments. + +* Added: Process utility class to the API. + + This class was previously annotated as @internal and has been refactored due to recent changes. diff --git a/app/vendor/composer/xdebug-handler/composer.json b/app/vendor/composer/xdebug-handler/composer.json index 7df9ea649..75cb1f745 100644 --- a/app/vendor/composer/xdebug-handler/composer.json +++ b/app/vendor/composer/xdebug-handler/composer.json @@ -19,7 +19,7 @@ }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1.0" + "psr/log": "^1 || ^2 || ^3" }, "require-dev": { "symfony/phpunit-bridge": "^4.2 || ^5", @@ -36,7 +36,7 @@ } }, "scripts": { - "test": "SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT=1 vendor/bin/simple-phpunit", + "test": "vendor/bin/simple-phpunit", "phpstan": "vendor/bin/phpstan analyse" } } diff --git a/app/vendor/composer/xdebug-handler/src/PhpConfig.php b/app/vendor/composer/xdebug-handler/src/PhpConfig.php index 5535eca56..c61cd7cfd 100644 --- a/app/vendor/composer/xdebug-handler/src/PhpConfig.php +++ b/app/vendor/composer/xdebug-handler/src/PhpConfig.php @@ -49,8 +49,8 @@ public function useStandard() public function usePersistent() { if ($data = $this->getDataAndReset()) { - Process::setEnv('PHPRC', $data['tmpIni']); - Process::setEnv('PHP_INI_SCAN_DIR', ''); + $this->updateEnv('PHPRC', $data['tmpIni']); + $this->updateEnv('PHP_INI_SCAN_DIR', ''); } return array(); @@ -64,10 +64,21 @@ public function usePersistent() private function getDataAndReset() { if ($data = XdebugHandler::getRestartSettings()) { - Process::setEnv('PHPRC', $data['phprc']); - Process::setEnv('PHP_INI_SCAN_DIR', $data['scanDir']); + $this->updateEnv('PHPRC', $data['phprc']); + $this->updateEnv('PHP_INI_SCAN_DIR', $data['scanDir']); } return $data; } + + /** + * Updates a restart settings value in the environment + * + * @param string $name + * @param string|false $value + */ + private function updateEnv($name, $value) + { + Process::setEnv($name, false !== $value ? $value : null); + } } diff --git a/app/vendor/composer/xdebug-handler/src/Process.php b/app/vendor/composer/xdebug-handler/src/Process.php index eb2ad2b4e..c60dff10f 100644 --- a/app/vendor/composer/xdebug-handler/src/Process.php +++ b/app/vendor/composer/xdebug-handler/src/Process.php @@ -12,59 +12,12 @@ namespace Composer\XdebugHandler; /** - * Provides utility functions to prepare a child process command-line and set - * environment variables in that process. + * Process utility functions * * @author John Stevenson - * @internal */ class Process { - /** - * Returns an array of parameters, including a color option if required - * - * A color option is needed because child process output is piped. - * - * @param array $args The script parameters - * @param string $colorOption The long option to force color output - * - * @return array - */ - public static function addColorOption(array $args, $colorOption) - { - if (!$colorOption - || in_array($colorOption, $args) - || !preg_match('/^--([a-z]+$)|(^--[a-z]+=)/', $colorOption, $matches)) { - return $args; - } - - if (isset($matches[2])) { - // Handle --color(s)= options - if (false !== ($index = array_search($matches[2].'auto', $args))) { - $args[$index] = $colorOption; - return $args; - } elseif (preg_grep('/^'.$matches[2].'/', $args)) { - return $args; - } - } elseif (in_array('--no-'.$matches[1], $args)) { - return $args; - } - - // Check for NO_COLOR variable (https://no-color.org/) - if (false !== getenv('NO_COLOR')) { - return $args; - } - - if (false !== ($index = array_search('--', $args))) { - // Position option before double-dash delimiter - array_splice($args, $index, 0, $colorOption); - } else { - $args[] = $colorOption; - } - - return $args; - } - /** * Escapes a string to be used as a shell argument. * @@ -109,53 +62,33 @@ public static function escape($arg, $meta = true, $module = false) } /** - * Returns true if the output stream supports colors - * - * This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo - * terminals via named pipes, so we can only check the environment. + * Escapes an array of arguments that make up a shell command * - * @param mixed $output A valid CLI output stream + * @param array $args Argument list, with the module name first * - * @return bool + * @return string The escaped command line */ - public static function supportsColor($output) + public static function escapeShellCommand(array $args) { - if ('Hyper' === getenv('TERM_PROGRAM')) { - return true; - } - - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - return (function_exists('sapi_windows_vt100_support') - && sapi_windows_vt100_support($output)) - || false !== getenv('ANSICON') - || 'ON' === getenv('ConEmuANSI') - || 'xterm' === getenv('TERM'); - } - - if (function_exists('stream_isatty')) { - return stream_isatty($output); - } - - if (function_exists('posix_isatty')) { - return posix_isatty($output); + $cmd = self::escape(array_shift($args), true, true); + foreach ($args as $arg) { + $cmd .= ' '.self::escape($arg); } - $stat = fstat($output); - // Check if formatted mode is S_IFCHR - return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; + return $cmd; } /** * Makes putenv environment changes available in $_SERVER and $_ENV * * @param string $name - * @param string|false $value A false value unsets the variable + * @param string|null $value A null value unsets the variable * * @return bool Whether the environment variable was set */ - public static function setEnv($name, $value = false) + public static function setEnv($name, $value = null) { - $unset = false === $value; + $unset = null === $value; if (!putenv($unset ? $name : $name.'='.$value)) { return false; diff --git a/app/vendor/composer/xdebug-handler/src/Status.php b/app/vendor/composer/xdebug-handler/src/Status.php index e714b1c2c..b3dcb87e8 100644 --- a/app/vendor/composer/xdebug-handler/src/Status.php +++ b/app/vendor/composer/xdebug-handler/src/Status.php @@ -33,6 +33,7 @@ class Status private $envAllowXdebug; private $loaded; private $logger; + private $modeOff; private $time; /** @@ -91,7 +92,12 @@ private function output($text, $level = null) private function reportCheck($loaded) { - $this->loaded = $loaded; + list($version, $mode) = explode('|', $loaded); + + if ($version) { + $this->loaded = '('.$version.')'.($mode ? ' mode='.$mode : ''); + } + $this->modeOff = $mode === 'off'; $this->output('Checking '.$this->envAllowXdebug); } @@ -112,7 +118,7 @@ private function reportNoRestart() if ($this->loaded) { $text = sprintf('No restart (%s)', $this->getEnvAllow()); if (!getenv($this->envAllowXdebug)) { - $text .= ' Allowed by application'; + $text .= ' Allowed by '.($this->modeOff ? 'mode' : 'application'); } $this->output($text); } @@ -157,7 +163,7 @@ private function getEnvAllow() */ private function getLoadedMessage() { - $loaded = $this->loaded ? sprintf('loaded (%s)', $this->loaded) : 'not loaded'; + $loaded = $this->loaded ? sprintf('loaded %s', $this->loaded) : 'not loaded'; return 'The Xdebug extension is '.$loaded; } } diff --git a/app/vendor/composer/xdebug-handler/src/XdebugHandler.php b/app/vendor/composer/xdebug-handler/src/XdebugHandler.php index 30391f096..cd1a73837 100644 --- a/app/vendor/composer/xdebug-handler/src/XdebugHandler.php +++ b/app/vendor/composer/xdebug-handler/src/XdebugHandler.php @@ -30,13 +30,14 @@ class XdebugHandler private static $inRestart; private static $name; private static $skipped; + private static $xdebugActive; private $cli; - private $colorOption; private $debug; private $envAllowXdebug; private $envOriginalInis; private $loaded; + private $mode; private $persistent; private $script; /** @var Status|null */ @@ -50,12 +51,11 @@ class XdebugHandler * would result in MYAPP_ALLOW_XDEBUG and MYAPP_ORIGINAL_INIS. * * @param string $envPrefix Value used in environment variables - * @param string $colorOption Command-line long option to force color output - * @throws \RuntimeException If a parameter is invalid + * @throws \RuntimeException If the parameter is invalid */ - public function __construct($envPrefix, $colorOption = '') + public function __construct($envPrefix) { - if (!is_string($envPrefix) || empty($envPrefix) || !is_string($colorOption)) { + if (!is_string($envPrefix) || empty($envPrefix)) { throw new \RuntimeException('Invalid constructor parameter'); } @@ -63,13 +63,23 @@ public function __construct($envPrefix, $colorOption = '') $this->envAllowXdebug = self::$name.self::SUFFIX_ALLOW; $this->envOriginalInis = self::$name.self::SUFFIX_INIS; - $this->colorOption = $colorOption; - if (extension_loaded('xdebug')) { - $ext = new \ReflectionExtension('xdebug'); - $this->loaded = $ext->getVersion() ?: 'unknown'; + $this->loaded = phpversion('xdebug') ?: 'unknown'; + + if (version_compare($this->loaded, '3.1', '>=')) { + /** @phpstan-ignore-next-line */ + $modes = xdebug_info('mode'); + $this->mode = empty($modes) ? 'off' : implode(',', $modes); + } elseif (false !== ($mode = ini_get('xdebug.mode'))) { + $this->mode = getenv('XDEBUG_MODE') ?: ($mode ?: 'off'); + if (preg_match('/^,+$/', str_replace(' ', '', $this->mode))) { + $this->mode = 'off'; + } + } } + self::$xdebugActive = $this->loaded && $this->mode !== 'off'; + if ($this->cli = PHP_SAPI === 'cli') { $this->debug = getenv(self::DEBUG); } @@ -123,10 +133,10 @@ public function setPersistent() */ public function check() { - $this->notify(Status::CHECK, $this->loaded); + $this->notify(Status::CHECK, $this->loaded.'|'.$this->mode); $envArgs = explode('|', (string) getenv($this->envAllowXdebug)); - if (empty($envArgs[0]) && $this->requiresRestart((bool) $this->loaded)) { + if (empty($envArgs[0]) && $this->requiresRestart(self::$xdebugActive)) { // Restart required $this->notify(Status::RESTART); @@ -229,21 +239,39 @@ public static function getSkippedVersion() } /** - * Returns true if Xdebug is loaded, or as directed by an extending class + * Returns whether Xdebug is loaded and active * - * @param bool $isLoaded Whether Xdebug is loaded + * true: if Xdebug is loaded and is running in an active mode. + * false: if Xdebug is not loaded, or it is running with xdebug.mode=off. * * @return bool */ - protected function requiresRestart($isLoaded) + public static function isXdebugActive() { - return $isLoaded; + return self::$xdebugActive; + } + + /** + * Allows an extending class to decide if there should be a restart + * + * The default is to restart if Xdebug is loaded and its mode is not "off". + * Do not typehint for 1.x compatibility. + * + * @param bool $default The default behaviour + * + * @return bool Whether the process should restart + */ + protected function requiresRestart($default) + { + return $default; } /** * Allows an extending class to access the tmpIni * - * @param string $command + * Do not typehint for 1.x compatibility + * + * @param array $command */ protected function restart($command) { @@ -253,28 +281,30 @@ protected function restart($command) /** * Executes the restarted command then deletes the tmp ini * - * @param string $command + * @param array $command */ - private function doRestart($command) + private function doRestart(array $command) { $this->tryEnableSignals(); - $this->notify(Status::RESTARTING, $command); + $this->notify(Status::RESTARTING, implode(' ', $command)); - // Prefer proc_open to keep fds intact, because passthru pipes to stdout - if (function_exists('proc_open')) { - if (defined('PHP_WINDOWS_VERSION_BUILD') && PHP_VERSION_ID < 80000) { - $command = '"'.$command.'"'; - } - $process = proc_open($command, array(), $pipes); - if (is_resource($process)) { - $exitCode = proc_close($process); - } + if (PHP_VERSION_ID >= 70400) { + $cmd = $command; } else { - passthru($command, $exitCode); + $cmd = Process::escapeShellCommand($command); + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + // Outer quotes required on cmd string below PHP 8 + $cmd = '"'.$cmd.'"'; + } + } + + $process = proc_open($cmd, array(), $pipes); + if (is_resource($process)) { + $exitCode = proc_close($process); } if (!isset($exitCode)) { - // Unlikely that the default shell cannot be invoked + // Unlikely that php or the default shell cannot be invoked $this->notify(Status::ERROR, 'Unable to restart process'); $exitCode = -1; } else { @@ -351,7 +381,8 @@ private function writeTmpIni(array $iniFiles, $tmpDir, &$error) } $content = ''; - $regex = '/^\s*(zend_extension\s*=.*xdebug.*)$/mi'; + $sectionRegex = '/^\s*\[(?:PATH|HOST)\s*=/mi'; + $xdebugRegex = '/^\s*(zend_extension\s*=.*xdebug.*)$/mi'; foreach ($iniFiles as $file) { // Check for inaccessible ini files @@ -359,7 +390,11 @@ private function writeTmpIni(array $iniFiles, $tmpDir, &$error) $error = 'Unable to read ini: '.$file; return false; } - $content .= preg_replace($regex, ';$1', $data).PHP_EOL; + // Check and remove directives after HOST and PATH sections + if (preg_match($sectionRegex, $data, $matches, PREG_OFFSET_CAPTURE)) { + $data = substr($data, 0, $matches[0][1]); + } + $content .= preg_replace($xdebugRegex, ';$1', $data).PHP_EOL; } // Merge loaded settings into our ini content, if it is valid @@ -375,9 +410,9 @@ private function writeTmpIni(array $iniFiles, $tmpDir, &$error) } /** - * Returns the restart command line + * Returns the command line arguments for the restart * - * @return string + * @return array */ private function getCommand() { @@ -389,18 +424,7 @@ private function getCommand() array_push($php, '-n', '-c', $this->tmpIni); } - if (defined('STDOUT') && Process::supportsColor(STDOUT)) { - $args = Process::addColorOption($args, $this->colorOption); - } - - $args = array_merge($php, array($this->script), $args); - - $cmd = Process::escape(array_shift($args), true, true); - foreach ($args as $arg) { - $cmd .= ' '.Process::escape($arg); - } - - return $cmd; + return array_merge($php, array($this->script), $args); } /** @@ -568,8 +592,8 @@ private function checkScanDirConfig() */ private function checkConfiguration(&$info) { - if (!function_exists('proc_open') && !function_exists('passthru')) { - $info = 'execution functions have been disabled (proc_open or passthru required)'; + if (!function_exists('proc_open')) { + $info = 'proc_open function is disabled'; return false; } @@ -583,6 +607,15 @@ private function checkConfiguration(&$info) } } + + $workingDir = getcwd(); + if (0 === strpos($workingDir, '\\\\')) { + if (defined('PHP_WINDOWS_VERSION_BUILD') && PHP_VERSION_ID < 70400) { + $info = 'cmd.exe does not support UNC paths: '.$workingDir; + return false; + } + } + return true; } @@ -600,13 +633,10 @@ private function tryEnableSignals() if (!self::$inRestart) { // Restarting, so ignore SIGINT in parent pcntl_signal(SIGINT, SIG_IGN); - $message .= ' (SIGINT = SIG_IGN)'; } elseif (is_int(pcntl_signal_get_handler(SIGINT))) { // Restarted, no handler set so force default action pcntl_signal(SIGINT, SIG_DFL); - $message .= ' (SIGINT = SIG_DFL)'; } - $this->notify(Status::INFO, $message); } if (!self::$inRestart && function_exists('sapi_windows_set_ctrl_handler')) { @@ -614,7 +644,6 @@ private function tryEnableSignals() // This ensures that CTRL+C events will be available in the child // process without having to enable them there, which is unreliable. sapi_windows_set_ctrl_handler(function ($evt) {}); - $this->notify(Status::INFO, 'CTRL signals suppressed'); } } } diff --git a/app/vendor/dealerdirect/phpcodesniffer-composer-installer/.remarkrc b/app/vendor/dealerdirect/phpcodesniffer-composer-installer/.remarkrc new file mode 100644 index 000000000..bfa065da3 --- /dev/null +++ b/app/vendor/dealerdirect/phpcodesniffer-composer-installer/.remarkrc @@ -0,0 +1,6 @@ +{ + "plugins": [ + "remark-preset-lint-recommended", + ["remark-lint-list-item-indent", "space"] + ] +} diff --git a/app/vendor/dealerdirect/phpcodesniffer-composer-installer/.yamllint b/app/vendor/dealerdirect/phpcodesniffer-composer-installer/.yamllint new file mode 100644 index 000000000..41d60da67 --- /dev/null +++ b/app/vendor/dealerdirect/phpcodesniffer-composer-installer/.yamllint @@ -0,0 +1,6 @@ +--- +extends: default +rules: + line-length: + level: warning + max: 120 diff --git a/app/vendor/dealerdirect/phpcodesniffer-composer-installer/CODE_OF_CONDUCT.md b/app/vendor/dealerdirect/phpcodesniffer-composer-installer/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..a408703d8 --- /dev/null +++ b/app/vendor/dealerdirect/phpcodesniffer-composer-installer/CODE_OF_CONDUCT.md @@ -0,0 +1,129 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/app/vendor/dealerdirect/phpcodesniffer-composer-installer/LICENSE.md b/app/vendor/dealerdirect/phpcodesniffer-composer-installer/LICENSE.md new file mode 100644 index 000000000..6a00e0c4c --- /dev/null +++ b/app/vendor/dealerdirect/phpcodesniffer-composer-installer/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016-2020 Dealerdirect B.V. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/app/vendor/dealerdirect/phpcodesniffer-composer-installer/README.md b/app/vendor/dealerdirect/phpcodesniffer-composer-installer/README.md new file mode 100644 index 000000000..2be42ed77 --- /dev/null +++ b/app/vendor/dealerdirect/phpcodesniffer-composer-installer/README.md @@ -0,0 +1,251 @@ +# PHP_CodeSniffer Standards Composer Installer Plugin + +![Project Stage][project-stage-shield] +![Last Commit][last-updated-shield] +![Awesome][awesome-shield] +[![License][license-shield]](LICENSE.md) + +[![Travis][travis-shield]][travis] +[![Scrutinizer][scrutinizer-shield]][scrutinizer] +[![Latest Version on Packagist][packagist-version-shield]][packagist-version] +[![Packagist][packagist-shield]][packagist] + +[![Contributor Covenant][code-of-conduct-shield]][code-of-conduct] + +This composer installer plugin allows for easy installation of [PHP_CodeSniffer][codesniffer] coding standards (rulesets). + +No more symbolic linking of directories, checking out repositories on specific locations or changing +the `phpcs` configuration. + +_Note: This plugin is compatible with both version 2.x and 3.x of_ [PHP_CodeSniffer][codesniffer] + +## Usage + +Installation can be done with [Composer][composer], by requiring this package as a development dependency: + +```bash +composer require --dev dealerdirect/phpcodesniffer-composer-installer +``` + +That's it. + +### How it works + +Basically, this plugin executes the following steps: + +- This plugin searches for `phpcodesniffer-standard` packages in all of your currently installed Composer packages. +- Matching packages and the project itself are scanned for PHP_CodeSniffer rulesets. +- The plugin will call PHP_CodeSniffer and configure the `installed_paths` option. + +### Example project + +The following is an example Composer project and has included +multiple `phpcodesniffer-standard` packages. + +```json +{ + "name": "dealerdirect/example-project", + "description": "Just an example project", + "type": "project", + "require": {}, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "*", + "object-calisthenics/phpcs-calisthenics-rules": "*", + "phpcompatibility/php-compatibility": "*", + "wp-coding-standards/wpcs": "*" + } +} +``` + +After running `composer install` PHP_CodeSniffer just works: + +```bash +$ ./vendor/bin/phpcs -i +The installed coding standards are MySource, PEAR, PSR1, PSR2, Squiz, Zend, PHPCompatibility, WordPress, +WordPress-Core, WordPress-Docs, WordPress-Extra and WordPress-VIP +``` + +### Calling the plugin directly + +In some circumstances, it is desirable to call this plugin's functionality +directly. For instance, during development or in [CI][definition-ci] environments. + +As the plugin requires Composer to work, direct calls need to be wired through a +project's `composer.json`. + +This is done by adding a call to the `Plugin::run` function in the `script` +section of the `composer.json`: + +```json +{ + "scripts": { + "install-codestandards": [ + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run" + ] + } +} + +``` + +The command can then be called using `composer run-script install-codestandards` or +referenced from other script configurations, as follows: + +```json +{ + "scripts": { + "install-codestandards": [ + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run" + ], + "post-install-cmd": [ + "@install-codestandards" + ] + } +} + +``` + +For more details about Composer scripts, please refer to [the section on scripts +in the Composer manual][composer-manual-scripts]. + +### Changing the Coding Standards search depth + +By default, this plugin searches up for Coding Standards up to three directories +deep. In most cases, this should be sufficient. However, this plugin allows +you to customize the search depth setting if needed. + +```json +{ + "extra": { + "phpcodesniffer-search-depth": 5 + } +} +``` + +### Caveats + +When this plugin is installed globally, composer will load the _global_ plugin rather +than the one from the local repository. Despite [this behavior being documented +in the composer manual][using-composer-plugins], it could potentially confuse +as another version of the plugin could be run and not the one specified by the project. + +## Developing Coding Standards + +Coding standard can be developed normally, as documented by [PHP_CodeSniffer][codesniffer], in the [Coding Standard Tutorial][tutorial]. + +Create a composer package of your coding standard by adding a `composer.json` file. + +```json +{ + "name" : "acme/phpcodesniffer-our-standards", + "description" : "Package contains all coding standards of the Acme company", + "require" : { + "php" : ">=5.4.0,<8.0.0-dev", + "squizlabs/php_codesniffer" : "^3.0" + }, + "type" : "phpcodesniffer-standard" +} +``` + +Requirements: +* The repository may contain one or more standards. +* Each standard can have a separate directory no deeper than 3 levels from the repository root. +* The package `type` must be `phpcodesniffer-standard`. Without this, the plugin will not trigger. + +### Requiring the plugin from within your coding standard + +If your coding standard itself depends on additional external PHPCS standards, this plugin can +make life easier on your end-users by taking care of the installation of all standards - yours +and your dependencies - for them. + +This can help reduce the number of support questions about setting the `installed_paths`, as well +as simplify your standard's installation instructions. + +For this to work, make sure your external standard adds this plugin to the `composer.json` config +via `require`, **not** `require-dev`. + +> :warning: Your end-user may already `require-dev` this plugin and/or other external standards used +> by your end-users may require this plugin as well. +> +> To prevent your end-users getting into "_dependency hell_", make sure to make the version requirement +> for this plugin flexible. +> +> As, for now, this plugin is still regarded as "unstable" (version < 1.0), remember that Composer +> treats unstable minors as majors and will not be able to resolve one config requiring this plugin +> at version `^0.5`, while another requires it at version `^0.6`. +> Either allow multiple minors or use `*` as the version requirement. +> +> Some examples of flexible requirements which can be used: +> ```bash +> composer require dealerdirect/phpcodesniffer-composer-installer:"*" +> composer require dealerdirect/phpcodesniffer-composer-installer:"0.*" +> composer require dealerdirect/phpcodesniffer-composer-installer:"^0.4 || ^0.5 || ^0.6" +> ``` + +## Changelog + +This repository does not contain a `CHANGELOG.md` file, however, we do publish a changelog on each release +using the [GitHub releases][changelog] functionality. + +## Contributing + +This is an active open-source project. We are always open to people who want to +use the code or contribute to it. + +We've set up a separate document for our [contribution guidelines][contributing-guidelines]. + +Thank you for being involved! :heart_eyes: + +## Authors & contributors + +The original idea and setup of this repository is by [Franck Nijhof][frenck], employee @ Dealerdirect. + +For a full list of all author and/or contributors, check [the contributors page][contributors]. + +## License + +The MIT License (MIT) + +Copyright (c) 2016-2020 Dealerdirect B.V. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +[awesome-shield]: https://img.shields.io/badge/awesome%3F-yes-brightgreen.svg +[changelog]: https://github.com/Dealerdirect/phpcodesniffer-composer-installer/releases +[code-of-conduct-shield]: https://img.shields.io/badge/Contributor%20Covenant-v2.0-ff69b4.svg +[code-of-conduct]: CODE_OF_CONDUCT.md +[codesniffer]: https://github.com/squizlabs/PHP_CodeSniffer +[composer-manual-scripts]: https://getcomposer.org/doc/articles/scripts.md +[composer]: https://getcomposer.org/ +[contributing-guidelines]: CONTRIBUTING.md +[contributors]: https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors +[definition-ci]: https://en.wikipedia.org/wiki/Continuous_integration +[frenck]: https://github.com/frenck +[last-updated-shield]: https://img.shields.io/github/last-commit/Dealerdirect/phpcodesniffer-composer-installer.svg +[license-shield]: https://img.shields.io/github/license/dealerdirect/phpcodesniffer-composer-installer.svg +[packagist-shield]: https://img.shields.io/packagist/dt/dealerdirect/phpcodesniffer-composer-installer.svg +[packagist-version-shield]: https://img.shields.io/packagist/v/dealerdirect/phpcodesniffer-composer-installer.svg +[packagist-version]: https://packagist.org/packages/dealerdirect/phpcodesniffer-composer-installer +[packagist]: https://packagist.org/packages/dealerdirect/phpcodesniffer-composer-installer +[project-stage-shield]: https://img.shields.io/badge/Project%20Stage-Development-yellowgreen.svg +[scrutinizer-shield]: https://img.shields.io/scrutinizer/g/dealerdirect/phpcodesniffer-composer-installer.svg +[scrutinizer]: https://scrutinizer-ci.com/g/dealerdirect/phpcodesniffer-composer-installer/ +[travis-shield]: https://img.shields.io/travis/Dealerdirect/phpcodesniffer-composer-installer.svg +[travis]: https://travis-ci.org/Dealerdirect/phpcodesniffer-composer-installer +[tutorial]: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Coding-Standard-Tutorial +[using-composer-plugins]: https://getcomposer.org/doc/articles/plugins.md#using-plugins diff --git a/app/vendor/dealerdirect/phpcodesniffer-composer-installer/composer.json b/app/vendor/dealerdirect/phpcodesniffer-composer-installer/composer.json new file mode 100644 index 000000000..7fefc0faf --- /dev/null +++ b/app/vendor/dealerdirect/phpcodesniffer-composer-installer/composer.json @@ -0,0 +1,50 @@ +{ + "name": "dealerdirect/phpcodesniffer-composer-installer", + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "type": "composer-plugin", + "keywords": [ + "composer", "installer", "plugin", + "phpcs", "codesniffer", "phpcodesniffer", "php_codesniffer", + "standard", "standards", "style guide", "stylecheck", + "qa", "quality", "code quality", "tests" + ], + "homepage": "http://www.dealerdirect.com", + "license": "MIT", + "authors": [ + { + "name": "Franck Nijhof", + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" + } + ], + "support": { + "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + }, + "require": { + "php": ">=5.3", + "composer-plugin-api": "^1.0 || ^2.0", + "squizlabs/php_codesniffer": "^2.0 || ^3.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "sensiolabs/security-checker": "^4.1.0", + "phpcompatibility/php-compatibility": "^9.0" + }, + "minimum-stability": "dev", + "prefer-stable": true, + "autoload": { + "psr-4": { + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "extra": { + "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "scripts": { + "install-codestandards": [ + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run" + ] + } +} diff --git a/app/vendor/dealerdirect/phpcodesniffer-composer-installer/src/Plugin.php b/app/vendor/dealerdirect/phpcodesniffer-composer-installer/src/Plugin.php new file mode 100644 index 000000000..c87f76302 --- /dev/null +++ b/app/vendor/dealerdirect/phpcodesniffer-composer-installer/src/Plugin.php @@ -0,0 +1,620 @@ + + */ +class Plugin implements PluginInterface, EventSubscriberInterface +{ + + const KEY_MAX_DEPTH = 'phpcodesniffer-search-depth'; + + const MESSAGE_ERROR_WRONG_MAX_DEPTH = + 'The value of "%s" (in the composer.json "extra".section) must be an integer larger then %d, %s given.'; + const MESSAGE_NOT_INSTALLED = 'PHPCodeSniffer is not installed'; + const MESSAGE_NOTHING_TO_INSTALL = 'Nothing to install or update'; + const MESSAGE_PLUGIN_UNINSTALLED = 'PHPCodeSniffer Composer Installer is uninstalled'; + const MESSAGE_RUNNING_INSTALLER = 'Running PHPCodeSniffer Composer Installer'; + + const PACKAGE_NAME = 'squizlabs/php_codesniffer'; + const PACKAGE_TYPE = 'phpcodesniffer-standard'; + + const PHPCS_CONFIG_REGEX = '`%s:[^\r\n]+`'; + const PHPCS_CONFIG_KEY = 'installed_paths'; + + const PLUGIN_NAME = 'dealerdirect/phpcodesniffer-composer-installer'; + + /** + * @var Composer + */ + private $composer; + + /** + * @var string + */ + private $cwd; + + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var array + */ + private $installedPaths; + + /** + * @var IOInterface + */ + private $io; + + /** + * @var ProcessExecutor + */ + private $processExecutor; + + /** + * Triggers the plugin's main functionality. + * + * Makes it possible to run the plugin as a custom command. + * + * @param Event $event + * + * @throws \InvalidArgumentException + * @throws \RuntimeException + * @throws LogicException + * @throws ProcessFailedException + * @throws RuntimeException + */ + public static function run(Event $event) + { + $io = $event->getIO(); + $composer = $event->getComposer(); + + $instance = new static(); + + $instance->io = $io; + $instance->composer = $composer; + $instance->init(); + $instance->onDependenciesChangedEvent(); + } + + /** + * {@inheritDoc} + * + * @throws \RuntimeException + * @throws LogicException + * @throws ProcessFailedException + * @throws RuntimeException + */ + public function activate(Composer $composer, IOInterface $io) + { + $this->composer = $composer; + $this->io = $io; + + $this->init(); + } + + /** + * {@inheritDoc} + */ + public function deactivate(Composer $composer, IOInterface $io) + { + } + + /** + * {@inheritDoc} + */ + public function uninstall(Composer $composer, IOInterface $io) + { + } + + /** + * Prepares the plugin so it's main functionality can be run. + * + * @throws \RuntimeException + * @throws LogicException + * @throws ProcessFailedException + * @throws RuntimeException + */ + private function init() + { + $this->cwd = getcwd(); + $this->installedPaths = array(); + + $this->processExecutor = new ProcessExecutor($this->io); + $this->filesystem = new Filesystem($this->processExecutor); + } + + /** + * {@inheritDoc} + */ + public static function getSubscribedEvents() + { + return array( + ScriptEvents::POST_INSTALL_CMD => array( + array('onDependenciesChangedEvent', 0), + ), + ScriptEvents::POST_UPDATE_CMD => array( + array('onDependenciesChangedEvent', 0), + ), + ); + } + + /** + * Entry point for post install and post update events. + * + * @throws \InvalidArgumentException + * @throws LogicException + * @throws ProcessFailedException + * @throws RuntimeException + */ + public function onDependenciesChangedEvent() + { + $io = $this->io; + $isVerbose = $io->isVerbose(); + $exitCode = 0; + + if ($isVerbose) { + $io->write(sprintf('%s', self::MESSAGE_RUNNING_INSTALLER)); + } + + if ($this->isPHPCodeSnifferInstalled() === true) { + $this->loadInstalledPaths(); + $installPathCleaned = $this->cleanInstalledPaths(); + $installPathUpdated = $this->updateInstalledPaths(); + + if ($installPathCleaned === true || $installPathUpdated === true) { + $exitCode = $this->saveInstalledPaths(); + } elseif ($isVerbose) { + $io->write(sprintf('%s', self::MESSAGE_NOTHING_TO_INSTALL)); + } + } else { + $pluginPackage = $this + ->composer + ->getRepositoryManager() + ->getLocalRepository() + ->findPackages(self::PLUGIN_NAME) + ; + + $isPluginUninstalled = count($pluginPackage) === 0; + + if ($isPluginUninstalled) { + if ($isVerbose) { + $io->write(sprintf('%s', self::MESSAGE_PLUGIN_UNINSTALLED)); + } + } else { + $exitCode = 1; + if ($isVerbose) { + $io->write(sprintf('%s', self::MESSAGE_NOT_INSTALLED)); + } + } + } + + return $exitCode; + } + + /** + * Load all paths from PHP_CodeSniffer into an array. + * + * @throws LogicException + * @throws ProcessFailedException + * @throws RuntimeException + */ + private function loadInstalledPaths() + { + if ($this->isPHPCodeSnifferInstalled() === true) { + $this->processExecutor->execute( + sprintf( + 'phpcs --config-show %s', + self::PHPCS_CONFIG_KEY + ), + $output, + $this->composer->getConfig()->get('bin-dir') + ); + + $regex = sprintf(self::PHPCS_CONFIG_REGEX, self::PHPCS_CONFIG_KEY); + if (preg_match($regex, $output, $match) === 1) { + $phpcsInstalledPaths = str_replace(self::PHPCS_CONFIG_KEY . ': ', '', $match[0]); + $phpcsInstalledPaths = trim($phpcsInstalledPaths); + + if ($phpcsInstalledPaths !== '') { + $this->installedPaths = explode(',', $phpcsInstalledPaths); + } + } + } + } + + /** + * Save all coding standard paths back into PHP_CodeSniffer + * + * @throws LogicException + * @throws ProcessFailedException + * @throws RuntimeException + * + * @return int Exit code. 0 for success, 1 or higher for failure. + */ + private function saveInstalledPaths() + { + // Check if we found installed paths to set. + if (count($this->installedPaths) !== 0) { + sort($this->installedPaths); + $paths = implode(',', $this->installedPaths); + $arguments = array('--config-set', self::PHPCS_CONFIG_KEY, $paths); + $configMessage = sprintf( + 'PHP CodeSniffer Config %s set to %s', + self::PHPCS_CONFIG_KEY, + $paths + ); + } else { + // Delete the installed paths if none were found. + $arguments = array('--config-delete', self::PHPCS_CONFIG_KEY); + $configMessage = sprintf( + 'PHP CodeSniffer Config %s delete', + self::PHPCS_CONFIG_KEY + ); + } + + // Prepare message in case of failure + $failMessage = sprintf( + 'Failed to set PHP CodeSniffer %s Config', + self::PHPCS_CONFIG_KEY + ); + + // Determine the path to the main PHPCS file. + $phpcsPath = $this->getPHPCodeSnifferInstallPath(); + if (file_exists($phpcsPath . '/bin/phpcs') === true) { + // PHPCS 3.x. + $phpcsExecutable = './bin/phpcs'; + } else { + // PHPCS 2.x. + $phpcsExecutable = './scripts/phpcs'; + } + + // Okay, lets rock! + $command = vsprintf( + '%s %s %s', + array( + 'php executable' => $this->getPhpExecCommand(), + 'phpcs executable' => $phpcsExecutable, + 'arguments' => implode(' ', $arguments) + ) + ); + + $exitCode = $this->processExecutor->execute($command, $configResult, $phpcsPath); + if ($exitCode === 0) { + $exitCode = $this->verifySaveSuccess(); + } + + if ($exitCode === 0) { + $this->io->write($configMessage); + } else { + $this->io->write($failMessage); + } + + if ($this->io->isVerbose() && !empty($configResult)) { + $this->io->write(sprintf('%s', $configResult)); + } + + return $exitCode; + } + + /** + * Verify that the paths which were expected to be saved, have been. + * + * @return int Exit code. 0 for success, 1 for failure. + */ + private function verifySaveSuccess() + { + $exitCode = 1; + $expectedPaths = $this->installedPaths; + + // Request the currently set installed paths after the save. + $this->loadInstalledPaths(); + + $registeredPaths = array_intersect($this->installedPaths, $expectedPaths); + $registeredCount = count($registeredPaths); + $expectedCount = count($expectedPaths); + + if ($expectedCount === $registeredCount) { + $exitCode = 0; + } + + if ($exitCode === 1 && $this->io->isVerbose()) { + $verificationMessage = sprintf( + "Paths to external standards found by the plugin: %s\n" + . 'Actual paths registered with PHPCS: %s', + implode(', ', $expectedPaths), + implode(', ', $this->installedPaths) + ); + $this->io->write($verificationMessage); + } + + return $exitCode; + } + + /** + * Get the path to the current PHP version being used. + * + * Duplicate of the same in the EventDispatcher class in Composer itself. + */ + protected function getPhpExecCommand() + { + $finder = new PhpExecutableFinder(); + + $phpPath = $finder->find(false); + + if ($phpPath === false) { + throw new \RuntimeException('Failed to locate PHP binary to execute ' . $phpPath); + } + + $phpArgs = $finder->findArguments(); + $phpArgs = $phpArgs + ? ' ' . implode(' ', $phpArgs) + : '' + ; + + $command = ProcessExecutor::escape($phpPath) . + $phpArgs . + ' -d allow_url_fopen=' . ProcessExecutor::escape(ini_get('allow_url_fopen')) . + ' -d disable_functions=' . ProcessExecutor::escape(ini_get('disable_functions')) . + ' -d memory_limit=' . ProcessExecutor::escape(ini_get('memory_limit')) + ; + + return $command; + } + + /** + * Iterate trough all known paths and check if they are still valid. + * + * If path does not exists, is not an directory or isn't readable, the path + * is removed from the list. + * + * @return bool True if changes where made, false otherwise + */ + private function cleanInstalledPaths() + { + $changes = false; + foreach ($this->installedPaths as $key => $path) { + // This might be a relative path as well + $alternativePath = realpath($this->getPHPCodeSnifferInstallPath() . DIRECTORY_SEPARATOR . $path); + + if ( + (is_dir($path) === false || is_readable($path) === false) && + (is_dir($alternativePath) === false || is_readable($alternativePath) === false) + ) { + unset($this->installedPaths[$key]); + $changes = true; + } + } + return $changes; + } + + /** + * Check all installed packages (including the root package) against + * the installed paths from PHP_CodeSniffer and add the missing ones. + * + * @return bool True if changes where made, false otherwise + * + * @throws \InvalidArgumentException + * @throws \RuntimeException + */ + private function updateInstalledPaths() + { + $changes = false; + + $searchPaths = array($this->cwd); + $codingStandardPackages = $this->getPHPCodingStandardPackages(); + foreach ($codingStandardPackages as $package) { + $installPath = $this->composer->getInstallationManager()->getInstallPath($package); + if ($this->filesystem->isAbsolutePath($installPath) === false) { + $installPath = $this->filesystem->normalizePath( + $this->cwd . DIRECTORY_SEPARATOR . $installPath + ); + } + $searchPaths[] = $installPath; + } + + $finder = new Finder(); + $finder->files() + ->depth('<= ' . $this->getMaxDepth()) + ->depth('>= ' . $this->getMinDepth()) + ->ignoreUnreadableDirs() + ->ignoreVCS(true) + ->in($searchPaths) + ->name('ruleset.xml'); + + // Process each found possible ruleset. + foreach ($finder as $ruleset) { + $standardsPath = $ruleset->getPath(); + + // Pick the directory above the directory containing the standard, unless this is the project root. + if ($standardsPath !== $this->cwd) { + $standardsPath = dirname($standardsPath); + } + + // Use relative paths for local project repositories. + if ($this->isRunningGlobally() === false) { + $standardsPath = $this->filesystem->findShortestPath( + $this->getPHPCodeSnifferInstallPath(), + $standardsPath, + true + ); + } + + // De-duplicate and add when directory is not configured. + if (in_array($standardsPath, $this->installedPaths, true) === false) { + $this->installedPaths[] = $standardsPath; + $changes = true; + } + } + + return $changes; + } + + /** + * Iterates through Composers' local repository looking for valid Coding + * Standard packages. + * + * If the package is the RootPackage (the one the plugin is installed into), + * the package is ignored for now since it needs a different install path logic. + * + * @return array Composer packages containing coding standard(s) + */ + private function getPHPCodingStandardPackages() + { + $codingStandardPackages = array_filter( + $this->composer->getRepositoryManager()->getLocalRepository()->getPackages(), + function (PackageInterface $package) { + if ($package instanceof AliasPackage) { + return false; + } + return $package->getType() === Plugin::PACKAGE_TYPE; + } + ); + + if ( + ! $this->composer->getPackage() instanceof RootPackageInterface + && $this->composer->getPackage()->getType() === self::PACKAGE_TYPE + ) { + $codingStandardPackages[] = $this->composer->getPackage(); + } + + return $codingStandardPackages; + } + + /** + * Searches for the installed PHP_CodeSniffer Composer package + * + * @param null|string|\Composer\Semver\Constraint\ConstraintInterface $versionConstraint to match against + * + * @return PackageInterface|null + */ + private function getPHPCodeSnifferPackage($versionConstraint = null) + { + $packages = $this + ->composer + ->getRepositoryManager() + ->getLocalRepository() + ->findPackages(self::PACKAGE_NAME, $versionConstraint); + + return array_shift($packages); + } + + /** + * Returns the path to the PHP_CodeSniffer package installation location + * + * @return string + */ + private function getPHPCodeSnifferInstallPath() + { + return $this->composer->getInstallationManager()->getInstallPath($this->getPHPCodeSnifferPackage()); + } + + /** + * Simple check if PHP_CodeSniffer is installed. + * + * @param null|string|\Composer\Semver\Constraint\ConstraintInterface $versionConstraint to match against + * + * @return bool Whether PHP_CodeSniffer is installed + */ + private function isPHPCodeSnifferInstalled($versionConstraint = null) + { + return ($this->getPHPCodeSnifferPackage($versionConstraint) !== null); + } + + /** + * Test if composer is running "global" + * This check kinda dirty, but it is the "Composer Way" + * + * @return bool Whether Composer is running "globally" + * + * @throws \RuntimeException + */ + private function isRunningGlobally() + { + return ($this->composer->getConfig()->get('home') === $this->cwd); + } + + /** + * Determines the maximum search depth when searching for Coding Standards. + * + * @return int + * + * @throws \InvalidArgumentException + */ + private function getMaxDepth() + { + $maxDepth = 3; + + $extra = $this->composer->getPackage()->getExtra(); + + if (array_key_exists(self::KEY_MAX_DEPTH, $extra)) { + $maxDepth = $extra[self::KEY_MAX_DEPTH]; + $minDepth = $this->getMinDepth(); + + if ( + (string) (int) $maxDepth !== (string) $maxDepth /* Must be an integer or cleanly castable to one */ + || $maxDepth <= $minDepth /* Larger than the minimum */ + || is_float($maxDepth) === true /* Within the boundaries of integer */ + ) { + $message = vsprintf( + self::MESSAGE_ERROR_WRONG_MAX_DEPTH, + array( + 'key' => self::KEY_MAX_DEPTH, + 'min' => $minDepth, + 'given' => var_export($maxDepth, true), + ) + ); + + throw new \InvalidArgumentException($message); + } + } + + return (int) $maxDepth; + } + + /** + * Returns the minimal search depth for Coding Standard packages. + * + * Usually this is 0, unless PHP_CodeSniffer >= 3 is used. + * + * @return int + */ + private function getMinDepth() + { + if ($this->isPHPCodeSnifferInstalled('>= 3.0.0') !== true) { + return 1; + } + return 0; + } +} diff --git a/app/vendor/doctrine/dbal/bin/doctrine-dbal.php b/app/vendor/doctrine/dbal/bin/doctrine-dbal.php index 2f0c65be8..2f83e0d14 100644 --- a/app/vendor/doctrine/dbal/bin/doctrine-dbal.php +++ b/app/vendor/doctrine/dbal/bin/doctrine-dbal.php @@ -1,8 +1,6 @@ > - * - * @throws Exception - */ - public function iterateNumeric(): Traversable; - - /** - * Returns an iterator over the result set rows represented as associative arrays. - * - * @return Traversable> - * - * @throws Exception - */ - public function iterateAssociative(): Traversable; - - /** - * Returns an iterator over the values of the first column of the result set. - * - * @return Traversable - * - * @throws Exception - */ - public function iterateColumn(): Traversable; -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php deleted file mode 100644 index 07c44f2d0..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php +++ /dev/null @@ -1,244 +0,0 @@ -data = $data; - if (! count($data)) { - return; - } - - $this->columnCount = count($data[0]); - } - - /** - * {@inheritdoc} - * - * @deprecated Use free() instead. - */ - public function closeCursor() - { - $this->free(); - - return true; - } - - /** - * {@inheritdoc} - */ - public function rowCount() - { - return count($this->data); - } - - /** - * {@inheritdoc} - */ - public function columnCount() - { - return $this->columnCount; - } - - /** - * {@inheritdoc} - * - * @deprecated Use one of the fetch- or iterate-related methods. - */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) - { - if ($arg2 !== null || $arg3 !== null) { - throw new InvalidArgumentException('Caching layer does not support 2nd/3rd argument to setFetchMode()'); - } - - $this->defaultFetchMode = $fetchMode; - - return true; - } - - /** - * {@inheritdoc} - * - * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. - */ - #[ReturnTypeWillChange] - public function getIterator() - { - $data = $this->fetchAll(); - - return new ArrayIterator($data); - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. - */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) - { - if (! isset($this->data[$this->num])) { - return false; - } - - $row = $this->data[$this->num++]; - $fetchMode = $fetchMode ?: $this->defaultFetchMode; - - if ($fetchMode === FetchMode::ASSOCIATIVE) { - return $row; - } - - if ($fetchMode === FetchMode::NUMERIC) { - return array_values($row); - } - - if ($fetchMode === FetchMode::MIXED) { - return array_merge($row, array_values($row)); - } - - if ($fetchMode === FetchMode::COLUMN) { - return reset($row); - } - - throw new InvalidArgumentException('Invalid fetch-style given for fetching result.'); - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. - */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) - { - $rows = []; - while ($row = $this->fetch($fetchMode)) { - $rows[] = $row; - } - - return $rows; - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchOne() instead. - */ - public function fetchColumn($columnIndex = 0) - { - $row = $this->fetch(FetchMode::NUMERIC); - - // TODO: verify that return false is the correct behavior - return $row[$columnIndex] ?? false; - } - - /** - * {@inheritdoc} - */ - public function fetchNumeric() - { - $row = $this->doFetch(); - - if ($row === false) { - return false; - } - - return array_values($row); - } - - /** - * {@inheritdoc} - */ - public function fetchAssociative() - { - return $this->doFetch(); - } - - /** - * {@inheritdoc} - */ - public function fetchOne() - { - $row = $this->doFetch(); - - if ($row === false) { - return false; - } - - return reset($row); - } - - /** - * {@inheritdoc} - */ - public function fetchAllNumeric(): array - { - return FetchUtils::fetchAllNumeric($this); - } - - /** - * {@inheritdoc} - */ - public function fetchAllAssociative(): array - { - return FetchUtils::fetchAllAssociative($this); - } - - /** - * {@inheritdoc} - */ - public function fetchFirstColumn(): array - { - return FetchUtils::fetchFirstColumn($this); - } - - public function free(): void - { - $this->data = []; - } - - /** - * @return mixed|false - */ - private function doFetch() - { - if (! isset($this->data[$this->num])) { - return false; - } - - return $this->data[$this->num++]; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php deleted file mode 100644 index 96e3219f8..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php +++ /dev/null @@ -1,361 +0,0 @@ ->|null */ - private $data; - - /** @var int */ - private $defaultFetchMode = FetchMode::MIXED; - - /** - * @param string $cacheKey - * @param string $realKey - * @param int $lifetime - */ - public function __construct(ResultStatement $stmt, Cache $resultCache, $cacheKey, $realKey, $lifetime) - { - $this->statement = $stmt; - $this->resultCache = $resultCache; - $this->cacheKey = $cacheKey; - $this->realKey = $realKey; - $this->lifetime = $lifetime; - } - - /** - * {@inheritdoc} - * - * @deprecated Use free() instead. - */ - public function closeCursor() - { - $this->free(); - - return true; - } - - /** - * {@inheritdoc} - */ - public function columnCount() - { - return $this->statement->columnCount(); - } - - /** - * {@inheritdoc} - * - * @deprecated Use one of the fetch- or iterate-related methods. - */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) - { - $this->defaultFetchMode = $fetchMode; - - return true; - } - - /** - * {@inheritdoc} - * - * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. - */ - #[ReturnTypeWillChange] - public function getIterator() - { - $data = $this->fetchAll(); - - return new ArrayIterator($data); - } - - /** - * Be warned that you will need to call this method until no rows are - * available for caching to happen. - * - * {@inheritdoc} - * - * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. - */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) - { - if ($this->data === null) { - $this->data = []; - } - - $row = $this->statement->fetch(FetchMode::ASSOCIATIVE); - - if ($row) { - $this->data[] = $row; - - $fetchMode = $fetchMode ?: $this->defaultFetchMode; - - if ($fetchMode === FetchMode::ASSOCIATIVE) { - return $row; - } - - if ($fetchMode === FetchMode::NUMERIC) { - return array_values($row); - } - - if ($fetchMode === FetchMode::MIXED) { - return array_merge($row, array_values($row)); - } - - if ($fetchMode === FetchMode::COLUMN) { - return reset($row); - } - - throw new InvalidArgumentException('Invalid fetch-style given for caching result.'); - } - - $this->saveToCache(); - - return false; - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. - */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) - { - $data = $this->statement->fetchAll(FetchMode::ASSOCIATIVE, $fetchArgument, $ctorArgs); - - $this->data = $data; - - $this->saveToCache(); - - if ($fetchMode === FetchMode::NUMERIC) { - foreach ($data as $i => $row) { - $data[$i] = array_values($row); - } - } elseif ($fetchMode === FetchMode::MIXED) { - foreach ($data as $i => $row) { - $data[$i] = array_merge($row, array_values($row)); - } - } elseif ($fetchMode === FetchMode::COLUMN) { - foreach ($data as $i => $row) { - $data[$i] = reset($row); - } - } - - return $data; - } - - /** - * Be warned that you will need to call this method until no rows are - * available for caching to happen. - * - * {@inheritdoc} - * - * @deprecated Use fetchOne() instead. - */ - public function fetchColumn($columnIndex = 0) - { - $row = $this->fetch(FetchMode::NUMERIC); - - // TODO: verify that return false is the correct behavior - return $row[$columnIndex] ?? false; - } - - /** - * Be warned that you will need to call this method until no rows are - * available for caching to happen. - * - * {@inheritdoc} - */ - public function fetchNumeric() - { - $row = $this->doFetch(); - - if ($row === false) { - return false; - } - - return array_values($row); - } - - /** - * Be warned that you will need to call this method until no rows are - * available for caching to happen. - * - * {@inheritdoc} - */ - public function fetchAssociative() - { - return $this->doFetch(); - } - - /** - * Be warned that you will need to call this method until no rows are - * available for caching to happen. - * - * {@inheritdoc} - */ - public function fetchOne() - { - return FetchUtils::fetchOne($this); - } - - /** - * {@inheritdoc} - */ - public function fetchAllNumeric(): array - { - if ($this->statement instanceof Result) { - $data = $this->statement->fetchAllAssociative(); - } else { - $data = $this->statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - $this->data = $data; - - $this->saveToCache(); - - return array_map('array_values', $data); - } - - /** - * {@inheritdoc} - */ - public function fetchAllAssociative(): array - { - if ($this->statement instanceof Result) { - $data = $this->statement->fetchAllAssociative(); - } else { - $data = $this->statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - $this->data = $data; - - $this->saveToCache(); - - return $data; - } - - /** - * {@inheritdoc} - */ - public function fetchFirstColumn(): array - { - return FetchUtils::fetchFirstColumn($this); - } - - /** - * Returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement - * executed by the corresponding object. - * - * If the last SQL statement executed by the associated Statement object was a SELECT statement, - * some databases may return the number of rows returned by that statement. However, - * this behaviour is not guaranteed for all databases and should not be - * relied on for portable applications. - * - * @return int The number of rows. - */ - public function rowCount() - { - assert($this->statement instanceof Statement); - - return $this->statement->rowCount(); - } - - public function free(): void - { - $this->data = null; - } - - /** - * @return array|false - * - * @throws Exception - */ - private function doFetch() - { - if ($this->data === null) { - $this->data = []; - } - - if ($this->statement instanceof Result) { - $row = $this->statement->fetchAssociative(); - } else { - $row = $this->statement->fetch(FetchMode::ASSOCIATIVE); - } - - if ($row !== false) { - $this->data[] = $row; - - return $row; - } - - $this->saveToCache(); - - return false; - } - - private function saveToCache(): void - { - if ($this->data === null) { - return; - } - - $data = $this->resultCache->fetch($this->cacheKey); - if (! $data) { - $data = []; - } - - $data[$this->realKey] = $this->data; - - $this->resultCache->save($this->cacheKey, $data, $this->lifetime); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/ColumnCase.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/ColumnCase.php deleted file mode 100644 index c26aac162..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/ColumnCase.php +++ /dev/null @@ -1,34 +0,0 @@ -_attributes['sqlLogger'] = $logger; - } - - /** - * Gets the SQL logger that is used. - * - * @return SQLLogger|null - */ - public function getSQLLogger() - { - return $this->_attributes['sqlLogger'] ?? null; - } - - /** - * Gets the cache driver implementation that is used for query result caching. - * - * @return Cache|null - */ - public function getResultCacheImpl() - { - return $this->_attributes['resultCacheImpl'] ?? null; - } - - /** - * Sets the cache driver implementation that is used for query result caching. - * - * @return void - */ - public function setResultCacheImpl(Cache $cacheImpl) - { - $this->_attributes['resultCacheImpl'] = $cacheImpl; - } - - /** - * Sets the filter schema assets expression. - * - * Only include tables/sequences matching the filter expression regexp in - * schema instances generated for the active connection when calling - * {AbstractSchemaManager#createSchema()}. - * - * @deprecated Use Configuration::setSchemaAssetsFilter() instead - * - * @param string|null $filterExpression - * - * @return void - */ - public function setFilterSchemaAssetsExpression($filterExpression) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3316', - 'Configuration::setFilterSchemaAssetsExpression() is deprecated, use setSchemaAssetsFilter() instead.' - ); - - $this->_attributes['filterSchemaAssetsExpression'] = $filterExpression; - if ($filterExpression) { - $this->_attributes['filterSchemaAssetsExpressionCallable'] - = $this->buildSchemaAssetsFilterFromExpression($filterExpression); - } else { - $this->_attributes['filterSchemaAssetsExpressionCallable'] = null; - } - } - - /** - * Returns filter schema assets expression. - * - * @deprecated Use Configuration::getSchemaAssetsFilter() instead - * - * @return string|null - */ - public function getFilterSchemaAssetsExpression() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3316', - 'Configuration::getFilterSchemaAssetsExpression() is deprecated, use getSchemaAssetsFilter() instead.' - ); - - return $this->_attributes['filterSchemaAssetsExpression'] ?? null; - } - - /** - * @param string $filterExpression - * - * @return callable(string|AbstractAsset) - */ - private function buildSchemaAssetsFilterFromExpression($filterExpression): callable - { - return static function ($assetName) use ($filterExpression) { - if ($assetName instanceof AbstractAsset) { - $assetName = $assetName->getName(); - } - - return preg_match($filterExpression, $assetName); - }; - } - - /** - * Sets the callable to use to filter schema assets. - */ - public function setSchemaAssetsFilter(?callable $callable = null): ?callable - { - $this->_attributes['filterSchemaAssetsExpression'] = null; - - return $this->_attributes['filterSchemaAssetsExpressionCallable'] = $callable; - } - - /** - * Returns the callable to use to filter schema assets. - */ - public function getSchemaAssetsFilter(): ?callable - { - return $this->_attributes['filterSchemaAssetsExpressionCallable'] ?? null; - } - - /** - * Sets the default auto-commit mode for connections. - * - * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual - * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either - * the method commit or the method rollback. By default, new connections are in auto-commit mode. - * - * @see getAutoCommit - * - * @param bool $autoCommit True to enable auto-commit mode; false to disable it. - * - * @return void - */ - public function setAutoCommit($autoCommit) - { - $this->_attributes['autoCommit'] = (bool) $autoCommit; - } - - /** - * Returns the default auto-commit mode for connections. - * - * @see setAutoCommit - * - * @return bool True if auto-commit mode is enabled by default for connections, false otherwise. - */ - public function getAutoCommit() - { - return $this->_attributes['autoCommit'] ?? true; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php deleted file mode 100644 index d2f5b8a9b..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php +++ /dev/null @@ -1,2271 +0,0 @@ - - * @phpstan-var array - * @psalm-var Params - */ - private $params; - - /** - * The database platform object used by the connection or NULL before it's initialized. - * - * @var AbstractPlatform|null - */ - private $platform; - - /** - * The schema manager. - * - * @var AbstractSchemaManager|null - */ - protected $_schemaManager; - - /** - * The used DBAL driver. - * - * @var Driver - */ - protected $_driver; - - /** - * Flag that indicates whether the current transaction is marked for rollback only. - * - * @var bool - */ - private $isRollbackOnly = false; - - /** @var int */ - protected $defaultFetchMode = FetchMode::ASSOCIATIVE; - - /** - * Initializes a new instance of the Connection class. - * - * @internal The connection can be only instantiated by the driver manager. - * - * @param array $params The connection parameters. - * @param Driver $driver The driver to use. - * @param Configuration|null $config The configuration, optional. - * @param EventManager|null $eventManager The event manager, optional. - * @psalm-param Params $params - * @phpstan-param array $params - * - * @throws Exception - */ - public function __construct( - array $params, - Driver $driver, - ?Configuration $config = null, - ?EventManager $eventManager = null - ) { - $this->_driver = $driver; - $this->params = $params; - - if (isset($params['pdo'])) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3554', - 'Passing a user provided PDO instance directly to Doctrine is deprecated.' - ); - - if (! $params['pdo'] instanceof PDO) { - throw Exception::invalidPdoInstance(); - } - - $this->_conn = $params['pdo']; - $this->_conn->setAttribute(PDO::ATTR_STATEMENT_CLASS, [PDODriverStatement::class, []]); - unset($this->params['pdo']); - } - - if (isset($params['platform'])) { - if (! $params['platform'] instanceof Platforms\AbstractPlatform) { - throw Exception::invalidPlatformType($params['platform']); - } - - $this->platform = $params['platform']; - } - - // Create default config and event manager if none given - if (! $config) { - $config = new Configuration(); - } - - if (! $eventManager) { - $eventManager = new EventManager(); - } - - $this->_config = $config; - $this->_eventManager = $eventManager; - - $this->_expr = new Query\Expression\ExpressionBuilder($this); - - $this->autoCommit = $config->getAutoCommit(); - } - - /** - * Gets the parameters used during instantiation. - * - * @internal - * - * @return array - * @psalm-return Params - * @phpstan-return array - */ - public function getParams() - { - return $this->params; - } - - /** - * Gets the name of the database this Connection is connected to. - * - * @return string - */ - public function getDatabase() - { - return $this->_driver->getDatabase($this); - } - - /** - * Gets the hostname of the currently connected database. - * - * @deprecated - * - * @return string|null - */ - public function getHost() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'Connection::getHost() is deprecated, get the database server host from application config ' . - 'or as a last resort from internal Connection::getParams() API.' - ); - - return $this->params['host'] ?? null; - } - - /** - * Gets the port of the currently connected database. - * - * @deprecated - * - * @return mixed - */ - public function getPort() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'Connection::getPort() is deprecated, get the database server port from application config ' . - 'or as a last resort from internal Connection::getParams() API.' - ); - - return $this->params['port'] ?? null; - } - - /** - * Gets the username used by this connection. - * - * @deprecated - * - * @return string|null - */ - public function getUsername() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'Connection::getUsername() is deprecated, get the username from application config ' . - 'or as a last resort from internal Connection::getParams() API.' - ); - - return $this->params['user'] ?? null; - } - - /** - * Gets the password used by this connection. - * - * @deprecated - * - * @return string|null - */ - public function getPassword() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'Connection::getPassword() is deprecated, get the password from application config ' . - 'or as a last resort from internal Connection::getParams() API.' - ); - - return $this->params['password'] ?? null; - } - - /** - * Gets the DBAL driver instance. - * - * @return Driver - */ - public function getDriver() - { - return $this->_driver; - } - - /** - * Gets the Configuration used by the Connection. - * - * @return Configuration - */ - public function getConfiguration() - { - return $this->_config; - } - - /** - * Gets the EventManager used by the Connection. - * - * @return EventManager - */ - public function getEventManager() - { - return $this->_eventManager; - } - - /** - * Gets the DatabasePlatform for the connection. - * - * @return AbstractPlatform - * - * @throws Exception - */ - public function getDatabasePlatform() - { - if ($this->platform === null) { - $this->platform = $this->detectDatabasePlatform(); - $this->platform->setEventManager($this->_eventManager); - } - - return $this->platform; - } - - /** - * Gets the ExpressionBuilder for the connection. - * - * @return ExpressionBuilder - */ - public function getExpressionBuilder() - { - return $this->_expr; - } - - /** - * Establishes the connection with the database. - * - * @return bool TRUE if the connection was successfully established, FALSE if - * the connection is already open. - */ - public function connect() - { - if ($this->_conn !== null) { - return false; - } - - $driverOptions = $this->params['driverOptions'] ?? []; - $user = $this->params['user'] ?? null; - $password = $this->params['password'] ?? null; - - $this->_conn = $this->_driver->connect($this->params, $user, $password, $driverOptions); - - $this->transactionNestingLevel = 0; - - if ($this->autoCommit === false) { - $this->beginTransaction(); - } - - if ($this->_eventManager->hasListeners(Events::postConnect)) { - $eventArgs = new Event\ConnectionEventArgs($this); - $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); - } - - return true; - } - - /** - * Detects and sets the database platform. - * - * Evaluates custom platform class and version in order to set the correct platform. - * - * @throws Exception If an invalid platform was specified for this connection. - */ - private function detectDatabasePlatform(): AbstractPlatform - { - $version = $this->getDatabasePlatformVersion(); - - if ($version !== null) { - assert($this->_driver instanceof VersionAwarePlatformDriver); - - return $this->_driver->createDatabasePlatformForVersion($version); - } - - return $this->_driver->getDatabasePlatform(); - } - - /** - * Returns the version of the related platform if applicable. - * - * Returns null if either the driver is not capable to create version - * specific platform instances, no explicit server version was specified - * or the underlying driver connection cannot determine the platform - * version without having to query it (performance reasons). - * - * @return string|null - * - * @throws Throwable - */ - private function getDatabasePlatformVersion() - { - // Driver does not support version specific platforms. - if (! $this->_driver instanceof VersionAwarePlatformDriver) { - return null; - } - - // Explicit platform version requested (supersedes auto-detection). - if (isset($this->params['serverVersion'])) { - return $this->params['serverVersion']; - } - - // If not connected, we need to connect now to determine the platform version. - if ($this->_conn === null) { - try { - $this->connect(); - } catch (Throwable $originalException) { - if (empty($this->params['dbname'])) { - throw $originalException; - } - - // The database to connect to might not yet exist. - // Retry detection without database name connection parameter. - $params = $this->params; - - unset($this->params['dbname']); - - try { - $this->connect(); - } catch (Throwable $fallbackException) { - // Either the platform does not support database-less connections - // or something else went wrong. - throw $originalException; - } finally { - $this->params = $params; - } - - $serverVersion = $this->getServerVersion(); - - // Close "temporary" connection to allow connecting to the real database again. - $this->close(); - - return $serverVersion; - } - } - - return $this->getServerVersion(); - } - - /** - * Returns the database server version if the underlying driver supports it. - * - * @return string|null - */ - private function getServerVersion() - { - $connection = $this->getWrappedConnection(); - - // Automatic platform version detection. - if ($connection instanceof ServerInfoAwareConnection && ! $connection->requiresQueryForServerVersion()) { - return $connection->getServerVersion(); - } - - // Unable to detect platform version. - return null; - } - - /** - * Returns the current auto-commit mode for this connection. - * - * @see setAutoCommit - * - * @return bool True if auto-commit mode is currently enabled for this connection, false otherwise. - */ - public function isAutoCommit() - { - return $this->autoCommit === true; - } - - /** - * Sets auto-commit mode for this connection. - * - * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual - * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either - * the method commit or the method rollback. By default, new connections are in auto-commit mode. - * - * NOTE: If this method is called during a transaction and the auto-commit mode is changed, the transaction is - * committed. If this method is called and the auto-commit mode is not changed, the call is a no-op. - * - * @see isAutoCommit - * - * @param bool $autoCommit True to enable auto-commit mode; false to disable it. - * - * @return void - */ - public function setAutoCommit($autoCommit) - { - $autoCommit = (bool) $autoCommit; - - // Mode not changed, no-op. - if ($autoCommit === $this->autoCommit) { - return; - } - - $this->autoCommit = $autoCommit; - - // Commit all currently active transactions if any when switching auto-commit mode. - if ($this->_conn === null || $this->transactionNestingLevel === 0) { - return; - } - - $this->commitAll(); - } - - /** - * Sets the fetch mode. - * - * @deprecated Use one of the fetch- or iterate-related methods. - * - * @param int $fetchMode - * - * @return void - */ - public function setFetchMode($fetchMode) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Default Fetch Mode configuration is deprecated, use explicit Connection::fetch*() APIs instead.' - ); - - $this->defaultFetchMode = $fetchMode; - } - - /** - * Prepares and executes an SQL query and returns the first row of the result - * as an associative array. - * - * @deprecated Use fetchAssociative() - * - * @param string $sql SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return array|false False is returned if no rows are found. - * - * @throws Exception - */ - public function fetchAssoc($sql, array $params = [], array $types = []) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Connection::fetchAssoc() is deprecated, use Connection::fetchAssociative() API instead.' - ); - - return $this->executeQuery($sql, $params, $types)->fetch(FetchMode::ASSOCIATIVE); - } - - /** - * Prepares and executes an SQL query and returns the first row of the result - * as a numerically indexed array. - * - * @deprecated Use fetchNumeric() - * - * @param string $sql SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return array|false False is returned if no rows are found. - */ - public function fetchArray($sql, array $params = [], array $types = []) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Connection::fetchArray() is deprecated, use Connection::fetchNumeric() API instead.' - ); - - return $this->executeQuery($sql, $params, $types)->fetch(FetchMode::NUMERIC); - } - - /** - * Prepares and executes an SQL query and returns the value of a single column - * of the first row of the result. - * - * @deprecated Use fetchOne() instead. - * - * @param string $sql SQL query - * @param array|array $params Query parameters - * @param int $column 0-indexed column number - * @param array|array $types Parameter types - * - * @return mixed|false False is returned if no rows are found. - * - * @throws Exception - */ - public function fetchColumn($sql, array $params = [], $column = 0, array $types = []) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Connection::fetchColumn() is deprecated, use Connection::fetchOne() API instead.' - ); - - return $this->executeQuery($sql, $params, $types)->fetchColumn($column); - } - - /** - * Prepares and executes an SQL query and returns the first row of the result - * as an associative array. - * - * @param string $query SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return array|false False is returned if no rows are found. - * - * @throws Exception - */ - public function fetchAssociative(string $query, array $params = [], array $types = []) - { - try { - $stmt = $this->ensureForwardCompatibilityStatement( - $this->executeQuery($query, $params, $types) - ); - - return $stmt->fetchAssociative(); - } catch (Throwable $e) { - $this->handleExceptionDuringQuery($e, $query, $params, $types); - } - } - - /** - * Prepares and executes an SQL query and returns the first row of the result - * as a numerically indexed array. - * - * @param string $query SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return array|false False is returned if no rows are found. - * - * @throws Exception - */ - public function fetchNumeric(string $query, array $params = [], array $types = []) - { - try { - $stmt = $this->ensureForwardCompatibilityStatement( - $this->executeQuery($query, $params, $types) - ); - - return $stmt->fetchNumeric(); - } catch (Throwable $e) { - $this->handleExceptionDuringQuery($e, $query, $params, $types); - } - } - - /** - * Prepares and executes an SQL query and returns the value of a single column - * of the first row of the result. - * - * @param string $query SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return mixed|false False is returned if no rows are found. - * - * @throws Exception - */ - public function fetchOne(string $query, array $params = [], array $types = []) - { - try { - $stmt = $this->ensureForwardCompatibilityStatement( - $this->executeQuery($query, $params, $types) - ); - - return $stmt->fetchOne(); - } catch (Throwable $e) { - $this->handleExceptionDuringQuery($e, $query, $params, $types); - } - } - - /** - * Whether an actual connection to the database is established. - * - * @return bool - */ - public function isConnected() - { - return $this->_conn !== null; - } - - /** - * Checks whether a transaction is currently active. - * - * @return bool TRUE if a transaction is currently active, FALSE otherwise. - */ - public function isTransactionActive() - { - return $this->transactionNestingLevel > 0; - } - - /** - * Adds condition based on the criteria to the query components - * - * @param array $criteria Map of key columns to their values - * @param string[] $columns Column names - * @param mixed[] $values Column values - * @param string[] $conditions Key conditions - * - * @throws Exception - */ - private function addCriteriaCondition( - array $criteria, - array &$columns, - array &$values, - array &$conditions - ): void { - $platform = $this->getDatabasePlatform(); - - foreach ($criteria as $columnName => $value) { - if ($value === null) { - $conditions[] = $platform->getIsNullExpression($columnName); - continue; - } - - $columns[] = $columnName; - $values[] = $value; - $conditions[] = $columnName . ' = ?'; - } - } - - /** - * Executes an SQL DELETE statement on a table. - * - * Table expression and columns are not escaped and are not safe for user-input. - * - * @param string $table Table name - * @param array $criteria Deletion criteria - * @param array|array $types Parameter types - * - * @return int The number of affected rows. - * - * @throws Exception - */ - public function delete($table, array $criteria, array $types = []) - { - if (empty($criteria)) { - throw InvalidArgumentException::fromEmptyCriteria(); - } - - $columns = $values = $conditions = []; - - $this->addCriteriaCondition($criteria, $columns, $values, $conditions); - - return $this->executeStatement( - 'DELETE FROM ' . $table . ' WHERE ' . implode(' AND ', $conditions), - $values, - is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types - ); - } - - /** - * Closes the connection. - * - * @return void - */ - public function close() - { - $this->_conn = null; - } - - /** - * Sets the transaction isolation level. - * - * @param int $level The level to set. - * - * @return int - */ - public function setTransactionIsolation($level) - { - $this->transactionIsolationLevel = $level; - - return $this->executeStatement($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level)); - } - - /** - * Gets the currently active transaction isolation level. - * - * @return int The current transaction isolation level. - */ - public function getTransactionIsolation() - { - if ($this->transactionIsolationLevel === null) { - $this->transactionIsolationLevel = $this->getDatabasePlatform()->getDefaultTransactionIsolationLevel(); - } - - return $this->transactionIsolationLevel; - } - - /** - * Executes an SQL UPDATE statement on a table. - * - * Table expression and columns are not escaped and are not safe for user-input. - * - * @param string $table Table name - * @param array $data Column-value pairs - * @param array $criteria Update criteria - * @param array|array $types Parameter types - * - * @return int The number of affected rows. - * - * @throws Exception - */ - public function update($table, array $data, array $criteria, array $types = []) - { - $columns = $values = $conditions = $set = []; - - foreach ($data as $columnName => $value) { - $columns[] = $columnName; - $values[] = $value; - $set[] = $columnName . ' = ?'; - } - - $this->addCriteriaCondition($criteria, $columns, $values, $conditions); - - if (is_string(key($types))) { - $types = $this->extractTypeValues($columns, $types); - } - - $sql = 'UPDATE ' . $table . ' SET ' . implode(', ', $set) - . ' WHERE ' . implode(' AND ', $conditions); - - return $this->executeStatement($sql, $values, $types); - } - - /** - * Inserts a table row with specified data. - * - * Table expression and columns are not escaped and are not safe for user-input. - * - * @param string $table Table name - * @param array $data Column-value pairs - * @param array|array $types Parameter types - * - * @return int The number of affected rows. - * - * @throws Exception - */ - public function insert($table, array $data, array $types = []) - { - if (empty($data)) { - return $this->executeStatement('INSERT INTO ' . $table . ' () VALUES ()'); - } - - $columns = []; - $values = []; - $set = []; - - foreach ($data as $columnName => $value) { - $columns[] = $columnName; - $values[] = $value; - $set[] = '?'; - } - - return $this->executeStatement( - 'INSERT INTO ' . $table . ' (' . implode(', ', $columns) . ')' . - ' VALUES (' . implode(', ', $set) . ')', - $values, - is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types - ); - } - - /** - * Extract ordered type list from an ordered column list and type map. - * - * @param array $columnList - * @param array|array $types - * - * @return array|array - */ - private function extractTypeValues(array $columnList, array $types) - { - $typeValues = []; - - foreach ($columnList as $columnIndex => $columnName) { - $typeValues[] = $types[$columnName] ?? ParameterType::STRING; - } - - return $typeValues; - } - - /** - * Quotes a string so it can be safely used as a table or column name, even if - * it is a reserved name. - * - * Delimiting style depends on the underlying database platform that is being used. - * - * NOTE: Just because you CAN use quoted identifiers does not mean - * you SHOULD use them. In general, they end up causing way more - * problems than they solve. - * - * @param string $str The name to be quoted. - * - * @return string The quoted name. - */ - public function quoteIdentifier($str) - { - return $this->getDatabasePlatform()->quoteIdentifier($str); - } - - /** - * {@inheritDoc} - * - * @param mixed $value - * @param int|string|Type|null $type - */ - public function quote($value, $type = ParameterType::STRING) - { - $connection = $this->getWrappedConnection(); - - [$value, $bindingType] = $this->getBindingInfo($value, $type); - - return $connection->quote($value, $bindingType); - } - - /** - * Prepares and executes an SQL query and returns the result as an associative array. - * - * @deprecated Use fetchAllAssociative() - * - * @param string $sql The SQL query. - * @param mixed[] $params The query parameters. - * @param int[]|string[] $types The query parameter types. - * - * @return mixed[] - */ - public function fetchAll($sql, array $params = [], $types = []) - { - return $this->executeQuery($sql, $params, $types)->fetchAll(); - } - - /** - * Prepares and executes an SQL query and returns the result as an array of numeric arrays. - * - * @param string $query SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return array> - * - * @throws Exception - */ - public function fetchAllNumeric(string $query, array $params = [], array $types = []): array - { - try { - $stmt = $this->ensureForwardCompatibilityStatement( - $this->executeQuery($query, $params, $types) - ); - - return $stmt->fetchAllNumeric(); - } catch (Throwable $e) { - $this->handleExceptionDuringQuery($e, $query, $params, $types); - } - } - - /** - * Prepares and executes an SQL query and returns the result as an array of associative arrays. - * - * @param string $query SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return array> - * - * @throws Exception - */ - public function fetchAllAssociative(string $query, array $params = [], array $types = []): array - { - try { - $stmt = $this->ensureForwardCompatibilityStatement( - $this->executeQuery($query, $params, $types) - ); - - return $stmt->fetchAllAssociative(); - } catch (Throwable $e) { - $this->handleExceptionDuringQuery($e, $query, $params, $types); - } - } - - /** - * Prepares and executes an SQL query and returns the result as an associative array with the keys - * mapped to the first column and the values mapped to the second column. - * - * @param string $query SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return array - * - * @throws Exception - */ - public function fetchAllKeyValue(string $query, array $params = [], array $types = []): array - { - $stmt = $this->executeQuery($query, $params, $types); - - $this->ensureHasKeyValue($stmt); - - $data = []; - - foreach ($stmt->fetchAll(FetchMode::NUMERIC) as [$key, $value]) { - $data[$key] = $value; - } - - return $data; - } - - /** - * Prepares and executes an SQL query and returns the result as an associative array with the keys mapped - * to the first column and the values being an associative array representing the rest of the columns - * and their values. - * - * @param string $query SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return array> - * - * @throws Exception - */ - public function fetchAllAssociativeIndexed(string $query, array $params = [], array $types = []): array - { - $stmt = $this->executeQuery($query, $params, $types); - - $data = []; - - foreach ($stmt->fetchAll(FetchMode::ASSOCIATIVE) as $row) { - $data[array_shift($row)] = $row; - } - - return $data; - } - - /** - * Prepares and executes an SQL query and returns the result as an array of the first column values. - * - * @param string $query SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return array - * - * @throws Exception - */ - public function fetchFirstColumn(string $query, array $params = [], array $types = []): array - { - try { - $stmt = $this->ensureForwardCompatibilityStatement( - $this->executeQuery($query, $params, $types) - ); - - return $stmt->fetchFirstColumn(); - } catch (Throwable $e) { - $this->handleExceptionDuringQuery($e, $query, $params, $types); - } - } - - /** - * Prepares and executes an SQL query and returns the result as an iterator over rows represented as numeric arrays. - * - * @param string $query SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return Traversable> - * - * @throws Exception - */ - public function iterateNumeric(string $query, array $params = [], array $types = []): Traversable - { - try { - $stmt = $this->ensureForwardCompatibilityStatement( - $this->executeQuery($query, $params, $types) - ); - - yield from $stmt->iterateNumeric(); - } catch (Throwable $e) { - $this->handleExceptionDuringQuery($e, $query, $params, $types); - } - } - - /** - * Prepares and executes an SQL query and returns the result as an iterator over rows represented - * as associative arrays. - * - * @param string $query SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return Traversable> - * - * @throws Exception - */ - public function iterateAssociative(string $query, array $params = [], array $types = []): Traversable - { - try { - $stmt = $this->ensureForwardCompatibilityStatement( - $this->executeQuery($query, $params, $types) - ); - - yield from $stmt->iterateAssociative(); - } catch (Throwable $e) { - $this->handleExceptionDuringQuery($e, $query, $params, $types); - } - } - - /** - * Prepares and executes an SQL query and returns the result as an iterator with the keys - * mapped to the first column and the values mapped to the second column. - * - * @param string $query SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return Traversable - * - * @throws Exception - */ - public function iterateKeyValue(string $query, array $params = [], array $types = []): Traversable - { - $stmt = $this->executeQuery($query, $params, $types); - - $this->ensureHasKeyValue($stmt); - - while (($row = $stmt->fetch(FetchMode::NUMERIC)) !== false) { - yield $row[0] => $row[1]; - } - } - - /** - * Prepares and executes an SQL query and returns the result as an iterator with the keys mapped - * to the first column and the values being an associative array representing the rest of the columns - * and their values. - * - * @param string $query SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return Traversable> - * - * @throws Exception - */ - public function iterateAssociativeIndexed(string $query, array $params = [], array $types = []): Traversable - { - $stmt = $this->executeQuery($query, $params, $types); - - while (($row = $stmt->fetch(FetchMode::ASSOCIATIVE)) !== false) { - yield array_shift($row) => $row; - } - } - - /** - * Prepares and executes an SQL query and returns the result as an iterator over the first column values. - * - * @param string $query SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return Traversable - * - * @throws Exception - */ - public function iterateColumn(string $query, array $params = [], array $types = []): Traversable - { - try { - $stmt = $this->ensureForwardCompatibilityStatement( - $this->executeQuery($query, $params, $types) - ); - - yield from $stmt->iterateColumn(); - } catch (Throwable $e) { - $this->handleExceptionDuringQuery($e, $query, $params, $types); - } - } - - /** - * Prepares an SQL statement. - * - * @param string $sql The SQL statement to prepare. - * - * @return Statement The prepared statement. - * - * @throws Exception - */ - public function prepare($sql) - { - try { - $stmt = new Statement($sql, $this); - } catch (Throwable $e) { - $this->handleExceptionDuringQuery($e, $sql); - } - - $stmt->setFetchMode($this->defaultFetchMode); - - return $stmt; - } - - /** - * Executes an, optionally parametrized, SQL query. - * - * If the query is parametrized, a prepared statement is used. - * If an SQLLogger is configured, the execution is logged. - * - * @param string $sql SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return ForwardCompatibility\DriverStatement|ForwardCompatibility\DriverResultStatement - * - * The executed statement or the cached result statement if a query cache profile is used - * - * @throws Exception - */ - public function executeQuery($sql, array $params = [], $types = [], ?QueryCacheProfile $qcp = null) - { - if ($qcp !== null) { - return $this->executeCacheQuery($sql, $params, $types, $qcp); - } - - $connection = $this->getWrappedConnection(); - - $logger = $this->_config->getSQLLogger(); - if ($logger) { - $logger->startQuery($sql, $params, $types); - } - - try { - if ($params) { - [$sql, $params, $types] = SQLParserUtils::expandListParameters($sql, $params, $types); - - $stmt = $connection->prepare($sql); - if ($types) { - $this->_bindTypedValues($stmt, $params, $types); - $stmt->execute(); - } else { - $stmt->execute($params); - } - } else { - $stmt = $connection->query($sql); - } - } catch (Throwable $e) { - $this->handleExceptionDuringQuery( - $e, - $sql, - $params, - $types - ); - } - - $stmt->setFetchMode($this->defaultFetchMode); - - if ($logger) { - $logger->stopQuery(); - } - - return $this->ensureForwardCompatibilityStatement($stmt); - } - - /** - * Executes a caching query. - * - * @param string $sql SQL query - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return ForwardCompatibility\DriverResultStatement - * - * @throws CacheException - */ - public function executeCacheQuery($sql, $params, $types, QueryCacheProfile $qcp) - { - $resultCache = $qcp->getResultCacheDriver() ?? $this->_config->getResultCacheImpl(); - - if ($resultCache === null) { - throw CacheException::noResultDriverConfigured(); - } - - $connectionParams = $this->params; - unset($connectionParams['platform']); - - [$cacheKey, $realKey] = $qcp->generateCacheKeys($sql, $params, $types, $connectionParams); - - // fetch the row pointers entry - $data = $resultCache->fetch($cacheKey); - - if ($data !== false) { - // is the real key part of this row pointers map or is the cache only pointing to other cache keys? - if (isset($data[$realKey])) { - $stmt = new ArrayStatement($data[$realKey]); - } elseif (array_key_exists($realKey, $data)) { - $stmt = new ArrayStatement([]); - } - } - - if (! isset($stmt)) { - $stmt = new ResultCacheStatement( - $this->executeQuery($sql, $params, $types), - $resultCache, - $cacheKey, - $realKey, - $qcp->getLifetime() - ); - } - - $stmt->setFetchMode($this->defaultFetchMode); - - return $this->ensureForwardCompatibilityStatement($stmt); - } - - /** - * @return ForwardCompatibility\Result - */ - private function ensureForwardCompatibilityStatement(ResultStatement $stmt) - { - return ForwardCompatibility\Result::ensure($stmt); - } - - /** - * Executes an, optionally parametrized, SQL query and returns the result, - * applying a given projection/transformation function on each row of the result. - * - * @deprecated - * - * @param string $sql The SQL query to execute. - * @param mixed[] $params The parameters, if any. - * @param Closure $function The transformation function that is applied on each row. - * The function receives a single parameter, an array, that - * represents a row of the result set. - * - * @return mixed[] The projected result of the query. - */ - public function project($sql, array $params, Closure $function) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3823', - 'Connection::project() is deprecated without replacement, implement data projections in your own code.' - ); - - $result = []; - $stmt = $this->executeQuery($sql, $params); - - while ($row = $stmt->fetch()) { - $result[] = $function($row); - } - - $stmt->closeCursor(); - - return $result; - } - - /** - * Executes an SQL statement, returning a result set as a Statement object. - * - * @deprecated Use {@link executeQuery()} instead. - * - * @return \Doctrine\DBAL\Driver\Statement - * - * @throws Exception - */ - public function query() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4163', - 'Connection::query() is deprecated, use Connection::executeQuery() instead.' - ); - - $connection = $this->getWrappedConnection(); - - $args = func_get_args(); - - $logger = $this->_config->getSQLLogger(); - if ($logger) { - $logger->startQuery($args[0]); - } - - try { - $statement = $connection->query(...$args); - } catch (Throwable $e) { - $this->handleExceptionDuringQuery($e, $args[0]); - } - - $statement->setFetchMode($this->defaultFetchMode); - - if ($logger) { - $logger->stopQuery(); - } - - return $statement; - } - - /** - * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters - * and returns the number of affected rows. - * - * This method supports PDO binding types as well as DBAL mapping types. - * - * @deprecated Use {@link executeStatement()} instead. - * - * @param string $sql SQL statement - * @param array|array $params Statement parameters - * @param array|array $types Parameter types - * - * @return int The number of affected rows. - * - * @throws Exception - */ - public function executeUpdate($sql, array $params = [], array $types = []) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4163', - 'Connection::executeUpdate() is deprecated, use Connection::executeStatement() instead.' - ); - - return $this->executeStatement($sql, $params, $types); - } - - /** - * Executes an SQL statement with the given parameters and returns the number of affected rows. - * - * Could be used for: - * - DML statements: INSERT, UPDATE, DELETE, etc. - * - DDL statements: CREATE, DROP, ALTER, etc. - * - DCL statements: GRANT, REVOKE, etc. - * - Session control statements: ALTER SESSION, SET, DECLARE, etc. - * - Other statements that don't yield a row set. - * - * This method supports PDO binding types as well as DBAL mapping types. - * - * @param string $sql SQL statement - * @param array|array $params Statement parameters - * @param array|array $types Parameter types - * - * @return int The number of affected rows. - * - * @throws Exception - */ - public function executeStatement($sql, array $params = [], array $types = []) - { - $connection = $this->getWrappedConnection(); - - $logger = $this->_config->getSQLLogger(); - if ($logger) { - $logger->startQuery($sql, $params, $types); - } - - try { - if ($params) { - [$sql, $params, $types] = SQLParserUtils::expandListParameters($sql, $params, $types); - - $stmt = $connection->prepare($sql); - - if ($types) { - $this->_bindTypedValues($stmt, $params, $types); - $stmt->execute(); - } else { - $stmt->execute($params); - } - - $result = $stmt->rowCount(); - } else { - $result = $connection->exec($sql); - } - } catch (Throwable $e) { - $this->handleExceptionDuringQuery( - $e, - $sql, - $params, - $types - ); - } - - if ($logger) { - $logger->stopQuery(); - } - - return $result; - } - - /** - * Executes an SQL statement and return the number of affected rows. - * - * @deprecated Use {@link executeStatement()} instead. - * - * @param string $sql - * - * @return int The number of affected rows. - * - * @throws Exception - */ - public function exec($sql) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4163', - 'Connection::exec() is deprecated, use Connection::executeStatement() instead.' - ); - - $connection = $this->getWrappedConnection(); - - $logger = $this->_config->getSQLLogger(); - if ($logger) { - $logger->startQuery($sql); - } - - try { - $result = $connection->exec($sql); - } catch (Throwable $e) { - $this->handleExceptionDuringQuery($e, $sql); - } - - if ($logger) { - $logger->stopQuery(); - } - - return $result; - } - - /** - * Returns the current transaction nesting level. - * - * @return int The nesting level. A value of 0 means there's no active transaction. - */ - public function getTransactionNestingLevel() - { - return $this->transactionNestingLevel; - } - - /** - * Fetches the SQLSTATE associated with the last database operation. - * - * @deprecated The error information is available via exceptions. - * - * @return string|null The last error code. - */ - public function errorCode() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3507', - 'Connection::errorCode() is deprecated, use getCode() or getSQLState() on Exception instead.' - ); - - return $this->getWrappedConnection()->errorCode(); - } - - /** - * {@inheritDoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorInfo() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3507', - 'Connection::errorInfo() is deprecated, use getCode() or getSQLState() on Exception instead.' - ); - - return $this->getWrappedConnection()->errorInfo(); - } - - /** - * Returns the ID of the last inserted row, or the last value from a sequence object, - * depending on the underlying driver. - * - * Note: This method may not return a meaningful or consistent result across different drivers, - * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY - * columns or sequences. - * - * @param string|null $name Name of the sequence object from which the ID should be returned. - * - * @return string A string representation of the last inserted ID. - */ - public function lastInsertId($name = null) - { - return $this->getWrappedConnection()->lastInsertId($name); - } - - /** - * Executes a function in a transaction. - * - * The function gets passed this Connection instance as an (optional) parameter. - * - * If an exception occurs during execution of the function or transaction commit, - * the transaction is rolled back and the exception re-thrown. - * - * @param Closure $func The function to execute transactionally. - * - * @return mixed The value returned by $func - * - * @throws Throwable - */ - public function transactional(Closure $func) - { - $this->beginTransaction(); - try { - $res = $func($this); - $this->commit(); - - return $res; - } catch (Throwable $e) { - $this->rollBack(); - - throw $e; - } - } - - /** - * Sets if nested transactions should use savepoints. - * - * @param bool $nestTransactionsWithSavepoints - * - * @return void - * - * @throws ConnectionException - */ - public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints) - { - if ($this->transactionNestingLevel > 0) { - throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction(); - } - - if (! $this->getDatabasePlatform()->supportsSavepoints()) { - throw ConnectionException::savepointsNotSupported(); - } - - $this->nestTransactionsWithSavepoints = (bool) $nestTransactionsWithSavepoints; - } - - /** - * Gets if nested transactions should use savepoints. - * - * @return bool - */ - public function getNestTransactionsWithSavepoints() - { - return $this->nestTransactionsWithSavepoints; - } - - /** - * Returns the savepoint name to use for nested transactions are false if they are not supported - * "savepointFormat" parameter is not set - * - * @return mixed A string with the savepoint name or false. - */ - protected function _getNestedTransactionSavePointName() - { - return 'DOCTRINE2_SAVEPOINT_' . $this->transactionNestingLevel; - } - - /** - * {@inheritDoc} - */ - public function beginTransaction() - { - $connection = $this->getWrappedConnection(); - - ++$this->transactionNestingLevel; - - $logger = $this->_config->getSQLLogger(); - - if ($this->transactionNestingLevel === 1) { - if ($logger) { - $logger->startQuery('"START TRANSACTION"'); - } - - $connection->beginTransaction(); - - if ($logger) { - $logger->stopQuery(); - } - } elseif ($this->nestTransactionsWithSavepoints) { - if ($logger) { - $logger->startQuery('"SAVEPOINT"'); - } - - $this->createSavepoint($this->_getNestedTransactionSavePointName()); - if ($logger) { - $logger->stopQuery(); - } - } - - return true; - } - - /** - * {@inheritDoc} - * - * @throws ConnectionException If the commit failed due to no active transaction or - * because the transaction was marked for rollback only. - */ - public function commit() - { - if ($this->transactionNestingLevel === 0) { - throw ConnectionException::noActiveTransaction(); - } - - if ($this->isRollbackOnly) { - throw ConnectionException::commitFailedRollbackOnly(); - } - - $result = true; - - $connection = $this->getWrappedConnection(); - - $logger = $this->_config->getSQLLogger(); - - if ($this->transactionNestingLevel === 1) { - if ($logger) { - $logger->startQuery('"COMMIT"'); - } - - $result = $connection->commit(); - - if ($logger) { - $logger->stopQuery(); - } - } elseif ($this->nestTransactionsWithSavepoints) { - if ($logger) { - $logger->startQuery('"RELEASE SAVEPOINT"'); - } - - $this->releaseSavepoint($this->_getNestedTransactionSavePointName()); - if ($logger) { - $logger->stopQuery(); - } - } - - --$this->transactionNestingLevel; - - if ($this->autoCommit !== false || $this->transactionNestingLevel !== 0) { - return $result; - } - - $this->beginTransaction(); - - return $result; - } - - /** - * Commits all current nesting transactions. - */ - private function commitAll(): void - { - while ($this->transactionNestingLevel !== 0) { - if ($this->autoCommit === false && $this->transactionNestingLevel === 1) { - // When in no auto-commit mode, the last nesting commit immediately starts a new transaction. - // Therefore we need to do the final commit here and then leave to avoid an infinite loop. - $this->commit(); - - return; - } - - $this->commit(); - } - } - - /** - * Cancels any database changes done during the current transaction. - * - * @return bool - * - * @throws ConnectionException If the rollback operation failed. - */ - public function rollBack() - { - if ($this->transactionNestingLevel === 0) { - throw ConnectionException::noActiveTransaction(); - } - - $connection = $this->getWrappedConnection(); - - $logger = $this->_config->getSQLLogger(); - - if ($this->transactionNestingLevel === 1) { - if ($logger) { - $logger->startQuery('"ROLLBACK"'); - } - - $this->transactionNestingLevel = 0; - $connection->rollBack(); - $this->isRollbackOnly = false; - if ($logger) { - $logger->stopQuery(); - } - - if ($this->autoCommit === false) { - $this->beginTransaction(); - } - } elseif ($this->nestTransactionsWithSavepoints) { - if ($logger) { - $logger->startQuery('"ROLLBACK TO SAVEPOINT"'); - } - - $this->rollbackSavepoint($this->_getNestedTransactionSavePointName()); - --$this->transactionNestingLevel; - if ($logger) { - $logger->stopQuery(); - } - } else { - $this->isRollbackOnly = true; - --$this->transactionNestingLevel; - } - - return true; - } - - /** - * Creates a new savepoint. - * - * @param string $savepoint The name of the savepoint to create. - * - * @return void - * - * @throws ConnectionException - */ - public function createSavepoint($savepoint) - { - $platform = $this->getDatabasePlatform(); - - if (! $platform->supportsSavepoints()) { - throw ConnectionException::savepointsNotSupported(); - } - - $this->getWrappedConnection()->exec($platform->createSavePoint($savepoint)); - } - - /** - * Releases the given savepoint. - * - * @param string $savepoint The name of the savepoint to release. - * - * @return void - * - * @throws ConnectionException - */ - public function releaseSavepoint($savepoint) - { - $platform = $this->getDatabasePlatform(); - - if (! $platform->supportsSavepoints()) { - throw ConnectionException::savepointsNotSupported(); - } - - if (! $platform->supportsReleaseSavepoints()) { - return; - } - - $this->getWrappedConnection()->exec($platform->releaseSavePoint($savepoint)); - } - - /** - * Rolls back to the given savepoint. - * - * @param string $savepoint The name of the savepoint to rollback to. - * - * @return void - * - * @throws ConnectionException - */ - public function rollbackSavepoint($savepoint) - { - $platform = $this->getDatabasePlatform(); - - if (! $platform->supportsSavepoints()) { - throw ConnectionException::savepointsNotSupported(); - } - - $this->getWrappedConnection()->exec($platform->rollbackSavePoint($savepoint)); - } - - /** - * Gets the wrapped driver connection. - * - * @return DriverConnection - */ - public function getWrappedConnection() - { - $this->connect(); - - assert($this->_conn !== null); - - return $this->_conn; - } - - /** - * Gets the SchemaManager that can be used to inspect or change the - * database schema through the connection. - * - * @return AbstractSchemaManager - */ - public function getSchemaManager() - { - if ($this->_schemaManager === null) { - $this->_schemaManager = $this->_driver->getSchemaManager($this); - } - - return $this->_schemaManager; - } - - /** - * Marks the current transaction so that the only possible - * outcome for the transaction to be rolled back. - * - * @return void - * - * @throws ConnectionException If no transaction is active. - */ - public function setRollbackOnly() - { - if ($this->transactionNestingLevel === 0) { - throw ConnectionException::noActiveTransaction(); - } - - $this->isRollbackOnly = true; - } - - /** - * Checks whether the current transaction is marked for rollback only. - * - * @return bool - * - * @throws ConnectionException If no transaction is active. - */ - public function isRollbackOnly() - { - if ($this->transactionNestingLevel === 0) { - throw ConnectionException::noActiveTransaction(); - } - - return $this->isRollbackOnly; - } - - /** - * Converts a given value to its database representation according to the conversion - * rules of a specific DBAL mapping type. - * - * @param mixed $value The value to convert. - * @param string $type The name of the DBAL mapping type. - * - * @return mixed The converted value. - */ - public function convertToDatabaseValue($value, $type) - { - return Type::getType($type)->convertToDatabaseValue($value, $this->getDatabasePlatform()); - } - - /** - * Converts a given value to its PHP representation according to the conversion - * rules of a specific DBAL mapping type. - * - * @param mixed $value The value to convert. - * @param string $type The name of the DBAL mapping type. - * - * @return mixed The converted type. - */ - public function convertToPHPValue($value, $type) - { - return Type::getType($type)->convertToPHPValue($value, $this->getDatabasePlatform()); - } - - /** - * Binds a set of parameters, some or all of which are typed with a PDO binding type - * or DBAL mapping type, to a given statement. - * - * @internal Duck-typing used on the $stmt parameter to support driver statements as well as - * raw PDOStatement instances. - * - * @param \Doctrine\DBAL\Driver\Statement $stmt Prepared statement - * @param array|array $params Statement parameters - * @param array|array $types Parameter types - * - * @return void - */ - private function _bindTypedValues($stmt, array $params, array $types) - { - // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO. - if (is_int(key($params))) { - // Positional parameters - $typeOffset = array_key_exists(0, $types) ? -1 : 0; - $bindIndex = 1; - foreach ($params as $value) { - $typeIndex = $bindIndex + $typeOffset; - if (isset($types[$typeIndex])) { - $type = $types[$typeIndex]; - [$value, $bindingType] = $this->getBindingInfo($value, $type); - $stmt->bindValue($bindIndex, $value, $bindingType); - } else { - $stmt->bindValue($bindIndex, $value); - } - - ++$bindIndex; - } - } else { - // Named parameters - foreach ($params as $name => $value) { - if (isset($types[$name])) { - $type = $types[$name]; - [$value, $bindingType] = $this->getBindingInfo($value, $type); - $stmt->bindValue($name, $value, $bindingType); - } else { - $stmt->bindValue($name, $value); - } - } - } - } - - /** - * Gets the binding type of a given type. The given type can be a PDO or DBAL mapping type. - * - * @param mixed $value The value to bind. - * @param int|string|Type|null $type The type to bind (PDO or DBAL). - * - * @return mixed[] [0] => the (escaped) value, [1] => the binding type. - */ - private function getBindingInfo($value, $type) - { - if (is_string($type)) { - $type = Type::getType($type); - } - - if ($type instanceof Type) { - $value = $type->convertToDatabaseValue($value, $this->getDatabasePlatform()); - $bindingType = $type->getBindingType(); - } else { - $bindingType = $type; - } - - return [$value, $bindingType]; - } - - /** - * Resolves the parameters to a format which can be displayed. - * - * @internal This is a purely internal method. If you rely on this method, you are advised to - * copy/paste the code as this method may change, or be removed without prior notice. - * - * @param array|array $params Query parameters - * @param array|array $types Parameter types - * - * @return array|array - */ - public function resolveParams(array $params, array $types) - { - $resolvedParams = []; - - // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO. - if (is_int(key($params))) { - // Positional parameters - $typeOffset = array_key_exists(0, $types) ? -1 : 0; - $bindIndex = 1; - foreach ($params as $value) { - $typeIndex = $bindIndex + $typeOffset; - if (isset($types[$typeIndex])) { - $type = $types[$typeIndex]; - [$value] = $this->getBindingInfo($value, $type); - $resolvedParams[$bindIndex] = $value; - } else { - $resolvedParams[$bindIndex] = $value; - } - - ++$bindIndex; - } - } else { - // Named parameters - foreach ($params as $name => $value) { - if (isset($types[$name])) { - $type = $types[$name]; - [$value] = $this->getBindingInfo($value, $type); - $resolvedParams[$name] = $value; - } else { - $resolvedParams[$name] = $value; - } - } - } - - return $resolvedParams; - } - - /** - * Creates a new instance of a SQL query builder. - * - * @return QueryBuilder - */ - public function createQueryBuilder() - { - return new Query\QueryBuilder($this); - } - - /** - * Ping the server - * - * When the server is not available the method returns FALSE. - * It is responsibility of the developer to handle this case - * and abort the request or reconnect manually: - * - * @deprecated - * - * @return bool - * - * @example - * - * if ($conn->ping() === false) { - * $conn->close(); - * $conn->connect(); - * } - * - * It is undefined if the underlying driver attempts to reconnect - * or disconnect when the connection is not available anymore - * as long it returns TRUE when a reconnect succeeded and - * FALSE when the connection was dropped. - */ - public function ping() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4119', - 'Retry and reconnecting lost connections now happens automatically, ping() will be removed in DBAL 3.' - ); - - $connection = $this->getWrappedConnection(); - - if ($connection instanceof PingableConnection) { - return $connection->ping(); - } - - try { - $this->query($this->getDatabasePlatform()->getDummySelectSQL()); - - return true; - } catch (DBALException $e) { - return false; - } - } - - /** - * @internal - * - * @param array|array $params - * @param array|array $types - * - * @psalm-return never-return - * - * @throws Exception - */ - public function handleExceptionDuringQuery(Throwable $e, string $sql, array $params = [], array $types = []): void - { - $this->throw( - Exception::driverExceptionDuringQuery( - $this->_driver, - $e, - $sql, - $this->resolveParams($params, $types) - ) - ); - } - - /** - * @internal - * - * @psalm-return never-return - * - * @throws Exception - */ - public function handleDriverException(Throwable $e): void - { - $this->throw( - Exception::driverException( - $this->_driver, - $e - ) - ); - } - - /** - * @internal - * - * @psalm-return never-return - * - * @throws Exception - */ - private function throw(Exception $e): void - { - if ($e instanceof ConnectionLost) { - $this->close(); - } - - throw $e; - } - - private function ensureHasKeyValue(ResultStatement $stmt): void - { - $columnCount = $stmt->columnCount(); - - if ($columnCount < 2) { - throw NoKeyValue::fromColumnCount($columnCount); - } - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php deleted file mode 100644 index be1d0b61b..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php +++ /dev/null @@ -1,101 +0,0 @@ - $params - * @psalm-param Params $params - * @phpstan-param array $params - * - * @throws InvalidArgumentException - */ - public function __construct( - array $params, - Driver $driver, - ?Configuration $config = null, - ?EventManager $eventManager = null - ) { - $this->deprecated(self::class, PrimaryReadReplicaConnection::class); - - if (isset($params['master'])) { - $this->deprecated('Params key "master"', '"primary"'); - - $params['primary'] = $params['master']; - } - - if (isset($params['slaves'])) { - $this->deprecated('Params key "slaves"', '"replica"'); - - $params['replica'] = $params['slaves']; - } - - if (isset($params['keepSlave'])) { - $this->deprecated('Params key "keepSlave"', '"keepReplica"'); - - $params['keepReplica'] = $params['keepSlave']; - } - - parent::__construct($params, $driver, $config, $eventManager); - } - - /** - * Checks if the connection is currently towards the primary or not. - */ - public function isConnectedToMaster(): bool - { - $this->deprecated('isConnectedtoMaster()', 'isConnectedToPrimary()'); - - return $this->isConnectedToPrimary(); - } - - /** - * @param string|null $connectionName - * - * @return bool - */ - public function connect($connectionName = null) - { - if ($connectionName === 'master') { - $connectionName = 'primary'; - - $this->deprecated('connect("master")', 'ensureConnectedToPrimary()'); - } - - if ($connectionName === 'slave') { - $connectionName = 'replica'; - - $this->deprecated('connect("slave")', 'ensureConnectedToReplica()'); - } - - return $this->performConnect($connectionName); - } - - private function deprecated(string $thing, string $instead): void - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4054', - '%s is deprecated since doctrine/dbal 2.11 and will be removed in 3.0, use %s instead.', - $thing, - $instead - ); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php deleted file mode 100644 index b9a33cf1f..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php +++ /dev/null @@ -1,314 +0,0 @@ -getMessage(); - - return static::wrapException($driver, $driverEx, $msg); - } - - /** - * @deprecated - * - * @return Exception - */ - public static function driverException(Driver $driver, Throwable $driverEx) - { - return static::wrapException($driver, $driverEx, 'An exception occurred in driver: ' . $driverEx->getMessage()); - } - - /** - * @return Exception - */ - private static function wrapException(Driver $driver, Throwable $driverEx, string $msg) - { - if ($driverEx instanceof DriverException) { - return $driverEx; - } - - if ($driver instanceof ExceptionConverterDriver && $driverEx instanceof DeprecatedDriverException) { - return $driver->convertException($msg, $driverEx); - } - - return new Exception($msg, 0, $driverEx); - } - - /** - * Returns a human-readable representation of an array of parameters. - * This properly handles binary data by returning a hex representation. - * - * @param mixed[] $params - * - * @return string - */ - private static function formatParameters(array $params) - { - return '[' . implode(', ', array_map(static function ($param) { - if (is_resource($param)) { - return (string) $param; - } - - $json = @json_encode($param); - - if (! is_string($json) || $json === 'null' && is_string($param)) { - // JSON encoding failed, this is not a UTF-8 string. - return sprintf('"%s"', preg_replace('/.{2}/', '\\x$0', bin2hex($param))); - } - - return $json; - }, $params)) . ']'; - } - - /** - * @param string $wrapperClass - * - * @return Exception - */ - public static function invalidWrapperClass($wrapperClass) - { - return new Exception("The given 'wrapperClass' " . $wrapperClass . ' has to be a ' . - 'subtype of \Doctrine\DBAL\Connection.'); - } - - /** - * @param string $driverClass - * - * @return Exception - */ - public static function invalidDriverClass($driverClass) - { - return new Exception( - "The given 'driverClass' " . $driverClass . ' has to implement the ' . Driver::class . ' interface.' - ); - } - - /** - * @param string $tableName - * - * @return Exception - */ - public static function invalidTableName($tableName) - { - return new Exception('Invalid table name specified: ' . $tableName); - } - - /** - * @param string $tableName - * - * @return Exception - */ - public static function noColumnsSpecifiedForTable($tableName) - { - return new Exception('No columns specified for table ' . $tableName); - } - - /** - * @return Exception - */ - public static function limitOffsetInvalid() - { - return new Exception('Invalid Offset in Limit Query, it has to be larger than or equal to 0.'); - } - - /** - * @param string $name - * - * @return Exception - */ - public static function typeExists($name) - { - return new Exception('Type ' . $name . ' already exists.'); - } - - /** - * @param string $name - * - * @return Exception - */ - public static function unknownColumnType($name) - { - return new Exception('Unknown column type "' . $name . '" requested. Any Doctrine type that you use has ' . - 'to be registered with \Doctrine\DBAL\Types\Type::addType(). You can get a list of all the ' . - 'known types with \Doctrine\DBAL\Types\Type::getTypesMap(). If this error occurs during database ' . - 'introspection then you might have forgotten to register all database types for a Doctrine Type. Use ' . - 'AbstractPlatform#registerDoctrineTypeMapping() or have your custom types implement ' . - 'Type#getMappedDatabaseTypes(). If the type name is empty you might ' . - 'have a problem with the cache or forgot some mapping information.'); - } - - /** - * @param string $name - * - * @return Exception - */ - public static function typeNotFound($name) - { - return new Exception('Type to be overwritten ' . $name . ' does not exist.'); - } - - public static function typeNotRegistered(Type $type): self - { - return new Exception( - sprintf('Type of the class %s@%s is not registered.', get_class($type), spl_object_hash($type)) - ); - } - - public static function typeAlreadyRegistered(Type $type): self - { - return new Exception( - sprintf('Type of the class %s@%s is already registered.', get_class($type), spl_object_hash($type)) - ); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php deleted file mode 100644 index 6f8afbf3c..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php +++ /dev/null @@ -1,61 +0,0 @@ -getParams(); - - return $params['dbname']; - } - - /** - * {@inheritdoc} - */ - public function getDatabasePlatform() - { - return new DB2Platform(); - } - - /** - * {@inheritdoc} - */ - public function getSchemaManager(Connection $conn) - { - return new DB2SchemaManager($conn); - } - - /** - * @param string $message - * - * @return DriverException - */ - public function convertException($message, TheDriverException $exception) - { - return new DriverException($message, $exception); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDriverException.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDriverException.php deleted file mode 100644 index bf104545e..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDriverException.php +++ /dev/null @@ -1,12 +0,0 @@ -errorCode = $errorCode; - $this->sqlState = $sqlState; - } - - /** - * {@inheritdoc} - */ - public function getErrorCode() - { - /** @psalm-suppress ImpureMethodCall */ - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4112', - 'Driver\AbstractException::getErrorCode() is deprecated, use getSQLState() or getCode() instead.' - ); - - return $this->errorCode; - } - - /** - * {@inheritdoc} - */ - public function getSQLState() - { - return $this->sqlState; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php deleted file mode 100644 index 5869cd78f..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ /dev/null @@ -1,256 +0,0 @@ -getErrorCode()) { - case '1213': - return new DeadlockException($message, $exception); - - case '1205': - return new LockWaitTimeoutException($message, $exception); - - case '1050': - return new TableExistsException($message, $exception); - - case '1051': - case '1146': - return new TableNotFoundException($message, $exception); - - case '1216': - case '1217': - case '1451': - case '1452': - case '1701': - return new ForeignKeyConstraintViolationException($message, $exception); - - case '1062': - case '1557': - case '1569': - case '1586': - return new UniqueConstraintViolationException($message, $exception); - - case '1054': - case '1166': - case '1611': - return new InvalidFieldNameException($message, $exception); - - case '1052': - case '1060': - case '1110': - return new NonUniqueFieldNameException($message, $exception); - - case '1064': - case '1149': - case '1287': - case '1341': - case '1342': - case '1343': - case '1344': - case '1382': - case '1479': - case '1541': - case '1554': - case '1626': - return new SyntaxErrorException($message, $exception); - - case '1044': - case '1045': - case '1046': - case '1049': - case '1095': - case '1142': - case '1143': - case '1227': - case '1370': - case '1429': - case '2002': - case '2005': - return new ConnectionException($message, $exception); - - case '2006': - return new ConnectionLost($message, $exception); - - case '1048': - case '1121': - case '1138': - case '1171': - case '1252': - case '1263': - case '1364': - case '1566': - return new NotNullConstraintViolationException($message, $exception); - } - - return new DriverException($message, $exception); - } - - /** - * {@inheritdoc} - * - * @throws Exception - */ - public function createDatabasePlatformForVersion($version) - { - $mariadb = stripos($version, 'mariadb') !== false; - if ($mariadb && version_compare($this->getMariaDbMysqlVersionNumber($version), '10.2.7', '>=')) { - return new MariaDb1027Platform(); - } - - if (! $mariadb) { - $oracleMysqlVersion = $this->getOracleMysqlVersionNumber($version); - if (version_compare($oracleMysqlVersion, '8', '>=')) { - return new MySQL80Platform(); - } - - if (version_compare($oracleMysqlVersion, '5.7.9', '>=')) { - return new MySQL57Platform(); - } - } - - return $this->getDatabasePlatform(); - } - - /** - * Get a normalized 'version number' from the server string - * returned by Oracle MySQL servers. - * - * @param string $versionString Version string returned by the driver, i.e. '5.7.10' - * - * @throws Exception - */ - private function getOracleMysqlVersionNumber(string $versionString): string - { - if ( - ! preg_match( - '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', - $versionString, - $versionParts - ) - ) { - throw Exception::invalidPlatformVersionSpecified( - $versionString, - '..' - ); - } - - $majorVersion = $versionParts['major']; - $minorVersion = $versionParts['minor'] ?? 0; - $patchVersion = $versionParts['patch'] ?? null; - - if ($majorVersion === '5' && $minorVersion === '7' && $patchVersion === null) { - $patchVersion = '9'; - } - - return $majorVersion . '.' . $minorVersion . '.' . $patchVersion; - } - - /** - * Detect MariaDB server version, including hack for some mariadb distributions - * that starts with the prefix '5.5.5-' - * - * @param string $versionString Version string as returned by mariadb server, i.e. '5.5.5-Mariadb-10.0.8-xenial' - * - * @throws Exception - */ - private function getMariaDbMysqlVersionNumber(string $versionString): string - { - if ( - ! preg_match( - '/^(?:5\.5\.5-)?(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', - $versionString, - $versionParts - ) - ) { - throw Exception::invalidPlatformVersionSpecified( - $versionString, - '^(?:5\.5\.5-)?(mariadb-)?..' - ); - } - - return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch']; - } - - /** - * {@inheritdoc} - * - * @deprecated Use Connection::getDatabase() instead. - */ - public function getDatabase(Connection $conn) - { - $params = $conn->getParams(); - - if (isset($params['dbname'])) { - return $params['dbname']; - } - - $database = $conn->query('SELECT DATABASE()')->fetchColumn(); - - assert($database !== false); - - return $database; - } - - /** - * {@inheritdoc} - * - * @return MySqlPlatform - */ - public function getDatabasePlatform() - { - return new MySqlPlatform(); - } - - /** - * {@inheritdoc} - * - * @return MySqlSchemaManager - */ - public function getSchemaManager(Connection $conn) - { - return new MySqlSchemaManager($conn); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractOracleDriver.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractOracleDriver.php deleted file mode 100644 index be5826659..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractOracleDriver.php +++ /dev/null @@ -1,111 +0,0 @@ -getErrorCode()) { - case '1': - case '2299': - case '38911': - return new UniqueConstraintViolationException($message, $exception); - - case '904': - return new InvalidFieldNameException($message, $exception); - - case '918': - case '960': - return new NonUniqueFieldNameException($message, $exception); - - case '923': - return new SyntaxErrorException($message, $exception); - - case '942': - return new TableNotFoundException($message, $exception); - - case '955': - return new TableExistsException($message, $exception); - - case '1017': - case '12545': - return new ConnectionException($message, $exception); - - case '1400': - return new NotNullConstraintViolationException($message, $exception); - - case '2266': - case '2291': - case '2292': - return new ForeignKeyConstraintViolationException($message, $exception); - } - - return new DriverException($message, $exception); - } - - /** - * {@inheritdoc} - * - * @deprecated Use Connection::getDatabase() instead. - */ - public function getDatabase(Connection $conn) - { - $params = $conn->getParams(); - - return $params['user']; - } - - /** - * {@inheritdoc} - */ - public function getDatabasePlatform() - { - return new OraclePlatform(); - } - - /** - * {@inheritdoc} - */ - public function getSchemaManager(Connection $conn) - { - return new OracleSchemaManager($conn); - } - - /** - * Returns an appropriate Easy Connect String for the given parameters. - * - * @param mixed[] $params The connection parameters to return the Easy Connect String for. - * - * @return string - */ - protected function getEasyConnectString(array $params) - { - return (string) EasyConnectString::fromConnectionParameters($params); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php deleted file mode 100644 index b96102555..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php +++ /dev/null @@ -1,171 +0,0 @@ -getSQLState(); - - switch ($sqlState) { - case '40001': - case '40P01': - return new DeadlockException($message, $exception); - - case '0A000': - // Foreign key constraint violations during a TRUNCATE operation - // are considered "feature not supported" in PostgreSQL. - if (strpos($exception->getMessage(), 'truncate') !== false) { - return new ForeignKeyConstraintViolationException($message, $exception); - } - - break; - - case '23502': - return new NotNullConstraintViolationException($message, $exception); - - case '23503': - return new ForeignKeyConstraintViolationException($message, $exception); - - case '23505': - return new UniqueConstraintViolationException($message, $exception); - - case '42601': - return new SyntaxErrorException($message, $exception); - - case '42702': - return new NonUniqueFieldNameException($message, $exception); - - case '42703': - return new InvalidFieldNameException($message, $exception); - - case '42P01': - return new TableNotFoundException($message, $exception); - - case '42P07': - return new TableExistsException($message, $exception); - - case '08006': - return new Exception\ConnectionException($message, $exception); - - case '7': - // Prior to fixing https://bugs.php.net/bug.php?id=64705 (PHP 7.3.22 and PHP 7.4.10), - // in some cases (mainly connection errors) the PDO exception wouldn't provide a SQLSTATE via its code. - // The exception code would be always set to 7 here. - // We have to match against the SQLSTATE in the error message in these cases. - if (strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) { - return new ConnectionException($message, $exception); - } - - break; - } - - return new DriverException($message, $exception); - } - - /** - * {@inheritdoc} - */ - public function createDatabasePlatformForVersion($version) - { - if (! preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $version, $versionParts)) { - throw Exception::invalidPlatformVersionSpecified( - $version, - '..' - ); - } - - $majorVersion = $versionParts['major']; - $minorVersion = $versionParts['minor'] ?? 0; - $patchVersion = $versionParts['patch'] ?? 0; - $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion; - - switch (true) { - case version_compare($version, '10.0', '>='): - return new PostgreSQL100Platform(); - case version_compare($version, '9.4', '>='): - return new PostgreSQL94Platform(); - case version_compare($version, '9.2', '>='): - return new PostgreSQL92Platform(); - case version_compare($version, '9.1', '>='): - return new PostgreSQL91Platform(); - default: - return new PostgreSqlPlatform(); - } - } - - /** - * {@inheritdoc} - * - * @deprecated Use Connection::getDatabase() instead. - */ - public function getDatabase(Connection $conn) - { - $params = $conn->getParams(); - - if (isset($params['dbname'])) { - return $params['dbname']; - } - - $database = $conn->query('SELECT CURRENT_DATABASE()')->fetchColumn(); - - assert($database !== false); - - return $database; - } - - /** - * {@inheritdoc} - */ - public function getDatabasePlatform() - { - return new PostgreSqlPlatform(); - } - - /** - * {@inheritdoc} - */ - public function getSchemaManager(Connection $conn) - { - return new PostgreSqlSchemaManager($conn); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php deleted file mode 100644 index 59ab872aa..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php +++ /dev/null @@ -1,170 +0,0 @@ -getErrorCode()) { - case '-306': - case '-307': - case '-684': - return new DeadlockException($message, $exception); - - case '-210': - case '-1175': - case '-1281': - return new LockWaitTimeoutException($message, $exception); - - case '-100': - case '-103': - case '-832': - return new ConnectionException($message, $exception); - - case '-143': - return new InvalidFieldNameException($message, $exception); - - case '-193': - case '-196': - return new UniqueConstraintViolationException($message, $exception); - - case '-194': - case '-198': - return new ForeignKeyConstraintViolationException($message, $exception); - - case '-144': - return new NonUniqueFieldNameException($message, $exception); - - case '-184': - case '-195': - return new NotNullConstraintViolationException($message, $exception); - - case '-131': - return new SyntaxErrorException($message, $exception); - - case '-110': - return new TableExistsException($message, $exception); - - case '-141': - case '-1041': - return new TableNotFoundException($message, $exception); - } - - return new DriverException($message, $exception); - } - - /** - * {@inheritdoc} - */ - public function createDatabasePlatformForVersion($version) - { - if ( - ! preg_match( - '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?)?/', - $version, - $versionParts - ) - ) { - throw Exception::invalidPlatformVersionSpecified( - $version, - '...' - ); - } - - $majorVersion = $versionParts['major']; - $minorVersion = $versionParts['minor'] ?? 0; - $patchVersion = $versionParts['patch'] ?? 0; - $buildVersion = $versionParts['build'] ?? 0; - $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion . '.' . $buildVersion; - - switch (true) { - case version_compare($version, '16', '>='): - return new SQLAnywhere16Platform(); - - case version_compare($version, '12', '>='): - return new SQLAnywhere12Platform(); - - case version_compare($version, '11', '>='): - return new SQLAnywhere11Platform(); - - default: - return new SQLAnywherePlatform(); - } - } - - /** - * {@inheritdoc} - * - * @deprecated Use Connection::getDatabase() instead. - */ - public function getDatabase(Connection $conn) - { - $params = $conn->getParams(); - - if (isset($params['dbname'])) { - return $params['dbname']; - } - - $database = $conn->query('SELECT DB_NAME()')->fetchColumn(); - - assert($database !== false); - - return $database; - } - - /** - * {@inheritdoc} - */ - public function getDatabasePlatform() - { - return new SQLAnywhere12Platform(); - } - - /** - * {@inheritdoc} - */ - public function getSchemaManager(Connection $conn) - { - return new SQLAnywhereSchemaManager($conn); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php deleted file mode 100644 index 2550ec5a8..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php +++ /dev/null @@ -1,107 +0,0 @@ -\d+)(?:\.(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?)?/', - $version, - $versionParts - ) - ) { - throw Exception::invalidPlatformVersionSpecified( - $version, - '...' - ); - } - - $majorVersion = $versionParts['major']; - $minorVersion = $versionParts['minor'] ?? 0; - $patchVersion = $versionParts['patch'] ?? 0; - $buildVersion = $versionParts['build'] ?? 0; - $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion . '.' . $buildVersion; - - switch (true) { - case version_compare($version, '11.00.2100', '>='): - return new SQLServer2012Platform(); - case version_compare($version, '10.00.1600', '>='): - return new SQLServer2008Platform(); - case version_compare($version, '9.00.1399', '>='): - return new SQLServer2005Platform(); - default: - return new SQLServerPlatform(); - } - } - - /** - * {@inheritdoc} - * - * @deprecated Use Connection::getDatabase() instead. - */ - public function getDatabase(Connection $conn) - { - $params = $conn->getParams(); - - if (isset($params['dbname'])) { - return $params['dbname']; - } - - $database = $conn->query('SELECT DB_NAME()')->fetchColumn(); - - assert($database !== false); - - return $database; - } - - /** - * {@inheritdoc} - */ - public function getDatabasePlatform() - { - return new SQLServer2008Platform(); - } - - /** - * {@inheritdoc} - */ - public function getSchemaManager(Connection $conn) - { - return new SQLServerSchemaManager($conn); - } - - /** - * @param string $message - * - * @return DriverException - */ - public function convertException($message, TheDriverException $exception) - { - return new DriverException($message, $exception); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver/Exception/PortWithoutHost.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver/Exception/PortWithoutHost.php deleted file mode 100644 index c6dbf34bb..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver/Exception/PortWithoutHost.php +++ /dev/null @@ -1,20 +0,0 @@ -getMessage(), 'database is locked') !== false) { - return new LockWaitTimeoutException($message, $exception); - } - - if ( - strpos($exception->getMessage(), 'must be unique') !== false || - strpos($exception->getMessage(), 'is not unique') !== false || - strpos($exception->getMessage(), 'are not unique') !== false || - strpos($exception->getMessage(), 'UNIQUE constraint failed') !== false - ) { - return new UniqueConstraintViolationException($message, $exception); - } - - if ( - strpos($exception->getMessage(), 'may not be NULL') !== false || - strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false - ) { - return new NotNullConstraintViolationException($message, $exception); - } - - if (strpos($exception->getMessage(), 'no such table:') !== false) { - return new TableNotFoundException($message, $exception); - } - - if (strpos($exception->getMessage(), 'already exists') !== false) { - return new TableExistsException($message, $exception); - } - - if (strpos($exception->getMessage(), 'has no column named') !== false) { - return new InvalidFieldNameException($message, $exception); - } - - if (strpos($exception->getMessage(), 'ambiguous column name') !== false) { - return new NonUniqueFieldNameException($message, $exception); - } - - if (strpos($exception->getMessage(), 'syntax error') !== false) { - return new SyntaxErrorException($message, $exception); - } - - if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) { - return new ReadOnlyException($message, $exception); - } - - if (strpos($exception->getMessage(), 'unable to open database file') !== false) { - return new ConnectionException($message, $exception); - } - - return new DriverException($message, $exception); - } - - /** - * {@inheritdoc} - * - * @deprecated Use Connection::getDatabase() instead. - */ - public function getDatabase(Connection $conn) - { - $params = $conn->getParams(); - - return $params['path'] ?? null; - } - - /** - * {@inheritdoc} - */ - public function getDatabasePlatform() - { - return new SqlitePlatform(); - } - - /** - * {@inheritdoc} - */ - public function getSchemaManager(Connection $conn) - { - return new SqliteSchemaManager($conn); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php deleted file mode 100644 index 8bd9f379f..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php +++ /dev/null @@ -1,97 +0,0 @@ -constructPdoDsn($params), - $username, - $password, - $driverOptions - ); - } - - /** - * {@inheritdoc} - */ - public function createDatabasePlatformForVersion($version) - { - return $this->getDatabasePlatform(); - } - - /** - * {@inheritdoc} - * - * @return DrizzlePlatform - */ - public function getDatabasePlatform() - { - return new DrizzlePlatform(); - } - - /** - * {@inheritdoc} - * - * @return DrizzleSchemaManager - */ - public function getSchemaManager(\Doctrine\DBAL\Connection $conn) - { - return new DrizzleSchemaManager($conn); - } - - /** - * {@inheritdoc} - * - * @deprecated - */ - public function getName() - { - return 'drizzle_pdo_mysql'; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Exception.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Exception.php deleted file mode 100644 index a16db4f8a..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Exception.php +++ /dev/null @@ -1,34 +0,0 @@ -conn = $conn; - } - - /** - * {@inheritdoc} - */ - public function getServerVersion() - { - $serverInfo = db2_server_info($this->conn); - assert($serverInfo instanceof stdClass); - - return $serverInfo->DBMS_VER; - } - - /** - * {@inheritdoc} - */ - public function requiresQueryForServerVersion() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4114', - 'ServerInfoAwareConnection::requiresQueryForServerVersion() is deprecated and removed in DBAL 3.' - ); - - return false; - } - - /** - * {@inheritdoc} - */ - public function prepare($sql) - { - $stmt = @db2_prepare($this->conn, $sql); - - if ($stmt === false) { - throw PrepareFailed::new(error_get_last()); - } - - return new Statement($stmt); - } - - /** - * {@inheritdoc} - */ - public function query() - { - $args = func_get_args(); - $sql = $args[0]; - $stmt = $this->prepare($sql); - $stmt->execute(); - - return $stmt; - } - - /** - * {@inheritdoc} - */ - public function quote($value, $type = ParameterType::STRING) - { - $value = db2_escape_string($value); - - if ($type === ParameterType::INTEGER) { - return $value; - } - - return "'" . $value . "'"; - } - - /** - * {@inheritdoc} - */ - public function exec($sql) - { - $stmt = @db2_exec($this->conn, $sql); - - if ($stmt === false) { - throw ConnectionError::new($this->conn); - } - - return db2_num_rows($stmt); - } - - /** - * {@inheritdoc} - */ - public function lastInsertId($name = null) - { - return db2_last_insert_id($this->conn); - } - - /** - * {@inheritdoc} - */ - public function beginTransaction() - { - $result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_OFF); - assert(is_bool($result)); - - return $result; - } - - /** - * {@inheritdoc} - */ - public function commit() - { - if (! db2_commit($this->conn)) { - throw ConnectionError::new($this->conn); - } - - $result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON); - assert(is_bool($result)); - - return $result; - } - - /** - * {@inheritdoc} - */ - public function rollBack() - { - if (! db2_rollback($this->conn)) { - throw ConnectionError::new($this->conn); - } - - $result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON); - assert(is_bool($result)); - - return $result; - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorCode() - { - return db2_conn_error($this->conn); - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorInfo() - { - return [ - 0 => db2_conn_errormsg($this->conn), - 1 => $this->errorCode(), - ]; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php deleted file mode 100644 index f4e50c9d4..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php +++ /dev/null @@ -1,47 +0,0 @@ -toString(); - - return new Connection( - $params, - (string) $username, - (string) $password, - $driverOptions - ); - } - - /** - * {@inheritdoc} - * - * @deprecated - */ - public function getName() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'Driver::getName() is deprecated' - ); - - return 'ibm_db2'; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php deleted file mode 100644 index 5297d70d4..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php +++ /dev/null @@ -1,14 +0,0 @@ -stmt = $stmt; - } - - /** - * {@inheritdoc} - */ - public function bindValue($param, $value, $type = ParameterType::STRING) - { - assert(is_int($param)); - - return $this->bindParam($param, $value, $type); - } - - /** - * {@inheritdoc} - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) - { - assert(is_int($param)); - - switch ($type) { - case ParameterType::INTEGER: - $this->bind($param, $variable, DB2_PARAM_IN, DB2_LONG); - break; - - case ParameterType::LARGE_OBJECT: - if (isset($this->lobs[$param])) { - [, $handle] = $this->lobs[$param]; - fclose($handle); - } - - $handle = $this->createTemporaryFile(); - $path = stream_get_meta_data($handle)['uri']; - - $this->bind($param, $path, DB2_PARAM_FILE, DB2_BINARY); - - $this->lobs[$param] = [&$variable, $handle]; - break; - - default: - $this->bind($param, $variable, DB2_PARAM_IN, DB2_CHAR); - break; - } - - return true; - } - - /** - * @param int $position Parameter position - * @param mixed $variable - * - * @throws DB2Exception - */ - private function bind($position, &$variable, int $parameterType, int $dataType): void - { - $this->bindParam[$position] =& $variable; - - if (! db2_bind_param($this->stmt, $position, 'variable', $parameterType, $dataType)) { - throw StatementError::new($this->stmt); - } - } - - /** - * {@inheritdoc} - * - * @deprecated Use free() instead. - */ - public function closeCursor() - { - $this->bindParam = []; - - if (! db2_free_result($this->stmt)) { - return false; - } - - $this->result = false; - - return true; - } - - /** - * {@inheritdoc} - */ - public function columnCount() - { - return db2_num_fields($this->stmt) ?: 0; - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorCode() - { - return db2_stmt_error(); - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorInfo() - { - return [ - db2_stmt_errormsg(), - db2_stmt_error(), - ]; - } - - /** - * {@inheritdoc} - */ - public function execute($params = null) - { - if ($params === null) { - ksort($this->bindParam); - - $params = []; - - foreach ($this->bindParam as $column => $value) { - $params[] = $value; - } - } - - foreach ($this->lobs as [$source, $target]) { - if (is_resource($source)) { - $this->copyStreamToStream($source, $target); - - continue; - } - - $this->writeStringToStream($source, $target); - } - - $retval = db2_execute($this->stmt, $params); - - foreach ($this->lobs as [, $handle]) { - fclose($handle); - } - - $this->lobs = []; - - if ($retval === false) { - throw StatementError::new($this->stmt); - } - - $this->result = true; - - return $retval; - } - - /** - * {@inheritdoc} - * - * @deprecated Use one of the fetch- or iterate-related methods. - */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) - { - $this->defaultFetchMode = $fetchMode; - $this->defaultFetchClass = $arg2 ?: $this->defaultFetchClass; - $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; - - return true; - } - - /** - * {@inheritdoc} - * - * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. - */ - #[ReturnTypeWillChange] - public function getIterator() - { - return new StatementIterator($this); - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. - */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) - { - // do not try fetching from the statement if it's not expected to contain result - // in order to prevent exceptional situation - if (! $this->result) { - return false; - } - - $fetchMode = $fetchMode ?: $this->defaultFetchMode; - switch ($fetchMode) { - case FetchMode::COLUMN: - return $this->fetchColumn(); - - case FetchMode::MIXED: - return db2_fetch_both($this->stmt); - - case FetchMode::ASSOCIATIVE: - return db2_fetch_assoc($this->stmt); - - case FetchMode::CUSTOM_OBJECT: - $className = $this->defaultFetchClass; - $ctorArgs = $this->defaultFetchClassCtorArgs; - - if (func_num_args() >= 2) { - $args = func_get_args(); - $className = $args[1]; - $ctorArgs = $args[2] ?? []; - } - - $result = db2_fetch_object($this->stmt); - - if ($result instanceof stdClass) { - $result = $this->castObject($result, $className, $ctorArgs); - } - - return $result; - - case FetchMode::NUMERIC: - return db2_fetch_array($this->stmt); - - case FetchMode::STANDARD_OBJECT: - return db2_fetch_object($this->stmt); - - default: - throw new DB2Exception('Given Fetch-Style ' . $fetchMode . ' is not supported.'); - } - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. - */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) - { - $rows = []; - - switch ($fetchMode) { - case FetchMode::CUSTOM_OBJECT: - while (($row = $this->fetch(...func_get_args())) !== false) { - $rows[] = $row; - } - - break; - - case FetchMode::COLUMN: - while (($row = $this->fetchColumn()) !== false) { - $rows[] = $row; - } - - break; - - default: - while (($row = $this->fetch($fetchMode)) !== false) { - $rows[] = $row; - } - } - - return $rows; - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchOne() instead. - */ - public function fetchColumn($columnIndex = 0) - { - $row = $this->fetch(FetchMode::NUMERIC); - - if ($row === false) { - return false; - } - - return $row[$columnIndex] ?? null; - } - - /** - * {@inheritDoc} - */ - public function fetchNumeric() - { - if (! $this->result) { - return false; - } - - return db2_fetch_array($this->stmt); - } - - /** - * {@inheritdoc} - */ - public function fetchAssociative() - { - // do not try fetching from the statement if it's not expected to contain the result - // in order to prevent exceptional situation - if (! $this->result) { - return false; - } - - return db2_fetch_assoc($this->stmt); - } - - /** - * {@inheritdoc} - */ - public function fetchOne() - { - return FetchUtils::fetchOne($this); - } - - /** - * {@inheritdoc} - */ - public function fetchAllNumeric(): array - { - return FetchUtils::fetchAllNumeric($this); - } - - /** - * {@inheritdoc} - */ - public function fetchAllAssociative(): array - { - return FetchUtils::fetchAllAssociative($this); - } - - /** - * {@inheritdoc} - */ - public function fetchFirstColumn(): array - { - return FetchUtils::fetchFirstColumn($this); - } - - /** - * {@inheritdoc} - */ - public function rowCount() - { - return @db2_num_rows($this->stmt) ? : 0; - } - - public function free(): void - { - $this->bindParam = []; - - db2_free_result($this->stmt); - - $this->result = false; - } - - /** - * Casts a stdClass object to the given class name mapping its' properties. - * - * @param stdClass $sourceObject Object to cast from. - * @param class-string|object $destinationClass Name of the class or class instance to cast to. - * @param mixed[] $ctorArgs Arguments to use for constructing the destination class instance. - * - * @return object - * - * @throws DB2Exception - */ - private function castObject(stdClass $sourceObject, $destinationClass, array $ctorArgs = []) - { - if (! is_string($destinationClass)) { - if (! is_object($destinationClass)) { - throw new DB2Exception(sprintf( - 'Destination class has to be of type string or object, %s given.', - gettype($destinationClass) - )); - } - } else { - $destinationClass = new ReflectionClass($destinationClass); - $destinationClass = $destinationClass->newInstanceArgs($ctorArgs); - } - - $sourceReflection = new ReflectionObject($sourceObject); - $destinationClassReflection = new ReflectionObject($destinationClass); - /** @var ReflectionProperty[] $destinationProperties */ - $destinationProperties = array_change_key_case($destinationClassReflection->getProperties(), CASE_LOWER); - - foreach ($sourceReflection->getProperties() as $sourceProperty) { - $sourceProperty->setAccessible(true); - - $name = $sourceProperty->getName(); - $value = $sourceProperty->getValue($sourceObject); - - // Try to find a case-matching property. - if ($destinationClassReflection->hasProperty($name)) { - $destinationProperty = $destinationClassReflection->getProperty($name); - - $destinationProperty->setAccessible(true); - $destinationProperty->setValue($destinationClass, $value); - - continue; - } - - $name = strtolower($name); - - // Try to find a property without matching case. - // Fallback for the driver returning either all uppercase or all lowercase column names. - if (isset($destinationProperties[$name])) { - $destinationProperty = $destinationProperties[$name]; - - $destinationProperty->setAccessible(true); - $destinationProperty->setValue($destinationClass, $value); - - continue; - } - - $destinationClass->$name = $value; - } - - return $destinationClass; - } - - /** - * @return resource - * - * @throws DB2Exception - */ - private function createTemporaryFile() - { - $handle = @tmpfile(); - - if ($handle === false) { - throw CannotCreateTemporaryFile::new(error_get_last()); - } - - return $handle; - } - - /** - * @param resource $source - * @param resource $target - * - * @throws DB2Exception - */ - private function copyStreamToStream($source, $target): void - { - if (@stream_copy_to_stream($source, $target) === false) { - throw CannotCopyStreamToStream::new(error_get_last()); - } - } - - /** - * @param resource $target - * - * @throws DB2Exception - */ - private function writeStringToStream(string $string, $target): void - { - if (@fwrite($target, $string) === false) { - throw CannotWriteToTemporaryFile::new(error_get_last()); - } - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Driver.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Driver.php deleted file mode 100644 index dcc84b32d..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Driver.php +++ /dev/null @@ -1,9 +0,0 @@ -error, $connection->sqlstate, $connection->errno); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Exception/ConnectionFailed.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Exception/ConnectionFailed.php deleted file mode 100644 index f73e07c32..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Exception/ConnectionFailed.php +++ /dev/null @@ -1,21 +0,0 @@ -connect_error, 'HY000', $connection->connect_errno); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Exception/FailedReadingStreamOffset.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Exception/FailedReadingStreamOffset.php deleted file mode 100644 index d3aaf8fbe..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Exception/FailedReadingStreamOffset.php +++ /dev/null @@ -1,22 +0,0 @@ -conn = $conn; - - $this->setSecureConnection($params); - $this->setDriverOptions($driverOptions); - - if (! @$this->conn->real_connect($params['host'], $username, $password, $dbname, $port, $socket, $flags)) { - throw ConnectionFailed::new($this->conn); - } - - if (! isset($params['charset'])) { - return; - } - - $this->conn->set_charset($params['charset']); - } - - /** - * Retrieves mysqli native resource handle. - * - * Could be used if part of your application is not using DBAL. - * - * @return mysqli - */ - public function getWrappedResourceHandle() - { - return $this->conn; - } - - /** - * {@inheritdoc} - * - * The server version detection includes a special case for MariaDB - * to support '5.5.5-' prefixed versions introduced in Maria 10+ - * - * @link https://jira.mariadb.org/browse/MDEV-4088 - */ - public function getServerVersion() - { - $serverInfos = $this->conn->get_server_info(); - if (stripos($serverInfos, 'mariadb') !== false) { - return $serverInfos; - } - - $majorVersion = floor($this->conn->server_version / 10000); - $minorVersion = floor(($this->conn->server_version - $majorVersion * 10000) / 100); - $patchVersion = floor($this->conn->server_version - $majorVersion * 10000 - $minorVersion * 100); - - return $majorVersion . '.' . $minorVersion . '.' . $patchVersion; - } - - /** - * {@inheritdoc} - */ - public function requiresQueryForServerVersion() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4114', - 'ServerInfoAwareConnection::requiresQueryForServerVersion() is deprecated and removed in DBAL 3.' - ); - - return false; - } - - /** - * {@inheritdoc} - */ - public function prepare($sql) - { - return new Statement($this->conn, $sql); - } - - /** - * {@inheritdoc} - */ - public function query() - { - $args = func_get_args(); - $sql = $args[0]; - $stmt = $this->prepare($sql); - $stmt->execute(); - - return $stmt; - } - - /** - * {@inheritdoc} - */ - public function quote($value, $type = ParameterType::STRING) - { - return "'" . $this->conn->escape_string($value) . "'"; - } - - /** - * {@inheritdoc} - */ - public function exec($sql) - { - if ($this->conn->query($sql) === false) { - throw ConnectionError::new($this->conn); - } - - return $this->conn->affected_rows; - } - - /** - * {@inheritdoc} - */ - public function lastInsertId($name = null) - { - return $this->conn->insert_id; - } - - /** - * {@inheritdoc} - */ - public function beginTransaction() - { - $this->conn->query('START TRANSACTION'); - - return true; - } - - /** - * {@inheritdoc} - */ - public function commit() - { - return $this->conn->commit(); - } - - /** - * {@inheritdoc}non-PHPdoc) - */ - public function rollBack() - { - return $this->conn->rollback(); - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - * - * @return int - */ - public function errorCode() - { - return $this->conn->errno; - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - * - * @return string - */ - public function errorInfo() - { - return $this->conn->error; - } - - /** - * Apply the driver options to the connection. - * - * @param mixed[] $driverOptions - * - * @throws MysqliException When one of of the options is not supported. - * @throws MysqliException When applying doesn't work - e.g. due to incorrect value. - */ - private function setDriverOptions(array $driverOptions = []): void - { - $supportedDriverOptions = [ - MYSQLI_OPT_CONNECT_TIMEOUT, - MYSQLI_OPT_LOCAL_INFILE, - MYSQLI_OPT_READ_TIMEOUT, - MYSQLI_INIT_COMMAND, - MYSQLI_READ_DEFAULT_FILE, - MYSQLI_READ_DEFAULT_GROUP, - MYSQLI_SERVER_PUBLIC_KEY, - ]; - - $exceptionMsg = "%s option '%s' with value '%s'"; - - foreach ($driverOptions as $option => $value) { - if ($option === static::OPTION_FLAGS) { - continue; - } - - if (! in_array($option, $supportedDriverOptions, true)) { - throw InvalidOption::fromOption($option, $value); - } - - if (@mysqli_options($this->conn, $option, $value)) { - continue; - } - - $msg = sprintf($exceptionMsg, 'Failed to set', $option, $value); - $msg .= sprintf(', error: %s (%d)', mysqli_error($this->conn), mysqli_errno($this->conn)); - - throw new MysqliException( - $msg, - $this->conn->sqlstate, - $this->conn->errno - ); - } - } - - /** - * Pings the server and re-connects when `mysqli.reconnect = 1` - * - * @deprecated - * - * @return bool - */ - public function ping() - { - return $this->conn->ping(); - } - - /** - * Establish a secure connection - * - * @param array $params - * - * @throws MysqliException - */ - private function setSecureConnection(array $params): void - { - if ( - ! isset($params['ssl_key']) && - ! isset($params['ssl_cert']) && - ! isset($params['ssl_ca']) && - ! isset($params['ssl_capath']) && - ! isset($params['ssl_cipher']) - ) { - return; - } - - $this->conn->ssl_set( - $params['ssl_key'] ?? '', - $params['ssl_cert'] ?? '', - $params['ssl_ca'] ?? '', - $params['ssl_capath'] ?? '', - $params['ssl_cipher'] ?? '' - ); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php deleted file mode 100644 index f583ed32f..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php +++ /dev/null @@ -1,14 +0,0 @@ - 's', - ParameterType::STRING => 's', - ParameterType::BINARY => 's', - ParameterType::BOOLEAN => 'i', - ParameterType::NULL => 's', - ParameterType::INTEGER => 'i', - ParameterType::LARGE_OBJECT => 'b', - ]; - - /** @var mysqli */ - protected $_conn; - - /** @var mysqli_stmt */ - protected $_stmt; - - /** @var string[]|false|null */ - protected $_columnNames; - - /** @var mixed[] */ - protected $_rowBindedValues = []; - - /** @var mixed[]|null */ - protected $_bindedValues; - - /** @var string */ - protected $types; - - /** - * Contains ref values for bindValue(). - * - * @var mixed[] - */ - protected $_values = []; - - /** @var int */ - protected $_defaultFetchMode = FetchMode::MIXED; - - /** - * Indicates whether the statement is in the state when fetching results is possible - * - * @var bool - */ - private $result = false; - - /** - * @internal The statement can be only instantiated by its driver connection. - * - * @param string $prepareString - * - * @throws MysqliException - */ - public function __construct(mysqli $conn, $prepareString) - { - $this->_conn = $conn; - - $stmt = $conn->prepare($prepareString); - - if ($stmt === false) { - throw ConnectionError::new($this->_conn); - } - - $this->_stmt = $stmt; - - $paramCount = $this->_stmt->param_count; - if (0 >= $paramCount) { - return; - } - - $this->types = str_repeat('s', $paramCount); - $this->_bindedValues = array_fill(1, $paramCount, null); - } - - /** - * {@inheritdoc} - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) - { - assert(is_int($param)); - - if (! isset(self::$_paramTypeMap[$type])) { - throw UnknownType::new($type); - } - - $this->_bindedValues[$param] =& $variable; - $this->types[$param - 1] = self::$_paramTypeMap[$type]; - - return true; - } - - /** - * {@inheritdoc} - */ - public function bindValue($param, $value, $type = ParameterType::STRING) - { - assert(is_int($param)); - - if (! isset(self::$_paramTypeMap[$type])) { - throw UnknownType::new($type); - } - - $this->_values[$param] = $value; - $this->_bindedValues[$param] =& $this->_values[$param]; - $this->types[$param - 1] = self::$_paramTypeMap[$type]; - - return true; - } - - /** - * {@inheritdoc} - */ - public function execute($params = null) - { - if ($this->_bindedValues !== null) { - if ($params !== null) { - if (! $this->bindUntypedValues($params)) { - throw StatementError::new($this->_stmt); - } - } else { - $this->bindTypedParameters(); - } - } - - if (! $this->_stmt->execute()) { - throw StatementError::new($this->_stmt); - } - - if ($this->_columnNames === null) { - $meta = $this->_stmt->result_metadata(); - if ($meta !== false) { - $fields = $meta->fetch_fields(); - assert(is_array($fields)); - - $columnNames = []; - foreach ($fields as $col) { - $columnNames[] = $col->name; - } - - $meta->free(); - - $this->_columnNames = $columnNames; - } else { - $this->_columnNames = false; - } - } - - if ($this->_columnNames !== false) { - // Store result of every execution which has it. Otherwise it will be impossible - // to execute a new statement in case if the previous one has non-fetched rows - // @link http://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html - $this->_stmt->store_result(); - - // Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql, - // it will have to allocate as much memory as it may be needed for the given column type - // (e.g. for a LONGBLOB column it's 4 gigabytes) - // @link https://bugs.php.net/bug.php?id=51386#1270673122 - // - // Make sure that the values are bound after each execution. Otherwise, if closeCursor() has been - // previously called on the statement, the values are unbound making the statement unusable. - // - // It's also important that row values are bound after _each_ call to store_result(). Otherwise, - // if mysqli is compiled with libmysql, subsequently fetched string values will get truncated - // to the length of the ones fetched during the previous execution. - $this->_rowBindedValues = array_fill(0, count($this->_columnNames), null); - - $refs = []; - foreach ($this->_rowBindedValues as $key => &$value) { - $refs[$key] =& $value; - } - - if (! $this->_stmt->bind_result(...$refs)) { - throw StatementError::new($this->_stmt); - } - } - - $this->result = true; - - return true; - } - - /** - * Binds parameters with known types previously bound to the statement - */ - private function bindTypedParameters(): void - { - $streams = $values = []; - $types = $this->types; - - foreach ($this->_bindedValues as $parameter => $value) { - assert(is_int($parameter)); - - if (! isset($types[$parameter - 1])) { - $types[$parameter - 1] = static::$_paramTypeMap[ParameterType::STRING]; - } - - if ($types[$parameter - 1] === static::$_paramTypeMap[ParameterType::LARGE_OBJECT]) { - if (is_resource($value)) { - if (get_resource_type($value) !== 'stream') { - throw new InvalidArgumentException( - 'Resources passed with the LARGE_OBJECT parameter type must be stream resources.' - ); - } - - $streams[$parameter] = $value; - $values[$parameter] = null; - continue; - } - - $types[$parameter - 1] = static::$_paramTypeMap[ParameterType::STRING]; - } - - $values[$parameter] = $value; - } - - if (! $this->_stmt->bind_param($types, ...$values)) { - throw StatementError::new($this->_stmt); - } - - $this->sendLongData($streams); - } - - /** - * Handle $this->_longData after regular query parameters have been bound - * - * @param array $streams - * - * @throws MysqliException - */ - private function sendLongData(array $streams): void - { - foreach ($streams as $paramNr => $stream) { - while (! feof($stream)) { - $chunk = fread($stream, 8192); - - if ($chunk === false) { - throw FailedReadingStreamOffset::new($paramNr); - } - - if (! $this->_stmt->send_long_data($paramNr - 1, $chunk)) { - throw StatementError::new($this->_stmt); - } - } - } - } - - /** - * Binds a array of values to bound parameters. - * - * @param mixed[] $values - * - * @return bool - */ - private function bindUntypedValues(array $values) - { - $params = []; - $types = str_repeat('s', count($values)); - - foreach ($values as &$v) { - $params[] =& $v; - } - - return $this->_stmt->bind_param($types, ...$params); - } - - /** - * @return mixed[]|false|null - */ - private function _fetch() - { - $ret = $this->_stmt->fetch(); - - if ($ret === true) { - $values = []; - foreach ($this->_rowBindedValues as $v) { - $values[] = $v; - } - - return $values; - } - - return $ret; - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. - */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) - { - // do not try fetching from the statement if it's not expected to contain result - // in order to prevent exceptional situation - if (! $this->result) { - return false; - } - - $fetchMode = $fetchMode ?: $this->_defaultFetchMode; - - if ($fetchMode === FetchMode::COLUMN) { - return $this->fetchColumn(); - } - - $values = $this->_fetch(); - - if ($values === null) { - return false; - } - - if ($values === false) { - throw StatementError::new($this->_stmt); - } - - if ($fetchMode === FetchMode::NUMERIC) { - return $values; - } - - assert(is_array($this->_columnNames)); - $assoc = array_combine($this->_columnNames, $values); - assert(is_array($assoc)); - - switch ($fetchMode) { - case FetchMode::ASSOCIATIVE: - return $assoc; - - case FetchMode::MIXED: - return $assoc + $values; - - case FetchMode::STANDARD_OBJECT: - return (object) $assoc; - - default: - throw new MysqliException(sprintf("Unknown fetch type '%s'", $fetchMode)); - } - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. - */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) - { - $fetchMode = $fetchMode ?: $this->_defaultFetchMode; - - $rows = []; - - if ($fetchMode === FetchMode::COLUMN) { - while (($row = $this->fetchColumn()) !== false) { - $rows[] = $row; - } - } else { - while (($row = $this->fetch($fetchMode)) !== false) { - $rows[] = $row; - } - } - - return $rows; - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchOne() instead. - */ - public function fetchColumn($columnIndex = 0) - { - $row = $this->fetch(FetchMode::NUMERIC); - - if ($row === false) { - return false; - } - - return $row[$columnIndex] ?? null; - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function fetchNumeric() - { - // do not try fetching from the statement if it's not expected to contain the result - // in order to prevent exceptional situation - if (! $this->result) { - return false; - } - - $values = $this->_fetch(); - - if ($values === null) { - return false; - } - - if ($values === false) { - throw StatementError::new($this->_stmt); - } - - return $values; - } - - /** - * {@inheritDoc} - */ - public function fetchAssociative() - { - $values = $this->fetchNumeric(); - - if ($values === false) { - return false; - } - - assert(is_array($this->_columnNames)); - $row = array_combine($this->_columnNames, $values); - assert(is_array($row)); - - return $row; - } - - /** - * {@inheritdoc} - */ - public function fetchOne() - { - return FetchUtils::fetchOne($this); - } - - /** - * {@inheritdoc} - */ - public function fetchAllNumeric(): array - { - return FetchUtils::fetchAllNumeric($this); - } - - /** - * {@inheritdoc} - */ - public function fetchAllAssociative(): array - { - return FetchUtils::fetchAllAssociative($this); - } - - /** - * {@inheritdoc} - */ - public function fetchFirstColumn(): array - { - return FetchUtils::fetchFirstColumn($this); - } - - /** - * {@inheritdoc} - */ - public function errorCode() - { - return $this->_stmt->errno; - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - * - * @return string - */ - public function errorInfo() - { - return $this->_stmt->error; - } - - /** - * {@inheritdoc} - * - * @deprecated Use free() instead. - */ - public function closeCursor() - { - $this->free(); - - return true; - } - - /** - * {@inheritdoc} - */ - public function rowCount() - { - if ($this->_columnNames === false) { - return $this->_stmt->affected_rows; - } - - return $this->_stmt->num_rows; - } - - /** - * {@inheritdoc} - */ - public function columnCount() - { - return $this->_stmt->field_count; - } - - public function free(): void - { - $this->_stmt->free_result(); - $this->result = false; - } - - /** - * {@inheritdoc} - * - * @deprecated Use one of the fetch- or iterate-related methods. - */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) - { - $this->_defaultFetchMode = $fetchMode; - - return true; - } - - /** - * {@inheritdoc} - * - * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. - */ - #[ReturnTypeWillChange] - public function getIterator() - { - return new StatementIterator($this); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Statement.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Statement.php deleted file mode 100644 index bfd6ae900..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Statement.php +++ /dev/null @@ -1,7 +0,0 @@ -_constructDsn($params), - $params['charset'] ?? '', - $params['sessionMode'] ?? OCI_NO_AUTO_COMMIT, - $params['persistent'] ?? false - ); - } catch (OCI8Exception $e) { - throw Exception::driverException($this, $e); - } - } - - /** - * Constructs the Oracle DSN. - * - * @param mixed[] $params - * - * @return string The DSN. - */ - protected function _constructDsn(array $params) - { - return $this->getEasyConnectString($params); - } - - /** - * {@inheritdoc} - * - * @deprecated - */ - public function getName() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'Driver::getName() is deprecated' - ); - - return 'oci8'; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Exception/SequenceDoesNotExist.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Exception/SequenceDoesNotExist.php deleted file mode 100644 index 274afa90e..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Exception/SequenceDoesNotExist.php +++ /dev/null @@ -1,20 +0,0 @@ -dbh = $dbh; - } - - /** - * {@inheritdoc} - * - * @throws UnexpectedValueException If the version string returned by the database server - * does not contain a parsable version number. - */ - public function getServerVersion() - { - $version = oci_server_version($this->dbh); - - if ($version === false) { - throw OCI8Exception::fromErrorInfo(oci_error($this->dbh)); - } - - if (! preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', $version, $matches)) { - throw new UnexpectedValueException( - sprintf( - 'Unexpected database version string "%s". Cannot parse an appropriate version number from it. ' . - 'Please report this database version string to the Doctrine team.', - $version - ) - ); - } - - return $matches[1]; - } - - /** - * {@inheritdoc} - */ - public function requiresQueryForServerVersion() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4114', - 'ServerInfoAwareConnection::requiresQueryForServerVersion() is deprecated and removed in DBAL 3.' - ); - - return false; - } - - /** - * {@inheritdoc} - */ - public function prepare($sql) - { - return new Statement($this->dbh, $sql, $this); - } - - /** - * {@inheritdoc} - */ - public function query() - { - $args = func_get_args(); - $sql = $args[0]; - //$fetchMode = $args[1]; - $stmt = $this->prepare($sql); - $stmt->execute(); - - return $stmt; - } - - /** - * {@inheritdoc} - */ - public function quote($value, $type = ParameterType::STRING) - { - if (is_int($value) || is_float($value)) { - return $value; - } - - $value = str_replace("'", "''", $value); - - return "'" . addcslashes($value, "\000\n\r\\\032") . "'"; - } - - /** - * {@inheritdoc} - */ - public function exec($sql) - { - $stmt = $this->prepare($sql); - $stmt->execute(); - - return $stmt->rowCount(); - } - - /** - * {@inheritdoc} - * - * @param string|null $name - * - * @return int|false - */ - public function lastInsertId($name = null) - { - if ($name === null) { - return false; - } - - $sql = 'SELECT ' . $name . '.CURRVAL FROM DUAL'; - $stmt = $this->query($sql); - $result = $stmt->fetchColumn(); - - if ($result === false) { - throw SequenceDoesNotExist::new(); - } - - return (int) $result; - } - - /** - * Returns the current execution mode. - * - * @internal - * - * @return int - */ - public function getExecuteMode() - { - return $this->executeMode; - } - - /** - * {@inheritdoc} - */ - public function beginTransaction() - { - $this->executeMode = OCI_NO_AUTO_COMMIT; - - return true; - } - - /** - * {@inheritdoc} - */ - public function commit() - { - if (! oci_commit($this->dbh)) { - throw OCI8Exception::fromErrorInfo($this->errorInfo()); - } - - $this->executeMode = OCI_COMMIT_ON_SUCCESS; - - return true; - } - - /** - * {@inheritdoc} - */ - public function rollBack() - { - if (! oci_rollback($this->dbh)) { - throw OCI8Exception::fromErrorInfo($this->errorInfo()); - } - - $this->executeMode = OCI_COMMIT_ON_SUCCESS; - - return true; - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorCode() - { - $error = oci_error($this->dbh); - - if ($error !== false) { - return $error['code']; - } - - return null; - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorInfo() - { - $error = oci_error($this->dbh); - - if ($error === false) { - return []; - } - - return $error; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php deleted file mode 100644 index 64de0194c..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php +++ /dev/null @@ -1,27 +0,0 @@ - OCI_BOTH, - FetchMode::ASSOCIATIVE => OCI_ASSOC, - FetchMode::NUMERIC => OCI_NUM, - FetchMode::COLUMN => OCI_NUM, - ]; - - /** @var int */ - protected $_defaultFetchMode = FetchMode::MIXED; - - /** @var string[] */ - protected $_paramMap = []; - - /** - * Holds references to bound parameter values. - * - * This is a new requirement for PHP7's oci8 extension that prevents bound values from being garbage collected. - * - * @var mixed[] - */ - private $boundValues = []; - - /** - * Indicates whether the statement is in the state when fetching results is possible - * - * @var bool - */ - private $result = false; - - /** - * Creates a new OCI8Statement that uses the given connection handle and SQL statement. - * - * @internal The statement can be only instantiated by its driver connection. - * - * @param resource $dbh The connection handle. - * @param string $query The SQL query. - */ - public function __construct($dbh, $query, OCI8Connection $conn) - { - [$query, $paramMap] = self::convertPositionalToNamedPlaceholders($query); - - $stmt = oci_parse($dbh, $query); - assert(is_resource($stmt)); - - $this->_sth = $stmt; - $this->_dbh = $dbh; - $this->_paramMap = $paramMap; - $this->_conn = $conn; - } - - /** - * Converts positional (?) into named placeholders (:param). - * - * Oracle does not support positional parameters, hence this method converts all - * positional parameters into artificially named parameters. Note that this conversion - * is not perfect. All question marks (?) in the original statement are treated as - * placeholders and converted to a named parameter. - * - * The algorithm uses a state machine with two possible states: InLiteral and NotInLiteral. - * Question marks inside literal strings are therefore handled correctly by this method. - * This comes at a cost, the whole sql statement has to be looped over. - * - * @internal - * - * @param string $statement The SQL statement to convert. - * - * @return mixed[] [0] => the statement value (string), [1] => the paramMap value (array). - * - * @throws OCI8Exception - * - * @todo extract into utility class in Doctrine\DBAL\Util namespace - * @todo review and test for lost spaces. we experienced missing spaces with oci8 in some sql statements. - */ - public static function convertPositionalToNamedPlaceholders($statement) - { - $fragmentOffset = $tokenOffset = 0; - $fragments = $paramMap = []; - $currentLiteralDelimiter = null; - - do { - if (! $currentLiteralDelimiter) { - $result = self::findPlaceholderOrOpeningQuote( - $statement, - $tokenOffset, - $fragmentOffset, - $fragments, - $currentLiteralDelimiter, - $paramMap - ); - } else { - $result = self::findClosingQuote($statement, $tokenOffset, $currentLiteralDelimiter); - } - } while ($result); - - if ($currentLiteralDelimiter !== null) { - throw NonTerminatedStringLiteral::new($tokenOffset - 1); - } - - $fragments[] = substr($statement, $fragmentOffset); - $statement = implode('', $fragments); - - return [$statement, $paramMap]; - } - - /** - * Finds next placeholder or opening quote. - * - * @param string $statement The SQL statement to parse - * @param int $tokenOffset The offset to start searching from - * @param int $fragmentOffset The offset to build the next fragment from - * @param string[] $fragments Fragments of the original statement - * not containing placeholders - * @param string|null $currentLiteralDelimiter The delimiter of the current string literal - * or NULL if not currently in a literal - * @param array $paramMap Mapping of the original parameter positions - * to their named replacements - * - * @return bool Whether the token was found - */ - private static function findPlaceholderOrOpeningQuote( - $statement, - &$tokenOffset, - &$fragmentOffset, - &$fragments, - &$currentLiteralDelimiter, - &$paramMap - ) { - $token = self::findToken($statement, $tokenOffset, '/[?\'"]/'); - - if (! $token) { - return false; - } - - if ($token === '?') { - $position = count($paramMap) + 1; - $param = ':param' . $position; - $fragments[] = substr($statement, $fragmentOffset, $tokenOffset - $fragmentOffset); - $fragments[] = $param; - $paramMap[$position] = $param; - $tokenOffset += 1; - $fragmentOffset = $tokenOffset; - - return true; - } - - $currentLiteralDelimiter = $token; - ++$tokenOffset; - - return true; - } - - /** - * Finds closing quote - * - * @param string $statement The SQL statement to parse - * @param int $tokenOffset The offset to start searching from - * @param string $currentLiteralDelimiter The delimiter of the current string literal - * - * @return bool Whether the token was found - * - * @param-out string|null $currentLiteralDelimiter - */ - private static function findClosingQuote( - $statement, - &$tokenOffset, - &$currentLiteralDelimiter - ) { - $token = self::findToken( - $statement, - $tokenOffset, - '/' . preg_quote($currentLiteralDelimiter, '/') . '/' - ); - - if (! $token) { - return false; - } - - $currentLiteralDelimiter = null; - ++$tokenOffset; - - return true; - } - - /** - * Finds the token described by regex starting from the given offset. Updates the offset with the position - * where the token was found. - * - * @param string $statement The SQL statement to parse - * @param int $offset The offset to start searching from - * @param string $regex The regex containing token pattern - * - * @return string|null Token or NULL if not found - */ - private static function findToken($statement, &$offset, $regex) - { - if (preg_match($regex, $statement, $matches, PREG_OFFSET_CAPTURE, $offset)) { - $offset = $matches[0][1]; - - return $matches[0][0]; - } - - return null; - } - - /** - * {@inheritdoc} - */ - public function bindValue($param, $value, $type = ParameterType::STRING) - { - return $this->bindParam($param, $value, $type, null); - } - - /** - * {@inheritdoc} - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) - { - if (is_int($param)) { - if (! isset($this->_paramMap[$param])) { - throw UnknownParameterIndex::new($param); - } - - $param = $this->_paramMap[$param]; - } - - if ($type === ParameterType::LARGE_OBJECT) { - $lob = oci_new_descriptor($this->_dbh, OCI_D_LOB); - - assert($lob !== false); - - $lob->writeTemporary($variable, OCI_TEMP_BLOB); - - $variable =& $lob; - } - - $this->boundValues[$param] =& $variable; - - return oci_bind_by_name( - $this->_sth, - $param, - $variable, - $length ?? -1, - $this->convertParameterType($type) - ); - } - - /** - * Converts DBAL parameter type to oci8 parameter type - */ - private function convertParameterType(int $type): int - { - switch ($type) { - case ParameterType::BINARY: - return OCI_B_BIN; - - case ParameterType::LARGE_OBJECT: - return OCI_B_BLOB; - - default: - return SQLT_CHR; - } - } - - /** - * {@inheritdoc} - * - * @deprecated Use free() instead. - */ - public function closeCursor() - { - $this->free(); - - return true; - } - - /** - * {@inheritdoc} - */ - public function columnCount() - { - return oci_num_fields($this->_sth) ?: 0; - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorCode() - { - $error = oci_error($this->_sth); - if ($error !== false) { - $error = $error['code']; - } - - return $error; - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorInfo() - { - $error = oci_error($this->_sth); - - if ($error === false) { - return []; - } - - return $error; - } - - /** - * {@inheritdoc} - */ - public function execute($params = null) - { - if ($params) { - $hasZeroIndex = array_key_exists(0, $params); - - foreach ($params as $key => $val) { - if ($hasZeroIndex && is_int($key)) { - $this->bindValue($key + 1, $val); - } else { - $this->bindValue($key, $val); - } - } - } - - $ret = @oci_execute($this->_sth, $this->_conn->getExecuteMode()); - if (! $ret) { - throw OCI8Exception::fromErrorInfo($this->errorInfo()); - } - - $this->result = true; - - return $ret; - } - - /** - * {@inheritdoc} - * - * @deprecated Use one of the fetch- or iterate-related methods. - */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) - { - $this->_defaultFetchMode = $fetchMode; - - return true; - } - - /** - * {@inheritdoc} - * - * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. - */ - #[ReturnTypeWillChange] - public function getIterator() - { - return new StatementIterator($this); - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. - */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) - { - // do not try fetching from the statement if it's not expected to contain result - // in order to prevent exceptional situation - if (! $this->result) { - return false; - } - - $fetchMode = $fetchMode ?: $this->_defaultFetchMode; - - if ($fetchMode === FetchMode::COLUMN) { - return $this->fetchColumn(); - } - - if ($fetchMode === FetchMode::STANDARD_OBJECT) { - return oci_fetch_object($this->_sth); - } - - if (! isset(self::$fetchModeMap[$fetchMode])) { - throw new InvalidArgumentException('Invalid fetch style: ' . $fetchMode); - } - - return oci_fetch_array( - $this->_sth, - self::$fetchModeMap[$fetchMode] | OCI_RETURN_NULLS | OCI_RETURN_LOBS - ); - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. - */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) - { - $fetchMode = $fetchMode ?: $this->_defaultFetchMode; - - $result = []; - - if ($fetchMode === FetchMode::STANDARD_OBJECT) { - while ($row = $this->fetch($fetchMode)) { - $result[] = $row; - } - - return $result; - } - - if (! isset(self::$fetchModeMap[$fetchMode])) { - throw new InvalidArgumentException('Invalid fetch style: ' . $fetchMode); - } - - if (self::$fetchModeMap[$fetchMode] === OCI_BOTH) { - while ($row = $this->fetch($fetchMode)) { - $result[] = $row; - } - } else { - $fetchStructure = OCI_FETCHSTATEMENT_BY_ROW; - - if ($fetchMode === FetchMode::COLUMN) { - $fetchStructure = OCI_FETCHSTATEMENT_BY_COLUMN; - } - - // do not try fetching from the statement if it's not expected to contain result - // in order to prevent exceptional situation - if (! $this->result) { - return []; - } - - oci_fetch_all( - $this->_sth, - $result, - 0, - -1, - self::$fetchModeMap[$fetchMode] | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS - ); - - if ($fetchMode === FetchMode::COLUMN) { - $result = $result[0]; - } - } - - return $result; - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchOne() instead. - */ - public function fetchColumn($columnIndex = 0) - { - // do not try fetching from the statement if it's not expected to contain result - // in order to prevent exceptional situation - if (! $this->result) { - return false; - } - - $row = oci_fetch_array($this->_sth, OCI_NUM | OCI_RETURN_NULLS | OCI_RETURN_LOBS); - - if ($row === false) { - return false; - } - - return $row[$columnIndex] ?? null; - } - - /** - * {@inheritdoc} - */ - public function rowCount() - { - return oci_num_rows($this->_sth) ?: 0; - } - - /** - * {@inheritdoc} - */ - public function fetchNumeric() - { - return $this->doFetch(OCI_NUM); - } - - /** - * {@inheritdoc} - */ - public function fetchAssociative() - { - return $this->doFetch(OCI_ASSOC); - } - - /** - * {@inheritdoc} - */ - public function fetchOne() - { - return FetchUtils::fetchOne($this); - } - - /** - * {@inheritdoc} - */ - public function fetchAllNumeric(): array - { - return $this->doFetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_ROW); - } - - /** - * {@inheritdoc} - */ - public function fetchAllAssociative(): array - { - return $this->doFetchAll(OCI_ASSOC, OCI_FETCHSTATEMENT_BY_ROW); - } - - /** - * {@inheritdoc} - */ - public function fetchFirstColumn(): array - { - return $this->doFetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_COLUMN)[0]; - } - - public function free(): void - { - // not having the result means there's nothing to close - if (! $this->result) { - return; - } - - oci_cancel($this->_sth); - - $this->result = false; - } - - /** - * @return mixed|false - */ - private function doFetch(int $mode) - { - // do not try fetching from the statement if it's not expected to contain the result - // in order to prevent exceptional situation - if (! $this->result) { - return false; - } - - return oci_fetch_array( - $this->_sth, - $mode | OCI_RETURN_NULLS | OCI_RETURN_LOBS - ); - } - - /** - * @return array - */ - private function doFetchAll(int $mode, int $fetchStructure): array - { - // do not try fetching from the statement if it's not expected to contain the result - // in order to prevent exceptional situation - if (! $this->result) { - return []; - } - - oci_fetch_all( - $this->_sth, - $result, - 0, - -1, - $mode | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS - ); - - return $result; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Statement.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Statement.php deleted file mode 100644 index 2cab1e59c..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Statement.php +++ /dev/null @@ -1,7 +0,0 @@ -setAttribute(PDO::ATTR_STATEMENT_CLASS, [Statement::class, []]); - $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - } catch (PDOException $exception) { - throw Exception::new($exception); - } - } - - /** - * {@inheritdoc} - */ - #[ReturnTypeWillChange] - public function exec($sql) - { - try { - $result = parent::exec($sql); - assert($result !== false); - - return $result; - } catch (PDOException $exception) { - throw Exception::new($exception); - } - } - - /** - * {@inheritdoc} - */ - public function getServerVersion() - { - return PDO::getAttribute(PDO::ATTR_SERVER_VERSION); - } - - /** - * @param string $sql - * @param array $driverOptions - * - * @return PDOStatement - */ - #[ReturnTypeWillChange] - public function prepare($sql, $driverOptions = []) - { - try { - $statement = parent::prepare($sql, $driverOptions); - assert($statement instanceof PDOStatement); - - return $statement; - } catch (PDOException $exception) { - throw Exception::new($exception); - } - } - - /** - * {@inheritdoc} - */ - #[ReturnTypeWillChange] - public function quote($value, $type = ParameterType::STRING) - { - return parent::quote($value, $type); - } - - /** - * {@inheritdoc} - */ - #[ReturnTypeWillChange] - public function lastInsertId($name = null) - { - try { - if ($name === null) { - return parent::lastInsertId(); - } - - return parent::lastInsertId($name); - } catch (PDOException $exception) { - throw Exception::new($exception); - } - } - - /** - * {@inheritdoc} - */ - public function requiresQueryForServerVersion() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4114', - 'ServerInfoAwareConnection::requiresQueryForServerVersion() is deprecated and removed in DBAL 3.' - ); - - return false; - } - - /** - * @param mixed ...$args - */ - private function doQuery(...$args): PDOStatement - { - try { - $stmt = parent::query(...$args); - } catch (PDOException $exception) { - throw Exception::new($exception); - } - - assert($stmt instanceof PDOStatement); - - return $stmt; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOException.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOException.php deleted file mode 100644 index 8c0d05d2a..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOException.php +++ /dev/null @@ -1,64 +0,0 @@ -getMessage(), 0, $exception); - - $this->code = $exception->getCode(); - $this->errorInfo = $exception->errorInfo; - $this->errorCode = $exception->errorInfo[1] ?? $exception->getCode(); - $this->sqlState = $exception->errorInfo[0] ?? $exception->getCode(); - } - - /** - * {@inheritdoc} - */ - public function getErrorCode() - { - /** @psalm-suppress ImpureMethodCall */ - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4112', - 'Driver\AbstractException::getErrorCode() is deprecated, use getSQLState() or getCode() instead.' - ); - - return $this->errorCode; - } - - /** - * {@inheritdoc} - */ - public function getSQLState() - { - return $this->sqlState; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php deleted file mode 100644 index 875310ee8..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php +++ /dev/null @@ -1,70 +0,0 @@ -_constructPdoDsn($params), - $username, - $password, - $driverOptions - ); - } - - /** - * Constructs the IBM PDO DSN. - * - * @param mixed[] $params - * - * @return string The DSN. - */ - private function _constructPdoDsn(array $params) - { - $dsn = 'ibm:'; - if (isset($params['host'])) { - $dsn .= 'HOSTNAME=' . $params['host'] . ';'; - } - - if (isset($params['port'])) { - $dsn .= 'PORT=' . $params['port'] . ';'; - } - - $dsn .= 'PROTOCOL=TCPIP;'; - if (isset($params['dbname'])) { - $dsn .= 'DATABASE=' . $params['dbname'] . ';'; - } - - return $dsn; - } - - /** - * {@inheritdoc} - * - * @deprecated - */ - public function getName() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'Driver::getName() is deprecated' - ); - - return 'pdo_ibm'; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php deleted file mode 100644 index 70d56778d..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php +++ /dev/null @@ -1,85 +0,0 @@ -constructPdoDsn($params), - $username, - $password, - $driverOptions - ); - } catch (PDOException $e) { - throw Exception::driverException($this, $e); - } - - return $conn; - } - - /** - * Constructs the MySql PDO DSN. - * - * @param mixed[] $params - * - * @return string The DSN. - */ - protected function constructPdoDsn(array $params) - { - $dsn = 'mysql:'; - if (isset($params['host']) && $params['host'] !== '') { - $dsn .= 'host=' . $params['host'] . ';'; - } - - if (isset($params['port'])) { - $dsn .= 'port=' . $params['port'] . ';'; - } - - if (isset($params['dbname'])) { - $dsn .= 'dbname=' . $params['dbname'] . ';'; - } - - if (isset($params['unix_socket'])) { - $dsn .= 'unix_socket=' . $params['unix_socket'] . ';'; - } - - if (isset($params['charset'])) { - $dsn .= 'charset=' . $params['charset'] . ';'; - } - - return $dsn; - } - - /** - * {@inheritdoc} - * - * @deprecated - */ - public function getName() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'Driver::getName() is deprecated' - ); - - return 'pdo_mysql'; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php deleted file mode 100644 index a84c67fb6..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php +++ /dev/null @@ -1,66 +0,0 @@ -constructPdoDsn($params), - $username, - $password, - $driverOptions - ); - } catch (PDOException $e) { - throw Exception::driverException($this, $e); - } - } - - /** - * Constructs the Oracle PDO DSN. - * - * @param mixed[] $params - * - * @return string The DSN. - */ - private function constructPdoDsn(array $params) - { - $dsn = 'oci:dbname=' . $this->getEasyConnectString($params); - - if (isset($params['charset'])) { - $dsn .= ';charset=' . $params['charset']; - } - - return $dsn; - } - - /** - * {@inheritdoc} - */ - public function getName() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'Driver::getName() is deprecated' - ); - - return 'pdo_oracle'; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php deleted file mode 100644 index bbdf23fdc..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php +++ /dev/null @@ -1,128 +0,0 @@ -_constructPdoDsn($params), - $username, - $password, - $driverOptions - ); - - if ( - defined('PDO::PGSQL_ATTR_DISABLE_PREPARES') - && (! isset($driverOptions[\PDO::PGSQL_ATTR_DISABLE_PREPARES]) - || $driverOptions[\PDO::PGSQL_ATTR_DISABLE_PREPARES] === true - ) - ) { - $pdo->setAttribute(\PDO::PGSQL_ATTR_DISABLE_PREPARES, true); - } - - /* defining client_encoding via SET NAMES to avoid inconsistent DSN support - * - the 'client_encoding' connection param only works with postgres >= 9.1 - * - passing client_encoding via the 'options' param breaks pgbouncer support - */ - if (isset($params['charset'])) { - $pdo->exec('SET NAMES \'' . $params['charset'] . '\''); - } - - return $pdo; - } catch (PDOException $e) { - throw Exception::driverException($this, $e); - } - } - - /** - * Constructs the Postgres PDO DSN. - * - * @param mixed[] $params - * - * @return string The DSN. - */ - private function _constructPdoDsn(array $params) - { - $dsn = 'pgsql:'; - - if (isset($params['host']) && $params['host'] !== '') { - $dsn .= 'host=' . $params['host'] . ';'; - } - - if (isset($params['port']) && $params['port'] !== '') { - $dsn .= 'port=' . $params['port'] . ';'; - } - - if (isset($params['dbname'])) { - $dsn .= 'dbname=' . $params['dbname'] . ';'; - } elseif (isset($params['default_dbname'])) { - $dsn .= 'dbname=' . $params['default_dbname'] . ';'; - } else { - // Used for temporary connections to allow operations like dropping the database currently connected to. - // Connecting without an explicit database does not work, therefore "postgres" database is used - // as it is mostly present in every server setup. - $dsn .= 'dbname=postgres;'; - } - - if (isset($params['sslmode'])) { - $dsn .= 'sslmode=' . $params['sslmode'] . ';'; - } - - if (isset($params['sslrootcert'])) { - $dsn .= 'sslrootcert=' . $params['sslrootcert'] . ';'; - } - - if (isset($params['sslcert'])) { - $dsn .= 'sslcert=' . $params['sslcert'] . ';'; - } - - if (isset($params['sslkey'])) { - $dsn .= 'sslkey=' . $params['sslkey'] . ';'; - } - - if (isset($params['sslcrl'])) { - $dsn .= 'sslcrl=' . $params['sslcrl'] . ';'; - } - - if (isset($params['application_name'])) { - $dsn .= 'application_name=' . $params['application_name'] . ';'; - } - - return $dsn; - } - - /** - * {@inheritdoc} - * - * @deprecated - */ - public function getName() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'Driver::getName() is deprecated' - ); - - return 'pdo_pgsql'; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOQueryImplementation.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOQueryImplementation.php deleted file mode 100644 index 68003d170..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOQueryImplementation.php +++ /dev/null @@ -1,41 +0,0 @@ -= 80000) { - /** - * @internal - */ - trait PDOQueryImplementation - { - /** - * @return PDOStatement - */ - #[ReturnTypeWillChange] - public function query(?string $query = null, ?int $fetchMode = null, mixed ...$fetchModeArgs) - { - return $this->doQuery($query, $fetchMode, ...$fetchModeArgs); - } - } -} else { - /** - * @internal - */ - trait PDOQueryImplementation - { - /** - * @return PDOStatement - */ - public function query() - { - return $this->doQuery(...func_get_args()); - } - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php deleted file mode 100644 index efced0c00..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php +++ /dev/null @@ -1,93 +0,0 @@ - ['callback' => [SqlitePlatform::class, 'udfSqrt'], 'numArgs' => 1], - 'mod' => ['callback' => [SqlitePlatform::class, 'udfMod'], 'numArgs' => 2], - 'locate' => ['callback' => [SqlitePlatform::class, 'udfLocate'], 'numArgs' => -1], - ]; - - /** - * {@inheritdoc} - */ - public function connect(array $params, $username = null, $password = null, array $driverOptions = []) - { - if (isset($driverOptions['userDefinedFunctions'])) { - $this->_userDefinedFunctions = array_merge( - $this->_userDefinedFunctions, - $driverOptions['userDefinedFunctions'] - ); - unset($driverOptions['userDefinedFunctions']); - } - - try { - $pdo = new PDO\Connection( - $this->_constructPdoDsn($params), - $username, - $password, - $driverOptions - ); - } catch (PDOException $ex) { - throw Exception::driverException($this, $ex); - } - - foreach ($this->_userDefinedFunctions as $fn => $data) { - $pdo->sqliteCreateFunction($fn, $data['callback'], $data['numArgs']); - } - - return $pdo; - } - - /** - * Constructs the Sqlite PDO DSN. - * - * @param mixed[] $params - * - * @return string The DSN. - */ - protected function _constructPdoDsn(array $params) - { - $dsn = 'sqlite:'; - if (isset($params['path'])) { - $dsn .= $params['path']; - } elseif (isset($params['memory'])) { - $dsn .= ':memory:'; - } - - return $dsn; - } - - /** - * {@inheritdoc} - * - * @deprecated - */ - public function getName() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'Driver::getName() is deprecated' - ); - - return 'pdo_sqlite'; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php deleted file mode 100644 index efc483cd9..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php +++ /dev/null @@ -1,49 +0,0 @@ -setAttribute(\PDO::ATTR_STATEMENT_CLASS, [PDO\SQLSrv\Statement::class, []]); - } - - /** - * {@inheritDoc} - */ - public function lastInsertId($name = null) - { - if ($name === null) { - return parent::lastInsertId($name); - } - - $stmt = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?'); - $stmt->execute([$name]); - - if ($stmt instanceof Result) { - return $stmt->fetchOne(); - } - - return $stmt->fetchColumn(); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php deleted file mode 100644 index 8784582eb..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php +++ /dev/null @@ -1,107 +0,0 @@ - $value) { - if (is_int($option)) { - $pdoOptions[$option] = $value; - } else { - $dsnOptions[$option] = $value; - } - } - - return new PDO\SQLSrv\Connection( - $this->_constructPdoDsn($params, $dsnOptions), - $username, - $password, - $pdoOptions - ); - } - - /** - * Constructs the Sqlsrv PDO DSN. - * - * @param mixed[] $params - * @param string[] $connectionOptions - * - * @return string The DSN. - */ - private function _constructPdoDsn(array $params, array $connectionOptions) - { - $dsn = 'sqlsrv:server='; - - if (isset($params['host'])) { - $dsn .= $params['host']; - - if (isset($params['port'])) { - $dsn .= ',' . $params['port']; - } - } elseif (isset($params['port'])) { - throw PortWithoutHost::new(); - } - - if (isset($params['dbname'])) { - $connectionOptions['Database'] = $params['dbname']; - } - - if (isset($params['MultipleActiveResultSets'])) { - $connectionOptions['MultipleActiveResultSets'] = $params['MultipleActiveResultSets'] ? 'true' : 'false'; - } - - return $dsn . $this->getConnectionOptionsDsn($connectionOptions); - } - - /** - * Converts a connection options array to the DSN - * - * @param string[] $connectionOptions - */ - private function getConnectionOptionsDsn(array $connectionOptions): string - { - $connectionOptionsDsn = ''; - - foreach ($connectionOptions as $paramName => $paramValue) { - $connectionOptionsDsn .= sprintf(';%s=%s', $paramName, $paramValue); - } - - return $connectionOptionsDsn; - } - - /** - * {@inheritdoc} - * - * @deprecated - */ - public function getName() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'Driver::getName() is deprecated' - ); - - return 'pdo_sqlsrv'; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Statement.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Statement.php deleted file mode 100644 index 5669ccc27..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Statement.php +++ /dev/null @@ -1,46 +0,0 @@ -bindParam($param, $value, $type); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php deleted file mode 100644 index 84f6b81ee..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php +++ /dev/null @@ -1,316 +0,0 @@ - PDO::PARAM_NULL, - ParameterType::INTEGER => PDO::PARAM_INT, - ParameterType::STRING => PDO::PARAM_STR, - ParameterType::ASCII => PDO::PARAM_STR, - ParameterType::BINARY => PDO::PARAM_LOB, - ParameterType::LARGE_OBJECT => PDO::PARAM_LOB, - ParameterType::BOOLEAN => PDO::PARAM_BOOL, - ]; - - private const FETCH_MODE_MAP = [ - FetchMode::ASSOCIATIVE => PDO::FETCH_ASSOC, - FetchMode::NUMERIC => PDO::FETCH_NUM, - FetchMode::MIXED => PDO::FETCH_BOTH, - FetchMode::STANDARD_OBJECT => PDO::FETCH_OBJ, - FetchMode::COLUMN => PDO::FETCH_COLUMN, - FetchMode::CUSTOM_OBJECT => PDO::FETCH_CLASS, - ]; - - /** - * Protected constructor. - * - * @internal The statement can be only instantiated by its driver connection. - */ - protected function __construct() - { - } - - /** - * {@inheritdoc} - */ - #[ReturnTypeWillChange] - public function bindValue($param, $value, $type = ParameterType::STRING) - { - $type = $this->convertParamType($type); - - try { - return parent::bindValue($param, $value, $type); - } catch (PDOException $exception) { - throw Exception::new($exception); - } - } - - /** - * @param mixed $param - * @param mixed $variable - * @param int $type - * @param int|null $length - * @param mixed $driverOptions - * - * @return bool - */ - #[ReturnTypeWillChange] - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null) - { - $type = $this->convertParamType($type); - - try { - return parent::bindParam($param, $variable, $type, ...array_slice(func_get_args(), 3)); - } catch (PDOException $exception) { - throw Exception::new($exception); - } - } - - /** - * {@inheritdoc} - * - * @deprecated Use free() instead. - */ - #[ReturnTypeWillChange] - public function closeCursor() - { - try { - return parent::closeCursor(); - } catch (PDOException $exception) { - // Exceptions not allowed by the interface. - // In case driver implementations do not adhere to the interface, silence exceptions here. - return true; - } - } - - /** - * {@inheritdoc} - */ - #[ReturnTypeWillChange] - public function execute($params = null) - { - try { - return parent::execute($params); - } catch (PDOException $exception) { - throw Exception::new($exception); - } - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. - */ - #[ReturnTypeWillChange] - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) - { - $args = func_get_args(); - - if (isset($args[0])) { - $args[0] = $this->convertFetchMode($args[0]); - } - - try { - return parent::fetch(...$args); - } catch (PDOException $exception) { - throw Exception::new($exception); - } - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchOne() instead. - */ - #[ReturnTypeWillChange] - public function fetchColumn($columnIndex = 0) - { - try { - return parent::fetchColumn($columnIndex); - } catch (PDOException $exception) { - throw Exception::new($exception); - } - } - - /** - * {@inheritdoc} - */ - public function fetchNumeric() - { - return $this->fetch(PDO::FETCH_NUM); - } - - /** - * {@inheritdoc} - */ - public function fetchAssociative() - { - return $this->fetch(PDO::FETCH_ASSOC); - } - - /** - * {@inheritdoc} - */ - public function fetchOne() - { - return $this->fetch(PDO::FETCH_COLUMN); - } - - /** - * {@inheritdoc} - */ - public function fetchAllNumeric(): array - { - return $this->fetchAll(PDO::FETCH_NUM); - } - - /** - * {@inheritdoc} - */ - public function fetchAllAssociative(): array - { - return $this->fetchAll(PDO::FETCH_ASSOC); - } - - /** - * {@inheritdoc} - */ - public function fetchFirstColumn(): array - { - return $this->fetchAll(PDO::FETCH_COLUMN); - } - - public function free(): void - { - parent::closeCursor(); - } - - /** - * @param mixed ...$args - */ - private function doSetFetchMode(int $fetchMode, ...$args): bool - { - $fetchMode = $this->convertFetchMode($fetchMode); - - // This thin wrapper is necessary to shield against the weird signature - // of PDOStatement::setFetchMode(): even if the second and third - // parameters are optional, PHP will not let us remove it from this - // declaration. - $slice = []; - - foreach ($args as $arg) { - if ($arg === null) { - break; - } - - $slice[] = $arg; - } - - try { - return parent::setFetchMode($fetchMode, ...$slice); - } catch (PDOException $exception) { - throw Exception::new($exception); - } - } - - /** - * @param mixed ...$args - * - * @return mixed[] - */ - private function doFetchAll(...$args): array - { - if (isset($args[0])) { - $args[0] = $this->convertFetchMode($args[0]); - } - - $slice = []; - - foreach ($args as $arg) { - if ($arg === null) { - break; - } - - $slice[] = $arg; - } - - try { - $data = parent::fetchAll(...$slice); - } catch (PDOException $exception) { - throw Exception::new($exception); - } - - assert(is_array($data)); - - return $data; - } - - /** - * Converts DBAL parameter type to PDO parameter type - * - * @param int $type Parameter type - */ - private function convertParamType(int $type): int - { - if (! isset(self::PARAM_TYPE_MAP[$type])) { - // TODO: next major: throw an exception - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3088', - 'Using a PDO parameter type (%d given) is deprecated, ' . - 'use \Doctrine\DBAL\Types\Types constants instead.', - $type - ); - - return $type; - } - - return self::PARAM_TYPE_MAP[$type]; - } - - /** - * Converts DBAL fetch mode to PDO fetch mode - * - * @param int $fetchMode Fetch mode - */ - private function convertFetchMode(int $fetchMode): int - { - if (! isset(self::FETCH_MODE_MAP[$fetchMode])) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3088', - 'Using an unsupported PDO fetch mode or a bitmask of fetch modes (%d given)' . - ' is deprecated and will cause an error in Doctrine DBAL 3.0', - $fetchMode - ); - - return $fetchMode; - } - - return self::FETCH_MODE_MAP[$fetchMode]; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatementImplementations.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatementImplementations.php deleted file mode 100644 index a1f9ae616..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatementImplementations.php +++ /dev/null @@ -1,77 +0,0 @@ -= 80000) { - /** - * @internal - */ - trait PDOStatementImplementations - { - /** - * @deprecated Use one of the fetch- or iterate-related methods. - * - * @param int $mode - * @param mixed ...$args - * - * @return bool - */ - #[ReturnTypeWillChange] - public function setFetchMode($mode, ...$args) - { - return $this->doSetFetchMode($mode, ...$args); - } - - /** - * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. - * - * @param int|null $mode - * @param mixed ...$args - * - * @return mixed[] - */ - #[ReturnTypeWillChange] - public function fetchAll($mode = null, ...$args) - { - return $this->doFetchAll($mode, ...$args); - } - } -} else { - /** - * @internal - */ - trait PDOStatementImplementations - { - /** - * @deprecated Use one of the fetch- or iterate-related methods. - * - * @param int $fetchMode - * @param mixed $arg2 - * @param mixed $arg3 - */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null): bool - { - return $this->doSetFetchMode(...func_get_args()); - } - - /** - * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. - * - * @param int|null $fetchMode - * @param mixed $fetchArgument - * @param mixed $ctorArgs - * - * @return mixed[] - */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) - { - return $this->doFetchAll(...func_get_args()); - } - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PingableConnection.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PingableConnection.php deleted file mode 100644 index 42202e7ab..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PingableConnection.php +++ /dev/null @@ -1,19 +0,0 @@ -|false - * - * @throws Exception - */ - public function fetchNumeric(); - - /** - * Returns the next row of the result as an associative array or FALSE if there are no more rows. - * - * @return array|false - * - * @throws Exception - */ - public function fetchAssociative(); - - /** - * Returns the first value of the next row of the result or FALSE if there are no more rows. - * - * @return mixed|false - * - * @throws Exception - */ - public function fetchOne(); - - /** - * Returns an array containing all of the result rows represented as numeric arrays. - * - * @return array> - * - * @throws Exception - */ - public function fetchAllNumeric(): array; - - /** - * Returns an array containing all of the result rows represented as associative arrays. - * - * @return array> - * - * @throws Exception - */ - public function fetchAllAssociative(): array; - - /** - * Returns an array containing the values of the first column of the result. - * - * @return array - * - * @throws Exception - */ - public function fetchFirstColumn(): array; - - /** - * Returns the number of rows affected by the DELETE, INSERT, or UPDATE statement that produced the result. - * - * If the statement executed a SELECT query or a similar platform-specific SQL (e.g. DESCRIBE, SHOW, etc.), - * some database drivers may return the number of rows returned by that query. However, this behaviour - * is not guaranteed for all drivers and should not be relied on in portable applications. - * - * @return int The number of rows. - */ - public function rowCount(); - - /** - * Returns the number of columns in the result - * - * @return int The number of columns in the result. If the columns cannot be counted, - * this method must return 0. - */ - public function columnCount(); - - /** - * Discards the non-fetched portion of the result, enabling the originating statement to be executed again. - */ - public function free(): void; -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php deleted file mode 100644 index 5373ee399..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php +++ /dev/null @@ -1,110 +0,0 @@ -buildDsn( - $params['host'] ?? null, - $params['port'] ?? null, - $params['server'] ?? null, - $params['dbname'] ?? null, - $username, - $password, - $driverOptions - ), - $params['persistent'] ?? false - ); - } catch (SQLAnywhereException $e) { - throw Exception::driverException($this, $e); - } - } - - /** - * {@inheritdoc} - * - * @deprecated - */ - public function getName() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'Driver::getName() is deprecated' - ); - - return 'sqlanywhere'; - } - - /** - * Build the connection string for given connection parameters and driver options. - * - * @param string|null $host Host address to connect to. - * @param int|null $port Port to use for the connection (default to SQL Anywhere standard port 2638). - * @param string|null $server Database server name on the host to connect to. - * SQL Anywhere allows multiple database server instances on the same host, - * therefore specifying the server instance name to use is mandatory. - * @param string|null $dbname Name of the database on the server instance to connect to. - * @param string $username User name to use for connection authentication. - * @param string $password Password to use for connection authentication. - * @param mixed[] $driverOptions Additional parameters to use for the connection. - * - * @return string - */ - private function buildDsn( - $host, - $port, - $server, - $dbname, - $username = null, - $password = null, - array $driverOptions = [] - ) { - $host = $host ?: 'localhost'; - $port = $port ?: 2638; - - if (! empty($server)) { - $server = ';ServerName=' . $server; - } - - return 'HOST=' . $host . ':' . $port . - $server . - ';DBN=' . $dbname . - ';UID=' . $username . - ';PWD=' . $password . - ';' . implode( - ';', - array_map(static function ($key, $value) { - return $key . '=' . $value; - }, array_keys($driverOptions), $driverOptions) - ); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php deleted file mode 100644 index 3c98026ed..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php +++ /dev/null @@ -1,250 +0,0 @@ -connection = $persistent ? @sasql_pconnect($dsn) : @sasql_connect($dsn); - - if (! is_resource($this->connection)) { - throw SQLAnywhereException::fromSQLAnywhereError(); - } - - // Disable PHP warnings on error. - if (! sasql_set_option($this->connection, 'verbose_errors', false)) { - throw SQLAnywhereException::fromSQLAnywhereError($this->connection); - } - - // Enable auto committing by default. - if (! sasql_set_option($this->connection, 'auto_commit', 'on')) { - throw SQLAnywhereException::fromSQLAnywhereError($this->connection); - } - } - - /** - * {@inheritdoc} - * - * @throws SQLAnywhereException - */ - public function beginTransaction() - { - if (! sasql_set_option($this->connection, 'auto_commit', 'off')) { - throw SQLAnywhereException::fromSQLAnywhereError($this->connection); - } - - return true; - } - - /** - * {@inheritdoc} - * - * @throws SQLAnywhereException - */ - public function commit() - { - if (! sasql_commit($this->connection)) { - throw SQLAnywhereException::fromSQLAnywhereError($this->connection); - } - - $this->endTransaction(); - - return true; - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorCode() - { - return sasql_errorcode($this->connection); - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorInfo() - { - return sasql_error($this->connection); - } - - /** - * {@inheritdoc} - */ - public function exec($sql) - { - if (sasql_real_query($this->connection, $sql) === false) { - throw SQLAnywhereException::fromSQLAnywhereError($this->connection); - } - - return sasql_affected_rows($this->connection); - } - - /** - * {@inheritdoc} - */ - public function getServerVersion() - { - $stmt = $this->query("SELECT PROPERTY('ProductVersion')"); - - if ($stmt instanceof Result) { - $version = $stmt->fetchOne(); - } else { - $version = $stmt->fetchColumn(); - } - - assert(is_string($version)); - - return $version; - } - - /** - * {@inheritdoc} - */ - public function lastInsertId($name = null) - { - if ($name === null) { - return sasql_insert_id($this->connection); - } - - $stmt = $this->query('SELECT ' . $name . '.CURRVAL'); - - if ($stmt instanceof Result) { - return $stmt->fetchOne(); - } - - return $stmt->fetchColumn(); - } - - /** - * {@inheritdoc} - */ - public function prepare($sql) - { - return new SQLAnywhereStatement($this->connection, $sql); - } - - /** - * {@inheritdoc} - */ - public function query() - { - $args = func_get_args(); - $stmt = $this->prepare($args[0]); - - $stmt->execute(); - - return $stmt; - } - - /** - * {@inheritdoc} - */ - public function quote($value, $type = ParameterType::STRING) - { - if (is_int($value) || is_float($value)) { - return $value; - } - - return "'" . sasql_escape_string($this->connection, $value) . "'"; - } - - /** - * {@inheritdoc} - */ - public function requiresQueryForServerVersion() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4114', - 'ServerInfoAwareConnection::requiresQueryForServerVersion() is deprecated and removed in DBAL 3.' - ); - - return true; - } - - /** - * {@inheritdoc} - * - * @throws SQLAnywhereException - */ - public function rollBack() - { - if (! sasql_rollback($this->connection)) { - throw SQLAnywhereException::fromSQLAnywhereError($this->connection); - } - - $this->endTransaction(); - - return true; - } - - /** - * Ends transactional mode and enables auto commit again. - * - * @return bool Whether or not ending transactional mode succeeded. - * - * @throws SQLAnywhereException - */ - private function endTransaction() - { - if (! sasql_set_option($this->connection, 'auto_commit', 'on')) { - throw SQLAnywhereException::fromSQLAnywhereError($this->connection); - } - - return true; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereException.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereException.php deleted file mode 100644 index 0edfa4fa5..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereException.php +++ /dev/null @@ -1,76 +0,0 @@ -conn = $conn; - $this->stmt = sasql_prepare($conn, $sql); - - if (! is_resource($this->stmt)) { - throw SQLAnywhereException::fromSQLAnywhereError($conn); - } - } - - /** - * {@inheritdoc} - * - * @throws SQLAnywhereException - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) - { - assert(is_int($param)); - - switch ($type) { - case ParameterType::INTEGER: - case ParameterType::BOOLEAN: - $type = 'i'; - break; - - case ParameterType::LARGE_OBJECT: - $type = 'b'; - break; - - case ParameterType::NULL: - case ParameterType::STRING: - case ParameterType::BINARY: - $type = 's'; - break; - - default: - throw new SQLAnywhereException('Unknown type: ' . $type); - } - - $this->boundValues[$param] =& $variable; - - if (! sasql_stmt_bind_param_ex($this->stmt, $param - 1, $variable, $type, $variable === null)) { - throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); - } - - return true; - } - - /** - * {@inheritdoc} - */ - public function bindValue($param, $value, $type = ParameterType::STRING) - { - assert(is_int($param)); - - return $this->bindParam($param, $value, $type); - } - - /** - * {@inheritdoc} - * - * @deprecated Use free() instead. - * - * @throws SQLAnywhereException - */ - public function closeCursor() - { - if (! sasql_stmt_reset($this->stmt)) { - throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); - } - - return true; - } - - /** - * {@inheritdoc} - */ - public function columnCount() - { - return sasql_stmt_field_count($this->stmt); - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorCode() - { - return sasql_stmt_errno($this->stmt); - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorInfo() - { - return sasql_stmt_error($this->stmt); - } - - /** - * {@inheritdoc} - * - * @throws SQLAnywhereException - */ - public function execute($params = null) - { - if (is_array($params)) { - $hasZeroIndex = array_key_exists(0, $params); - - foreach ($params as $key => $val) { - if ($hasZeroIndex && is_int($key)) { - $this->bindValue($key + 1, $val); - } else { - $this->bindValue($key, $val); - } - } - } - - if (! sasql_stmt_execute($this->stmt)) { - throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); - } - - $this->result = sasql_stmt_result_metadata($this->stmt); - - return true; - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. - * - * @throws SQLAnywhereException - */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) - { - if (! is_resource($this->result)) { - return false; - } - - $fetchMode = $fetchMode ?: $this->defaultFetchMode; - - switch ($fetchMode) { - case FetchMode::COLUMN: - return $this->fetchColumn(); - - case FetchMode::ASSOCIATIVE: - return sasql_fetch_assoc($this->result); - - case FetchMode::MIXED: - return sasql_fetch_array($this->result, SASQL_BOTH); - - case FetchMode::CUSTOM_OBJECT: - $className = $this->defaultFetchClass; - $ctorArgs = $this->defaultFetchClassCtorArgs; - - if (func_num_args() >= 2) { - $args = func_get_args(); - $className = $args[1]; - $ctorArgs = $args[2] ?? []; - } - - $result = sasql_fetch_object($this->result); - - if ($result instanceof stdClass) { - $result = $this->castObject($result, $className, $ctorArgs); - } - - return $result; - - case FetchMode::NUMERIC: - return sasql_fetch_row($this->result); - - case FetchMode::STANDARD_OBJECT: - return sasql_fetch_object($this->result); - - default: - throw new SQLAnywhereException('Fetch mode is not supported: ' . $fetchMode); - } - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. - */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) - { - $rows = []; - - switch ($fetchMode) { - case FetchMode::CUSTOM_OBJECT: - while (($row = $this->fetch(...func_get_args())) !== false) { - $rows[] = $row; - } - - break; - - case FetchMode::COLUMN: - while (($row = $this->fetchColumn()) !== false) { - $rows[] = $row; - } - - break; - - default: - while (($row = $this->fetch($fetchMode)) !== false) { - $rows[] = $row; - } - } - - return $rows; - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchOne() instead. - */ - public function fetchColumn($columnIndex = 0) - { - $row = $this->fetch(FetchMode::NUMERIC); - - if ($row === false) { - return false; - } - - return $row[$columnIndex] ?? null; - } - - /** - * {@inheritdoc} - * - * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. - */ - #[ReturnTypeWillChange] - public function getIterator() - { - return new StatementIterator($this); - } - - /** - * {@inheritDoc} - */ - public function fetchNumeric() - { - if (! is_resource($this->result)) { - return false; - } - - return sasql_fetch_row($this->result); - } - - /** - * {@inheritdoc} - */ - public function fetchAssociative() - { - if (! is_resource($this->result)) { - return false; - } - - return sasql_fetch_assoc($this->result); - } - - /** - * {@inheritdoc} - * - * @throws Exception - */ - public function fetchOne() - { - return FetchUtils::fetchOne($this); - } - - /** - * @return array> - * - * @throws Exception - */ - public function fetchAllNumeric(): array - { - return FetchUtils::fetchAllNumeric($this); - } - - /** - * @return array> - * - * @throws Exception - */ - public function fetchAllAssociative(): array - { - return FetchUtils::fetchAllAssociative($this); - } - - /** - * @return array - * - * @throws Exception - */ - public function fetchFirstColumn(): array - { - return FetchUtils::fetchFirstColumn($this); - } - - /** - * {@inheritdoc} - */ - public function rowCount() - { - return sasql_stmt_affected_rows($this->stmt); - } - - public function free(): void - { - sasql_stmt_reset($this->stmt); - } - - /** - * {@inheritdoc} - * - * @deprecated Use one of the fetch- or iterate-related methods. - */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) - { - $this->defaultFetchMode = $fetchMode; - $this->defaultFetchClass = $arg2 ?: $this->defaultFetchClass; - $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; - - return true; - } - - /** - * Casts a stdClass object to the given class name mapping its' properties. - * - * @param stdClass $sourceObject Object to cast from. - * @param class-string|object $destinationClass Name of the class or class instance to cast to. - * @param mixed[] $ctorArgs Arguments to use for constructing the destination class instance. - * - * @return object - * - * @throws SQLAnywhereException - */ - private function castObject(stdClass $sourceObject, $destinationClass, array $ctorArgs = []) - { - if (! is_string($destinationClass)) { - if (! is_object($destinationClass)) { - throw new SQLAnywhereException(sprintf( - 'Destination class has to be of type string or object, %s given.', - gettype($destinationClass) - )); - } - } else { - $destinationClass = new ReflectionClass($destinationClass); - $destinationClass = $destinationClass->newInstanceArgs($ctorArgs); - } - - $sourceReflection = new ReflectionObject($sourceObject); - $destinationClassReflection = new ReflectionObject($destinationClass); - - foreach ($sourceReflection->getProperties() as $sourceProperty) { - $sourceProperty->setAccessible(true); - - $name = $sourceProperty->getName(); - $value = $sourceProperty->getValue($sourceObject); - - if ($destinationClassReflection->hasProperty($name)) { - $destinationProperty = $destinationClassReflection->getProperty($name); - - $destinationProperty->setAccessible(true); - $destinationProperty->setValue($destinationClass, $value); - } else { - $destinationClass->$name = $value; - } - } - - return $destinationClass; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Connection.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Connection.php deleted file mode 100644 index 6e009b5db..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Connection.php +++ /dev/null @@ -1,7 +0,0 @@ -id = $id; - } - - /** - * @return int - */ - public function getId() - { - return $this->id; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php deleted file mode 100644 index f1974ee61..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php +++ /dev/null @@ -1,226 +0,0 @@ -conn = $conn; - $this->lastInsertId = new LastInsertId(); - } - - /** - * {@inheritdoc} - */ - public function getServerVersion() - { - $serverInfo = sqlsrv_server_info($this->conn); - - return $serverInfo['SQLServerVersion']; - } - - /** - * {@inheritdoc} - */ - public function requiresQueryForServerVersion() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4114', - 'ServerInfoAwareConnection::requiresQueryForServerVersion() is deprecated and removed in DBAL 3.' - ); - - return false; - } - - /** - * {@inheritDoc} - */ - public function prepare($sql) - { - return new Statement($this->conn, $sql, $this->lastInsertId); - } - - /** - * {@inheritDoc} - */ - public function query() - { - $args = func_get_args(); - $sql = $args[0]; - $stmt = $this->prepare($sql); - $stmt->execute(); - - return $stmt; - } - - /** - * {@inheritDoc} - */ - public function quote($value, $type = ParameterType::STRING) - { - if (is_int($value)) { - return $value; - } - - if (is_float($value)) { - return sprintf('%F', $value); - } - - return "'" . str_replace("'", "''", $value) . "'"; - } - - /** - * {@inheritDoc} - */ - public function exec($sql) - { - $stmt = sqlsrv_query($this->conn, $sql); - - if ($stmt === false) { - throw Error::new(); - } - - $rowsAffected = sqlsrv_rows_affected($stmt); - - if ($rowsAffected === false) { - throw Error::new(); - } - - return $rowsAffected; - } - - /** - * {@inheritDoc} - */ - public function lastInsertId($name = null) - { - if ($name !== null) { - $stmt = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?'); - $stmt->execute([$name]); - } else { - $stmt = $this->query('SELECT @@IDENTITY'); - } - - if ($stmt instanceof Result) { - return $stmt->fetchOne(); - } - - return $stmt->fetchColumn(); - } - - /** - * {@inheritDoc} - */ - public function beginTransaction() - { - if (! sqlsrv_begin_transaction($this->conn)) { - throw Error::new(); - } - - return true; - } - - /** - * {@inheritDoc} - */ - public function commit() - { - if (! sqlsrv_commit($this->conn)) { - throw Error::new(); - } - - return true; - } - - /** - * {@inheritDoc} - */ - public function rollBack() - { - if (! sqlsrv_rollback($this->conn)) { - throw Error::new(); - } - - return true; - } - - /** - * {@inheritDoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorCode() - { - $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); - if ($errors) { - return $errors[0]['code']; - } - - return null; - } - - /** - * {@inheritDoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorInfo() - { - return (array) sqlsrv_errors(SQLSRV_ERR_ERRORS); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php deleted file mode 100644 index 312ef6b9d..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php +++ /dev/null @@ -1,24 +0,0 @@ - SQLSRV_FETCH_BOTH, - FetchMode::ASSOCIATIVE => SQLSRV_FETCH_ASSOC, - FetchMode::NUMERIC => SQLSRV_FETCH_NUMERIC, - ]; - - /** - * The name of the default class to instantiate when fetching class instances. - * - * @var string - */ - private $defaultFetchClass = '\stdClass'; - - /** - * The constructor arguments for the default class to instantiate when fetching class instances. - * - * @var mixed[] - */ - private $defaultFetchClassCtorArgs = []; - - /** - * The fetch style. - * - * @var int - */ - private $defaultFetchMode = FetchMode::MIXED; - - /** - * The last insert ID. - * - * @var LastInsertId|null - */ - private $lastInsertId; - - /** - * Indicates whether the statement is in the state when fetching results is possible - * - * @var bool - */ - private $result = false; - - /** - * Append to any INSERT query to retrieve the last insert id. - * - * @deprecated This constant has been deprecated and will be made private in 3.0 - */ - public const LAST_INSERT_ID_SQL = ';SELECT SCOPE_IDENTITY() AS LastInsertId;'; - - /** - * @internal The statement can be only instantiated by its driver connection. - * - * @param resource $conn - * @param string $sql - */ - public function __construct($conn, $sql, ?LastInsertId $lastInsertId = null) - { - $this->conn = $conn; - $this->sql = $sql; - - if (stripos($sql, 'INSERT INTO ') !== 0) { - return; - } - - $this->sql .= self::LAST_INSERT_ID_SQL; - $this->lastInsertId = $lastInsertId; - } - - /** - * {@inheritdoc} - */ - public function bindValue($param, $value, $type = ParameterType::STRING) - { - if (! is_numeric($param)) { - throw new SQLSrvException( - 'sqlsrv does not support named parameters to queries, use question mark (?) placeholders instead.' - ); - } - - $this->variables[$param] = $value; - $this->types[$param] = $type; - - return true; - } - - /** - * {@inheritdoc} - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) - { - if (! is_numeric($param)) { - throw new SQLSrvException( - 'sqlsrv does not support named parameters to queries, use question mark (?) placeholders instead.' - ); - } - - $this->variables[$param] =& $variable; - $this->types[$param] = $type; - - // unset the statement resource if it exists as the new one will need to be bound to the new variable - $this->stmt = null; - - return true; - } - - /** - * {@inheritdoc} - * - * @deprecated Use free() instead. - */ - public function closeCursor() - { - $this->free(); - - return true; - } - - /** - * {@inheritdoc} - */ - public function columnCount() - { - if ($this->stmt === null) { - return 0; - } - - return sqlsrv_num_fields($this->stmt) ?: 0; - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorCode() - { - $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); - if ($errors) { - return $errors[0]['code']; - } - - return false; - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorInfo() - { - return (array) sqlsrv_errors(SQLSRV_ERR_ERRORS); - } - - /** - * {@inheritdoc} - */ - public function execute($params = null) - { - if ($params) { - $hasZeroIndex = array_key_exists(0, $params); - - foreach ($params as $key => $val) { - if ($hasZeroIndex && is_int($key)) { - $this->bindValue($key + 1, $val); - } else { - $this->bindValue($key, $val); - } - } - } - - if (! $this->stmt) { - $this->stmt = $this->prepare(); - } - - if (! sqlsrv_execute($this->stmt)) { - throw Error::new(); - } - - if ($this->lastInsertId) { - sqlsrv_next_result($this->stmt); - sqlsrv_fetch($this->stmt); - $this->lastInsertId->setId(sqlsrv_get_field($this->stmt, 0)); - } - - $this->result = true; - - return true; - } - - /** - * Prepares SQL Server statement resource - * - * @return resource - * - * @throws SQLSrvException - */ - private function prepare() - { - $params = []; - - foreach ($this->variables as $column => &$variable) { - switch ($this->types[$column]) { - case ParameterType::LARGE_OBJECT: - $params[$column - 1] = [ - &$variable, - SQLSRV_PARAM_IN, - SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY), - SQLSRV_SQLTYPE_VARBINARY('max'), - ]; - break; - - case ParameterType::BINARY: - $params[$column - 1] = [ - &$variable, - SQLSRV_PARAM_IN, - SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY), - ]; - break; - - case ParameterType::ASCII: - $params[$column - 1] = [ - &$variable, - SQLSRV_PARAM_IN, - SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), - ]; - break; - - default: - $params[$column - 1] =& $variable; - break; - } - } - - $stmt = sqlsrv_prepare($this->conn, $this->sql, $params); - - if (! $stmt) { - throw Error::new(); - } - - return $stmt; - } - - /** - * {@inheritdoc} - * - * @deprecated Use one of the fetch- or iterate-related methods. - */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) - { - $this->defaultFetchMode = $fetchMode; - $this->defaultFetchClass = $arg2 ?: $this->defaultFetchClass; - $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; - - return true; - } - - /** - * {@inheritdoc} - * - * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. - */ - #[ReturnTypeWillChange] - public function getIterator() - { - return new StatementIterator($this); - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. - * - * @throws SQLSrvException - */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) - { - // do not try fetching from the statement if it's not expected to contain result - // in order to prevent exceptional situation - if ($this->stmt === null || ! $this->result) { - return false; - } - - $args = func_get_args(); - $fetchMode = $fetchMode ?: $this->defaultFetchMode; - - if ($fetchMode === FetchMode::COLUMN) { - return $this->fetchColumn(); - } - - if (isset(self::$fetchMap[$fetchMode])) { - return sqlsrv_fetch_array($this->stmt, self::$fetchMap[$fetchMode]) ?: false; - } - - if (in_array($fetchMode, [FetchMode::STANDARD_OBJECT, FetchMode::CUSTOM_OBJECT], true)) { - $className = $this->defaultFetchClass; - $ctorArgs = $this->defaultFetchClassCtorArgs; - - if (count($args) >= 2) { - $className = $args[1]; - $ctorArgs = $args[2] ?? []; - } - - return sqlsrv_fetch_object($this->stmt, $className, $ctorArgs) ?: false; - } - - throw new SQLSrvException('Fetch mode is not supported!'); - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. - */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) - { - $rows = []; - - switch ($fetchMode) { - case FetchMode::CUSTOM_OBJECT: - while (($row = $this->fetch(...func_get_args())) !== false) { - $rows[] = $row; - } - - break; - - case FetchMode::COLUMN: - while (($row = $this->fetchColumn()) !== false) { - $rows[] = $row; - } - - break; - - default: - while (($row = $this->fetch($fetchMode)) !== false) { - $rows[] = $row; - } - } - - return $rows; - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchOne() instead. - */ - public function fetchColumn($columnIndex = 0) - { - $row = $this->fetch(FetchMode::NUMERIC); - - if ($row === false) { - return false; - } - - return $row[$columnIndex] ?? null; - } - - /** - * {@inheritdoc} - */ - public function fetchNumeric() - { - return $this->doFetch(SQLSRV_FETCH_NUMERIC); - } - - /** - * {@inheritdoc} - */ - public function fetchAssociative() - { - return $this->doFetch(SQLSRV_FETCH_ASSOC); - } - - /** - * {@inheritdoc} - */ - public function fetchOne() - { - return FetchUtils::fetchOne($this); - } - - /** - * {@inheritdoc} - */ - public function fetchAllNumeric(): array - { - return FetchUtils::fetchAllNumeric($this); - } - - /** - * {@inheritdoc} - */ - public function fetchAllAssociative(): array - { - return FetchUtils::fetchAllAssociative($this); - } - - /** - * {@inheritdoc} - */ - public function fetchFirstColumn(): array - { - return FetchUtils::fetchFirstColumn($this); - } - - /** - * {@inheritdoc} - */ - public function rowCount() - { - if ($this->stmt === null) { - return 0; - } - - return sqlsrv_rows_affected($this->stmt) ?: 0; - } - - public function free(): void - { - // not having the result means there's nothing to close - if ($this->stmt === null || ! $this->result) { - return; - } - - // emulate it by fetching and discarding rows, similarly to what PDO does in this case - // @link http://php.net/manual/en/pdostatement.closecursor.php - // @link https://github.com/php/php-src/blob/php-7.0.11/ext/pdo/pdo_stmt.c#L2075 - // deliberately do not consider multiple result sets, since doctrine/dbal doesn't support them - while (sqlsrv_fetch($this->stmt)) { - } - - $this->result = false; - } - - /** - * @return mixed|false - */ - private function doFetch(int $fetchType) - { - // do not try fetching from the statement if it's not expected to contain the result - // in order to prevent exceptional situation - if ($this->stmt === null || ! $this->result) { - return false; - } - - return sqlsrv_fetch_array($this->stmt, $fetchType) ?? false; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Statement.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Statement.php deleted file mode 100644 index 6a96b87c9..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Statement.php +++ /dev/null @@ -1,7 +0,0 @@ -bindValue(), - * the variable is bound as a reference and will only be evaluated at the time - * that PDOStatement->execute() is called. - * - * As mentioned above, the named parameters are not natively supported by the mysqli driver, use executeQuery(), - * fetchAll(), fetchArray(), fetchColumn(), fetchAssoc() methods to have the named parameter emulated by doctrine. - * - * Most parameters are input parameters, that is, parameters that are - * used in a read-only fashion to build up the query. Some drivers support the invocation - * of stored procedures that return data as output parameters, and some also as input/output - * parameters that both send in data and are updated to receive it. - * - * @param int|string $param Parameter identifier. For a prepared statement using named placeholders, - * this will be a parameter name of the form :name. For a prepared statement using - * question mark placeholders, this will be the 1-indexed position of the parameter. - * @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter. - * @param int $type Explicit data type for the parameter using the {@link ParameterType} - * constants. To return an INOUT parameter from a stored procedure, use the bitwise - * OR operator to set the PDO::PARAM_INPUT_OUTPUT bits for the data_type parameter. - * @param int|null $length You must specify maxlength when using an OUT bind - * so that PHP allocates enough memory to hold the returned value. - * - * @return bool TRUE on success or FALSE on failure. - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null); - - /** - * Fetches the SQLSTATE associated with the last operation on the statement handle. - * - * @deprecated The error information is available via exceptions. - * - * @see Doctrine_Adapter_Interface::errorCode() - * - * @return string|int|bool The error code string. - */ - public function errorCode(); - - /** - * Fetches extended error information associated with the last operation on the statement handle. - * - * @deprecated The error information is available via exceptions. - * - * @return mixed[] The error info array. - */ - public function errorInfo(); - - /** - * Executes a prepared statement - * - * If the prepared statement included parameter markers, you must either: - * call PDOStatement->bindParam() to bind PHP variables to the parameter markers: - * bound variables pass their value as input and receive the output value, - * if any, of their associated parameter markers or pass an array of input-only - * parameter values. - * - * @param mixed[]|null $params An array of values with as many elements as there are - * bound parameters in the SQL statement being executed. - * - * @return bool TRUE on success or FALSE on failure. - */ - public function execute($params = null); - - /** - * Returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement - * executed by the corresponding object. - * - * If the last SQL statement executed by the associated Statement object was a SELECT statement, - * some databases may return the number of rows returned by that statement. However, - * this behaviour is not guaranteed for all databases and should not be - * relied on for portable applications. - * - * @return int The number of rows. - */ - public function rowCount(); -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/StatementIterator.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/StatementIterator.php deleted file mode 100644 index d447ca696..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/StatementIterator.php +++ /dev/null @@ -1,31 +0,0 @@ -statement = $statement; - } - - /** - * {@inheritdoc} - */ - #[ReturnTypeWillChange] - public function getIterator() - { - while (($result = $this->statement->fetch()) !== false) { - yield $result; - } - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php deleted file mode 100644 index 35fadc40b..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php +++ /dev/null @@ -1,83 +0,0 @@ -connection = $connection; - } - - /** - * @return Connection - */ - public function getConnection() - { - return $this->connection; - } - - /** - * @deprecated Use ConnectionEventArgs::getConnection() and Connection::getDriver() instead. - * - * @return Driver - */ - public function getDriver() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'ConnectionEventArgs::getDriver() is deprecated, ' . - 'use ConnectionEventArgs::getConnection()->getDriver() instead.' - ); - - return $this->connection->getDriver(); - } - - /** - * @deprecated Use ConnectionEventArgs::getConnection() and Connection::getDatabasePlatform() instead. - * - * @return AbstractPlatform - */ - public function getDatabasePlatform() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'ConnectionEventArgs::getDatabasePlatform() is deprecated, ' . - 'use ConnectionEventArgs::getConnection()->getDatabasePlatform() instead.' - ); - - return $this->connection->getDatabasePlatform(); - } - - /** - * @deprecated Use ConnectionEventArgs::getConnection() and Connection::getSchemaManager() instead. - * - * @return AbstractSchemaManager - */ - public function getSchemaManager() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'ConnectionEventArgs::getSchemaManager() is deprecated, ' . - 'use ConnectionEventArgs::getConnection()->getSchemaManager() instead.' - ); - - return $this->connection->getSchemaManager(); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php deleted file mode 100644 index 8748dd174..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php +++ /dev/null @@ -1,58 +0,0 @@ -charset = $charset; - $this->collation = $collation; - } - - /** - * @return void - */ - public function postConnect(ConnectionEventArgs $args) - { - $collation = $this->collation ? ' COLLATE ' . $this->collation : ''; - $args->getConnection()->executeStatement('SET NAMES ' . $this->charset . $collation); - } - - /** - * {@inheritdoc} - */ - public function getSubscribedEvents() - { - return [Events::postConnect]; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php deleted file mode 100644 index 4264e4b97..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php +++ /dev/null @@ -1,116 +0,0 @@ -tableColumn = $tableColumn; - $this->table = $table; - $this->database = $database; - $this->connection = $connection; - } - - /** - * Allows to clear the column which means the column will be excluded from - * tables column list. - * - * @return SchemaColumnDefinitionEventArgs - */ - public function setColumn(?Column $column = null) - { - $this->column = $column; - - return $this; - } - - /** - * @return Column|null - */ - public function getColumn() - { - return $this->column; - } - - /** - * @return mixed[] - */ - public function getTableColumn() - { - return $this->tableColumn; - } - - /** - * @return string - */ - public function getTable() - { - return $this->table; - } - - /** - * @return string - */ - public function getDatabase() - { - return $this->database; - } - - /** - * @return Connection - */ - public function getConnection() - { - return $this->connection; - } - - /** - * @deprecated Use SchemaColumnDefinitionEventArgs::getConnection() and Connection::getDatabasePlatform() instead. - * - * @return AbstractPlatform - */ - public function getDatabasePlatform() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'SchemaColumnDefinitionEventArgs::getDatabasePlatform() is deprecated, ' . - 'use SchemaColumnDefinitionEventArgs::getConnection()->getDatabasePlatform() instead.' - ); - - return $this->connection->getDatabasePlatform(); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php deleted file mode 100644 index 868243c80..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php +++ /dev/null @@ -1,102 +0,0 @@ -tableIndex = $tableIndex; - $this->table = $table; - $this->connection = $connection; - } - - /** - * Allows to clear the index which means the index will be excluded from tables index list. - * - * @return SchemaIndexDefinitionEventArgs - */ - public function setIndex(?Index $index = null) - { - $this->index = $index; - - return $this; - } - - /** - * @return Index|null - */ - public function getIndex() - { - return $this->index; - } - - /** - * @return mixed[] - */ - public function getTableIndex() - { - return $this->tableIndex; - } - - /** - * @return string - */ - public function getTable() - { - return $this->table; - } - - /** - * @return Connection - */ - public function getConnection() - { - return $this->connection; - } - - /** - * @deprecated Use SchemaIndexDefinitionEventArgs::getConnection() and Connection::getDatabasePlatform() instead. - * - * @return AbstractPlatform - */ - public function getDatabasePlatform() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'SchemaIndexDefinitionEventArgs::getDatabasePlatform() is deprecated, ' . - 'use SchemaIndexDefinitionEventArgs::getConnection()->getDatabasePlatform() instead.' - ); - - return $this->connection->getDatabasePlatform(); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception.php deleted file mode 100644 index 34442da6a..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception.php +++ /dev/null @@ -1,10 +0,0 @@ -driverException = $driverException; - } - - /** - * Returns the driver specific error code if given. - * - * Returns null if no error code was given by the driver. - * - * @return int|string|null - */ - public function getErrorCode() - { - return $this->driverException->getErrorCode(); - } - - /** - * Returns the SQLSTATE the driver was in at the time the error occurred, if given. - * - * Returns null if no SQLSTATE was given by the driver. - * - * @return string|null - */ - public function getSQLState() - { - return $this->driverException->getSQLState(); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/FetchMode.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/FetchMode.php deleted file mode 100644 index bef473f5e..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/FetchMode.php +++ /dev/null @@ -1,75 +0,0 @@ -stmt = $stmt; - } - - /** - * @return Driver\ResultStatement - */ - #[ReturnTypeWillChange] - public function getIterator() - { - return $this->stmt; - } - - /** - * {@inheritDoc} - * - * @deprecated Use Result::free() instead. - */ - public function closeCursor() - { - return $this->stmt->closeCursor(); - } - - /** - * {@inheritDoc} - */ - public function columnCount() - { - return $this->stmt->columnCount(); - } - - /** - * {@inheritDoc} - * - * @deprecated Use one of the fetch- or iterate-related methods. - */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) - { - return $this->stmt->setFetchMode($fetchMode, $arg2, $arg3); - } - - /** - * {@inheritDoc} - * - * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. - */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Result::fetch() is deprecated, use Result::fetchNumeric(), fetchAssociative() or fetchOne() instead.' - ); - - return $this->stmt->fetch(...func_get_args()); - } - - /** - * {@inheritDoc} - * - * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. - */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Result::fetchAll() is deprecated, use Result::fetchAllNumeric(), fetchAllAssociative() or ' . - 'fetchFirstColumn() instead.' - ); - - return $this->stmt->fetchAll($fetchMode, $fetchArgument, $ctorArgs); - } - - /** - * {@inheritDoc} - * - * @deprecated Use fetchOne() instead. - */ - public function fetchColumn($columnIndex = 0) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Result::fetchColumn() is deprecated, use Result::fetchOne() instead.' - ); - - return $this->stmt->fetchColumn($columnIndex); - } - - /** - * {@inheritDoc} - */ - public function fetchNumeric() - { - return $this->stmt->fetch(PDO::FETCH_NUM); - } - - /** - * {@inheritDoc} - */ - public function fetchAssociative() - { - return $this->stmt->fetch(PDO::FETCH_ASSOC); - } - - /** - * {@inheritDoc} - */ - public function fetchOne() - { - $row = $this->fetchNumeric(); - - if ($row === false) { - return false; - } - - return $row[0]; - } - - /** - * {@inheritDoc} - */ - public function fetchAllNumeric(): array - { - $rows = []; - - while (($row = $this->fetchNumeric()) !== false) { - $rows[] = $row; - } - - return $rows; - } - - /** - * {@inheritDoc} - */ - public function fetchAllAssociative(): array - { - $rows = []; - - while (($row = $this->fetchAssociative()) !== false) { - $rows[] = $row; - } - - return $rows; - } - - /** - * {@inheritDoc} - */ - public function fetchAllKeyValue(): array - { - $this->ensureHasKeyValue(); - $data = []; - - foreach ($this->fetchAllNumeric() as [$key, $value]) { - $data[$key] = $value; - } - - return $data; - } - - /** - * {@inheritDoc} - */ - public function fetchAllAssociativeIndexed(): array - { - $data = []; - - foreach ($this->fetchAllAssociative() as $row) { - $data[array_shift($row)] = $row; - } - - return $data; - } - - /** - * {@inheritDoc} - */ - public function fetchFirstColumn(): array - { - $rows = []; - - while (($row = $this->fetchOne()) !== false) { - $rows[] = $row; - } - - return $rows; - } - - /** - * {@inheritdoc} - * - * @return Traversable> - */ - public function iterateNumeric(): Traversable - { - while (($row = $this->fetchNumeric()) !== false) { - yield $row; - } - } - - /** - * {@inheritDoc} - * - * @return Traversable> - */ - public function iterateAssociative(): Traversable - { - while (($row = $this->fetchAssociative()) !== false) { - yield $row; - } - } - - /** - * {@inheritDoc} - * - * @return Traversable - */ - public function iterateKeyValue(): Traversable - { - $this->ensureHasKeyValue(); - - foreach ($this->iterateNumeric() as [$key, $value]) { - yield $key => $value; - } - } - - /** - * {@inheritDoc} - * - * @return Traversable> - */ - public function iterateAssociativeIndexed(): Traversable - { - foreach ($this->iterateAssociative() as $row) { - yield array_shift($row) => $row; - } - } - - /** - * {@inheritDoc} - * - * @return Traversable - */ - public function iterateColumn(): Traversable - { - while (($value = $this->fetchOne()) !== false) { - yield $value; - } - } - - /** - * {@inheritDoc} - */ - public function rowCount() - { - if (method_exists($this->stmt, 'rowCount')) { - return $this->stmt->rowCount(); - } - - throw Exception::notSupported('rowCount'); - } - - public function free(): void - { - $this->closeCursor(); - } - - private function ensureHasKeyValue(): void - { - $columnCount = $this->columnCount(); - - if ($columnCount < 2) { - throw NoKeyValue::fromColumnCount($columnCount); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This feature will no longer be available on Result object in 3.0.x version. - */ - public function bindValue($param, $value, $type = ParameterType::STRING) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Result::bindValue() is deprecated, no replacement.' - ); - - if ($this->stmt instanceof Driver\Statement) { - return $this->stmt->bindValue($param, $value, $type); - } - - throw Exception::notSupported('bindValue'); - } - - /** - * {@inheritDoc} - * - * @deprecated This feature will no longer be available on Result object in 3.0.x version. - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Result::bindParam() is deprecated, no replacement.' - ); - - if ($this->stmt instanceof Driver\Statement) { - return $this->stmt->bindParam($param, $variable, $type, $length); - } - - throw Exception::notSupported('bindParam'); - } - - /** - * {@inheritDoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorCode() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Result::errorCode() is deprecated, the error information is available via exceptions.' - ); - - if ($this->stmt instanceof Driver\Statement) { - return $this->stmt->errorCode(); - } - - throw Exception::notSupported('errorCode'); - } - - /** - * {@inheritDoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorInfo() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Result::errorInfo() is deprecated, the error information is available via exceptions.' - ); - - if ($this->stmt instanceof Driver\Statement) { - return $this->stmt->errorInfo(); - } - - throw Exception::notSupported('errorInfo'); - } - - /** - * {@inheritDoc} - * - * @deprecated This feature will no longer be available on Result object in 3.0.x version. - */ - public function execute($params = null) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Result::execute() is deprecated, no replacement.' - ); - - if ($this->stmt instanceof Driver\Statement) { - return $this->stmt->execute($params); - } - - throw Exception::notSupported('execute'); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php deleted file mode 100644 index 1acd4e3b7..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php +++ /dev/null @@ -1,51 +0,0 @@ -loggers = $loggers; - } - - /** - * Adds a logger in the chain. - * - * @deprecated Inject list of loggers via constructor instead - * - * @return void - */ - public function addLogger(SQLLogger $logger) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3572', - 'LoggerChain::addLogger() is deprecated, use LoggerChain constructor instead.' - ); - - $this->loggers[] = $logger; - } - - /** - * {@inheritdoc} - */ - public function startQuery($sql, ?array $params = null, ?array $types = null) - { - foreach ($this->loggers as $logger) { - $logger->startQuery($sql, $params, $types); - } - } - - /** - * {@inheritdoc} - */ - public function stopQuery() - { - foreach ($this->loggers as $logger) { - $logger->stopQuery(); - } - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/ParameterType.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/ParameterType.php deleted file mode 100644 index 2c4c3ad18..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/ParameterType.php +++ /dev/null @@ -1,65 +0,0 @@ -_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - protected function _getCommonIntegerTypeDeclarationSQL(array $column) - { - $autoinc = ''; - if (! empty($column['autoincrement'])) { - $autoinc = ' AUTO_INCREMENT'; - } - - return $autoinc; - } - - /** - * {@inheritDoc} - */ - public function getBigIntTypeDeclarationSQL(array $column) - { - return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getSmallIntTypeDeclarationSQL(array $column) - { - return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) - { - return $length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'; - } - - /** - * {@inheritdoc} - */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) - { - return 'VARBINARY(' . ($length ?: 255) . ')'; - } - - /** - * {@inheritDoc} - */ - protected function initializeDoctrineTypeMappings() - { - $this->doctrineTypeMapping = [ - 'boolean' => 'boolean', - 'varchar' => 'string', - 'varbinary' => 'binary', - 'integer' => 'integer', - 'blob' => 'blob', - 'decimal' => 'decimal', - 'datetime' => 'datetime', - 'date' => 'date', - 'time' => 'time', - 'text' => 'text', - 'timestamp' => 'datetime', - 'double' => 'float', - 'bigint' => 'bigint', - ]; - } - - /** - * {@inheritDoc} - */ - public function getClobTypeDeclarationSQL(array $column) - { - return 'TEXT'; - } - - /** - * {@inheritDoc} - */ - public function getBlobTypeDeclarationSQL(array $column) - { - return 'BLOB'; - } - - /** - * {@inheritDoc} - */ - public function getCreateDatabaseSQL($name) - { - return 'CREATE DATABASE ' . $name; - } - - /** - * {@inheritDoc} - */ - public function getDropDatabaseSQL($name) - { - return 'DROP DATABASE ' . $name; - } - - /** - * {@inheritDoc} - */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) - { - $queryFields = $this->getColumnDeclarationListSQL($columns); - - if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { - foreach ($options['uniqueConstraints'] as $index => $definition) { - $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition); - } - } - - // add all indexes - if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach ($options['indexes'] as $index => $definition) { - $queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition); - } - } - - // attach all primary keys - if (isset($options['primary']) && ! empty($options['primary'])) { - $keyColumns = array_unique(array_values($options['primary'])); - $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; - } - - $query = 'CREATE '; - - if (! empty($options['temporary'])) { - $query .= 'TEMPORARY '; - } - - $query .= 'TABLE ' . $name . ' (' . $queryFields . ') '; - $query .= $this->buildTableOptions($options); - $query .= $this->buildPartitionOptions($options); - - $sql = [$query]; - - if (isset($options['foreignKeys'])) { - foreach ((array) $options['foreignKeys'] as $definition) { - $sql[] = $this->getCreateForeignKeySQL($definition, $name); - } - } - - return $sql; - } - - /** - * Build SQL for table options - * - * @param mixed[] $options - * - * @return string - */ - private function buildTableOptions(array $options) - { - if (isset($options['table_options'])) { - return $options['table_options']; - } - - $tableOptions = []; - - // Collate - if (! isset($options['collate'])) { - $options['collate'] = 'utf8_unicode_ci'; - } - - $tableOptions[] = sprintf('COLLATE %s', $options['collate']); - - // Engine - if (! isset($options['engine'])) { - $options['engine'] = 'InnoDB'; - } - - $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); - - // Auto increment - if (isset($options['auto_increment'])) { - $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); - } - - // Comment - if (isset($options['comment'])) { - $comment = trim($options['comment'], " '"); - - $tableOptions[] = sprintf('COMMENT = %s ', $this->quoteStringLiteral($comment)); - } - - // Row format - if (isset($options['row_format'])) { - $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']); - } - - return implode(' ', $tableOptions); - } - - /** - * Build SQL for partition options. - * - * @param mixed[] $options - * - * @return string - */ - private function buildPartitionOptions(array $options) - { - return isset($options['partition_options']) - ? ' ' . $options['partition_options'] - : ''; - } - - /** - * {@inheritDoc} - */ - public function getListDatabasesSQL() - { - return "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE CATALOG_NAME='LOCAL'"; - } - - /** - * {@inheritDoc} - */ - protected function getReservedKeywordsClass() - { - return Keywords\DrizzleKeywords::class; - } - - /** - * {@inheritDoc} - */ - public function getListTablesSQL() - { - return "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE' AND TABLE_SCHEMA=DATABASE()"; - } - - /** - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - if ($database) { - $databaseSQL = $this->quoteStringLiteral($database); - } else { - $databaseSQL = 'DATABASE()'; - } - - return 'SELECT COLUMN_NAME, DATA_TYPE, COLUMN_COMMENT, IS_NULLABLE, IS_AUTO_INCREMENT,' . - ' CHARACTER_MAXIMUM_LENGTH, COLUMN_DEFAULT, NUMERIC_PRECISION, NUMERIC_SCALE, COLLATION_NAME' . - ' FROM DATA_DICTIONARY.COLUMNS' . - ' WHERE TABLE_SCHEMA=' . $databaseSQL . ' AND TABLE_NAME = ' . $this->quoteStringLiteral($table); - } - - /** - * @param string $table - * @param string|null $database - * - * @return string - */ - public function getListTableForeignKeysSQL($table, $database = null) - { - if ($database) { - $databaseSQL = $this->quoteStringLiteral($database); - } else { - $databaseSQL = 'DATABASE()'; - } - - return 'SELECT CONSTRAINT_NAME, CONSTRAINT_COLUMNS, REFERENCED_TABLE_NAME, REFERENCED_TABLE_COLUMNS,' - . ' UPDATE_RULE, DELETE_RULE' - . ' FROM DATA_DICTIONARY.FOREIGN_KEYS' - . ' WHERE CONSTRAINT_SCHEMA=' . $databaseSQL - . ' AND CONSTRAINT_TABLE=' . $this->quoteStringLiteral($table); - } - - /** - * {@inheritDoc} - */ - public function getListTableIndexesSQL($table, $database = null) - { - if ($database) { - $databaseSQL = $this->quoteStringLiteral($database); - } else { - $databaseSQL = 'DATABASE()'; - } - - return "SELECT INDEX_NAME AS 'key_name'," - . " COLUMN_NAME AS 'column_name'," - . " IS_USED_IN_PRIMARY AS 'primary'," - . " IS_UNIQUE=0 AS 'non_unique'" - . ' FROM DATA_DICTIONARY.INDEX_PARTS' - . ' WHERE TABLE_SCHEMA=' . $databaseSQL . ' AND TABLE_NAME=' . $this->quoteStringLiteral($table); - } - - /** - * {@inheritDoc} - */ - public function prefersIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function supportsIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function supportsInlineColumnComments() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function supportsViews() - { - return false; - } - - /** - * {@inheritdoc} - */ - public function supportsColumnCollation() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function getDropIndexSQL($index, $table = null) - { - if ($index instanceof Index) { - $indexName = $index->getQuotedName($this); - } elseif (is_string($index)) { - $indexName = $index; - } else { - throw new InvalidArgumentException( - __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.' - ); - } - - if ($table instanceof Table) { - $table = $table->getQuotedName($this); - } elseif (! is_string($table)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.' - ); - } - - if ($index instanceof Index && $index->isPrimary()) { - // drizzle primary keys are always named "PRIMARY", - // so we cannot use them in statements because of them being keyword. - return $this->getDropPrimaryKeySQL($table); - } - - return 'DROP INDEX ' . $indexName . ' ON ' . $table; - } - - /** - * @param string $table - * - * @return string - */ - protected function getDropPrimaryKeySQL($table) - { - return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTypeDeclarationSQL(array $column) - { - if (isset($column['version']) && $column['version'] === true) { - return 'TIMESTAMP'; - } - - return 'DATETIME'; - } - - /** - * {@inheritDoc} - */ - public function getTimeTypeDeclarationSQL(array $column) - { - return 'TIME'; - } - - /** - * {@inheritDoc} - */ - public function getDateTypeDeclarationSQL(array $column) - { - return 'DATE'; - } - - /** - * {@inheritDoc} - */ - public function getAlterTableSQL(TableDiff $diff) - { - $columnSql = []; - $queryParts = []; - - $newName = $diff->getNewName(); - - if ($newName !== false) { - $queryParts[] = 'RENAME TO ' . $newName->getQuotedName($this); - } - - foreach ($diff->addedColumns as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { - continue; - } - - $columnArray = array_merge($column->toArray(), [ - 'comment' => $this->getColumnComment($column), - ]); - - $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); - } - - foreach ($diff->removedColumns as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { - continue; - } - - $queryParts[] = 'DROP ' . $column->getQuotedName($this); - } - - foreach ($diff->changedColumns as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - - $column = $columnDiff->column; - $columnArray = $column->toArray(); - - // Do not generate column alteration clause if type is binary and only fixed property has changed. - // Drizzle only supports binary type columns with variable length. - // Avoids unnecessary table alteration statements. - if ( - $columnArray['type'] instanceof BinaryType && - $columnDiff->hasChanged('fixed') && - count($columnDiff->changedProperties) === 1 - ) { - continue; - } - - $columnArray['comment'] = $this->getColumnComment($column); - $queryParts[] = 'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' ' - . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); - } - - foreach ($diff->renamedColumns as $oldColumnName => $column) { - if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { - continue; - } - - $oldColumnName = new Identifier($oldColumnName); - - $columnArray = $column->toArray(); - $columnArray['comment'] = $this->getColumnComment($column); - $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' - . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); - } - - $sql = []; - $tableSql = []; - - if (! $this->onSchemaAlterTable($diff, $tableSql)) { - if (count($queryParts) > 0) { - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) - . ' ' . implode(', ', $queryParts); - } - - $sql = array_merge( - $this->getPreAlterTableIndexForeignKeySQL($diff), - $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff) - ); - } - - return array_merge($sql, $tableSql, $columnSql); - } - - /** - * {@inheritDoc} - */ - public function getDropTemporaryTableSQL($table) - { - if ($table instanceof Table) { - $table = $table->getQuotedName($this); - } elseif (! is_string($table)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.' - ); - } - - return 'DROP TEMPORARY TABLE ' . $table; - } - - /** - * {@inheritDoc} - */ - public function convertBooleans($item) - { - if (is_array($item)) { - foreach ($item as $key => $value) { - if (! is_bool($value) && ! is_numeric($value)) { - continue; - } - - $item[$key] = $value ? 'true' : 'false'; - } - } elseif (is_bool($item) || is_numeric($item)) { - $item = $item ? 'true' : 'false'; - } - - return $item; - } - - /** - * {@inheritDoc} - */ - public function getLocateExpression($str, $substr, $startPos = false) - { - if ($startPos === false) { - return 'LOCATE(' . $substr . ', ' . $str . ')'; - } - - return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')'; - } - - /** - * {@inheritDoc} - * - * @deprecated Use application-generated UUIDs instead - */ - public function getGuidExpression() - { - return 'UUID()'; - } - - /** - * {@inheritDoc} - */ - public function getRegexpExpression() - { - return 'RLIKE'; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php deleted file mode 100644 index 70f69f44b..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php +++ /dev/null @@ -1,326 +0,0 @@ -doctrineTypeMapping['json'] = Types::JSON; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySQL57Platform.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySQL57Platform.php deleted file mode 100644 index 5ef44b2e3..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySQL57Platform.php +++ /dev/null @@ -1,71 +0,0 @@ -getQuotedName($this)]; - } - - /** - * {@inheritdoc} - */ - protected function getReservedKeywordsClass() - { - return Keywords\MySQL57Keywords::class; - } - - /** - * {@inheritdoc} - */ - protected function initializeDoctrineTypeMappings() - { - parent::initializeDoctrineTypeMappings(); - - $this->doctrineTypeMapping['json'] = Types::JSON; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySQL80Platform.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySQL80Platform.php deleted file mode 100644 index f6d4be9dc..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySQL80Platform.php +++ /dev/null @@ -1,17 +0,0 @@ - 0) { - $query .= ' OFFSET ' . $offset; - } - } elseif ($offset > 0) { - // 2^64-1 is the maximum of unsigned BIGINT, the biggest limit possible - $query .= ' LIMIT 18446744073709551615 OFFSET ' . $offset; - } - - return $query; - } - - /** - * {@inheritDoc} - */ - public function getIdentifierQuoteCharacter() - { - return '`'; - } - - /** - * {@inheritDoc} - */ - public function getRegexpExpression() - { - return 'RLIKE'; - } - - /** - * {@inheritDoc} - * - * @deprecated Use application-generated UUIDs instead - */ - public function getGuidExpression() - { - return 'UUID()'; - } - - /** - * {@inheritDoc} - */ - public function getLocateExpression($str, $substr, $startPos = false) - { - if ($startPos === false) { - return 'LOCATE(' . $substr . ', ' . $str . ')'; - } - - return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')'; - } - - /** - * {@inheritDoc} - */ - public function getConcatExpression() - { - return sprintf('CONCAT(%s)', implode(', ', func_get_args())); - } - - /** - * {@inheritdoc} - */ - protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) - { - $function = $operator === '+' ? 'DATE_ADD' : 'DATE_SUB'; - - return $function . '(' . $date . ', INTERVAL ' . $interval . ' ' . $unit . ')'; - } - - /** - * {@inheritDoc} - */ - public function getDateDiffExpression($date1, $date2) - { - return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; - } - - /** - * {@inheritDoc} - */ - public function getListDatabasesSQL() - { - return 'SHOW DATABASES'; - } - - /** - * {@inheritDoc} - */ - public function getListTableConstraintsSQL($table) - { - return 'SHOW INDEX FROM ' . $table; - } - - /** - * {@inheritDoc} - * - * Two approaches to listing the table indexes. The information_schema is - * preferred, because it doesn't cause problems with SQL keywords such as "order" or "table". - */ - public function getListTableIndexesSQL($table, $database = null) - { - if ($database) { - $database = $this->quoteStringLiteral($database); - $table = $this->quoteStringLiteral($table); - - return 'SELECT NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, COLUMN_NAME AS Column_Name,' . - ' SUB_PART AS Sub_Part, INDEX_TYPE AS Index_Type' . - ' FROM information_schema.STATISTICS WHERE TABLE_NAME = ' . $table . - ' AND TABLE_SCHEMA = ' . $database . - ' ORDER BY SEQ_IN_INDEX ASC'; - } - - return 'SHOW INDEX FROM ' . $table; - } - - /** - * {@inheritDoc} - */ - public function getListViewsSQL($database) - { - $database = $this->quoteStringLiteral($database); - - return 'SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = ' . $database; - } - - /** - * @param string $table - * @param string|null $database - * - * @return string - */ - public function getListTableForeignKeysSQL($table, $database = null) - { - $table = $this->quoteStringLiteral($table); - - if ($database !== null) { - $database = $this->quoteStringLiteral($database); - } - - $sql = 'SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, ' . - 'k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ ' . - 'FROM information_schema.key_column_usage k /*!50116 ' . - 'INNER JOIN information_schema.referential_constraints c ON ' . - ' c.constraint_name = k.constraint_name AND ' . - ' c.table_name = ' . $table . ' */ WHERE k.table_name = ' . $table; - - $databaseNameSql = $database ?? 'DATABASE()'; - - return $sql . ' AND k.table_schema = ' . $databaseNameSql - . ' /*!50116 AND c.constraint_schema = ' . $databaseNameSql . ' */' - . ' AND k.`REFERENCED_COLUMN_NAME` is not NULL'; - } - - /** - * {@inheritDoc} - */ - public function getCreateViewSQL($name, $sql) - { - return 'CREATE VIEW ' . $name . ' AS ' . $sql; - } - - /** - * {@inheritDoc} - */ - public function getDropViewSQL($name) - { - return 'DROP VIEW ' . $name; - } - - /** - * {@inheritDoc} - */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) - { - return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') - : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); - } - - /** - * {@inheritdoc} - */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) - { - return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')'; - } - - /** - * Gets the SQL snippet used to declare a CLOB column type. - * TINYTEXT : 2 ^ 8 - 1 = 255 - * TEXT : 2 ^ 16 - 1 = 65535 - * MEDIUMTEXT : 2 ^ 24 - 1 = 16777215 - * LONGTEXT : 2 ^ 32 - 1 = 4294967295 - * - * {@inheritDoc} - */ - public function getClobTypeDeclarationSQL(array $column) - { - if (! empty($column['length']) && is_numeric($column['length'])) { - $length = $column['length']; - - if ($length <= static::LENGTH_LIMIT_TINYTEXT) { - return 'TINYTEXT'; - } - - if ($length <= static::LENGTH_LIMIT_TEXT) { - return 'TEXT'; - } - - if ($length <= static::LENGTH_LIMIT_MEDIUMTEXT) { - return 'MEDIUMTEXT'; - } - } - - return 'LONGTEXT'; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTypeDeclarationSQL(array $column) - { - if (isset($column['version']) && $column['version'] === true) { - return 'TIMESTAMP'; - } - - return 'DATETIME'; - } - - /** - * {@inheritDoc} - */ - public function getDateTypeDeclarationSQL(array $column) - { - return 'DATE'; - } - - /** - * {@inheritDoc} - */ - public function getTimeTypeDeclarationSQL(array $column) - { - return 'TIME'; - } - - /** - * {@inheritDoc} - */ - public function getBooleanTypeDeclarationSQL(array $column) - { - return 'TINYINT(1)'; - } - - /** - * Obtain DBMS specific SQL code portion needed to set the COLLATION - * of a column declaration to be used in statements like CREATE TABLE. - * - * @deprecated Deprecated since version 2.5, Use {@link self::getColumnCollationDeclarationSQL()} instead. - * - * @param string $collation name of the collation - * - * @return string DBMS specific SQL code portion needed to set the COLLATION - * of a column declaration. - */ - public function getCollationFieldDeclaration($collation) - { - return $this->getColumnCollationDeclarationSQL($collation); - } - - /** - * {@inheritDoc} - * - * MySql prefers "autoincrement" identity columns since sequences can only - * be emulated with a table. - */ - public function prefersIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - * - * MySql supports this through AUTO_INCREMENT columns. - */ - public function supportsIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function supportsInlineColumnComments() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function supportsColumnCollation() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function getListTablesSQL() - { - return "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"; - } - - /** - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - $table = $this->quoteStringLiteral($table); - - if ($database) { - $database = $this->quoteStringLiteral($database); - } else { - $database = 'DATABASE()'; - } - - return 'SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, ' . - 'COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, ' . - 'CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation ' . - 'FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ' . $database . ' AND TABLE_NAME = ' . $table . - ' ORDER BY ORDINAL_POSITION ASC'; - } - - public function getListTableMetadataSQL(string $table, ?string $database = null): string - { - return sprintf( - <<<'SQL' -SELECT ENGINE, AUTO_INCREMENT, TABLE_COLLATION, TABLE_COMMENT, CREATE_OPTIONS -FROM information_schema.TABLES -WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = %s AND TABLE_NAME = %s -SQL - , - $database ? $this->quoteStringLiteral($database) : 'DATABASE()', - $this->quoteStringLiteral($table) - ); - } - - /** - * {@inheritDoc} - */ - public function getCreateDatabaseSQL($name) - { - return 'CREATE DATABASE ' . $name; - } - - /** - * {@inheritDoc} - */ - public function getDropDatabaseSQL($name) - { - return 'DROP DATABASE ' . $name; - } - - /** - * {@inheritDoc} - */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) - { - $queryFields = $this->getColumnDeclarationListSQL($columns); - - if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { - foreach ($options['uniqueConstraints'] as $index => $definition) { - $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition); - } - } - - // add all indexes - if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach ($options['indexes'] as $index => $definition) { - $queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition); - } - } - - // attach all primary keys - if (isset($options['primary']) && ! empty($options['primary'])) { - $keyColumns = array_unique(array_values($options['primary'])); - $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; - } - - $query = 'CREATE '; - - if (! empty($options['temporary'])) { - $query .= 'TEMPORARY '; - } - - $query .= 'TABLE ' . $name . ' (' . $queryFields . ') '; - $query .= $this->buildTableOptions($options); - $query .= $this->buildPartitionOptions($options); - - $sql = [$query]; - $engine = 'INNODB'; - - if (isset($options['engine'])) { - $engine = strtoupper(trim($options['engine'])); - } - - // Propagate foreign key constraints only for InnoDB. - if (isset($options['foreignKeys']) && $engine === 'INNODB') { - foreach ((array) $options['foreignKeys'] as $definition) { - $sql[] = $this->getCreateForeignKeySQL($definition, $name); - } - } - - return $sql; - } - - /** - * {@inheritdoc} - */ - public function getDefaultValueDeclarationSQL($column) - { - // Unset the default value if the given column definition does not allow default values. - if ($column['type'] instanceof TextType || $column['type'] instanceof BlobType) { - $column['default'] = null; - } - - return parent::getDefaultValueDeclarationSQL($column); - } - - /** - * Build SQL for table options - * - * @param mixed[] $options - * - * @return string - */ - private function buildTableOptions(array $options) - { - if (isset($options['table_options'])) { - return $options['table_options']; - } - - $tableOptions = []; - - // Charset - if (! isset($options['charset'])) { - $options['charset'] = 'utf8'; - } - - $tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']); - - // Collate - if (! isset($options['collate'])) { - $options['collate'] = $options['charset'] . '_unicode_ci'; - } - - $tableOptions[] = $this->getColumnCollationDeclarationSQL($options['collate']); - - // Engine - if (! isset($options['engine'])) { - $options['engine'] = 'InnoDB'; - } - - $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); - - // Auto increment - if (isset($options['auto_increment'])) { - $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); - } - - // Comment - if (isset($options['comment'])) { - $tableOptions[] = sprintf('COMMENT = %s ', $this->quoteStringLiteral($options['comment'])); - } - - // Row format - if (isset($options['row_format'])) { - $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']); - } - - return implode(' ', $tableOptions); - } - - /** - * Build SQL for partition options. - * - * @param mixed[] $options - * - * @return string - */ - private function buildPartitionOptions(array $options) - { - return isset($options['partition_options']) - ? ' ' . $options['partition_options'] - : ''; - } - - /** - * {@inheritDoc} - */ - public function getAlterTableSQL(TableDiff $diff) - { - $columnSql = []; - $queryParts = []; - $newName = $diff->getNewName(); - - if ($newName !== false) { - $queryParts[] = 'RENAME TO ' . $newName->getQuotedName($this); - } - - foreach ($diff->addedColumns as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { - continue; - } - - $columnArray = array_merge($column->toArray(), [ - 'comment' => $this->getColumnComment($column), - ]); - - $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); - } - - foreach ($diff->removedColumns as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { - continue; - } - - $queryParts[] = 'DROP ' . $column->getQuotedName($this); - } - - foreach ($diff->changedColumns as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - - $column = $columnDiff->column; - $columnArray = $column->toArray(); - - // Don't propagate default value changes for unsupported column types. - if ( - $columnDiff->hasChanged('default') && - count($columnDiff->changedProperties) === 1 && - ($columnArray['type'] instanceof TextType || $columnArray['type'] instanceof BlobType) - ) { - continue; - } - - $columnArray['comment'] = $this->getColumnComment($column); - $queryParts[] = 'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' ' - . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); - } - - foreach ($diff->renamedColumns as $oldColumnName => $column) { - if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { - continue; - } - - $oldColumnName = new Identifier($oldColumnName); - $columnArray = $column->toArray(); - $columnArray['comment'] = $this->getColumnComment($column); - $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' - . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); - } - - if (isset($diff->addedIndexes['primary'])) { - $keyColumns = array_unique(array_values($diff->addedIndexes['primary']->getColumns())); - $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; - unset($diff->addedIndexes['primary']); - } elseif (isset($diff->changedIndexes['primary'])) { - // Necessary in case the new primary key includes a new auto_increment column - foreach ($diff->changedIndexes['primary']->getColumns() as $columnName) { - if (isset($diff->addedColumns[$columnName]) && $diff->addedColumns[$columnName]->getAutoincrement()) { - $keyColumns = array_unique(array_values($diff->changedIndexes['primary']->getColumns())); - $queryParts[] = 'DROP PRIMARY KEY'; - $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; - unset($diff->changedIndexes['primary']); - break; - } - } - } - - $sql = []; - $tableSql = []; - - if (! $this->onSchemaAlterTable($diff, $tableSql)) { - if (count($queryParts) > 0) { - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' - . implode(', ', $queryParts); - } - - $sql = array_merge( - $this->getPreAlterTableIndexForeignKeySQL($diff), - $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff) - ); - } - - return array_merge($sql, $tableSql, $columnSql); - } - - /** - * {@inheritDoc} - */ - protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) - { - $sql = []; - $table = $diff->getName($this)->getQuotedName($this); - - foreach ($diff->changedIndexes as $changedIndex) { - $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $changedIndex)); - } - - foreach ($diff->removedIndexes as $remKey => $remIndex) { - $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $remIndex)); - - foreach ($diff->addedIndexes as $addKey => $addIndex) { - if ($remIndex->getColumns() !== $addIndex->getColumns()) { - continue; - } - - $indexClause = 'INDEX ' . $addIndex->getName(); - - if ($addIndex->isPrimary()) { - $indexClause = 'PRIMARY KEY'; - } elseif ($addIndex->isUnique()) { - $indexClause = 'UNIQUE INDEX ' . $addIndex->getName(); - } - - $query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', '; - $query .= 'ADD ' . $indexClause; - $query .= ' (' . $this->getIndexFieldDeclarationListSQL($addIndex) . ')'; - - $sql[] = $query; - - unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]); - - break; - } - } - - $engine = 'INNODB'; - - if ($diff->fromTable instanceof Table && $diff->fromTable->hasOption('engine')) { - $engine = strtoupper(trim($diff->fromTable->getOption('engine'))); - } - - // Suppress foreign key constraint propagation on non-supporting engines. - if ($engine !== 'INNODB') { - $diff->addedForeignKeys = []; - $diff->changedForeignKeys = []; - $diff->removedForeignKeys = []; - } - - $sql = array_merge( - $sql, - $this->getPreAlterTableAlterIndexForeignKeySQL($diff), - parent::getPreAlterTableIndexForeignKeySQL($diff), - $this->getPreAlterTableRenameIndexForeignKeySQL($diff) - ); - - return $sql; - } - - /** - * @return string[] - */ - private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $index) - { - $sql = []; - - if (! $index->isPrimary() || ! $diff->fromTable instanceof Table) { - return $sql; - } - - $tableName = $diff->getName($this)->getQuotedName($this); - - // Dropping primary keys requires to unset autoincrement attribute on the particular column first. - foreach ($index->getColumns() as $columnName) { - if (! $diff->fromTable->hasColumn($columnName)) { - continue; - } - - $column = $diff->fromTable->getColumn($columnName); - - if ($column->getAutoincrement() !== true) { - continue; - } - - $column->setAutoincrement(false); - - $sql[] = 'ALTER TABLE ' . $tableName . ' MODIFY ' . - $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); - - // original autoincrement information might be needed later on by other parts of the table alteration - $column->setAutoincrement(true); - } - - return $sql; - } - - /** - * @param TableDiff $diff The table diff to gather the SQL for. - * - * @return string[] - */ - private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff) - { - $sql = []; - $table = $diff->getName($this)->getQuotedName($this); - - foreach ($diff->changedIndexes as $changedIndex) { - // Changed primary key - if (! $changedIndex->isPrimary() || ! ($diff->fromTable instanceof Table)) { - continue; - } - - foreach ($diff->fromTable->getPrimaryKeyColumns() as $columnName) { - $column = $diff->fromTable->getColumn($columnName); - - // Check if an autoincrement column was dropped from the primary key. - if (! $column->getAutoincrement() || in_array($columnName, $changedIndex->getColumns())) { - continue; - } - - // The autoincrement attribute needs to be removed from the dropped column - // before we can drop and recreate the primary key. - $column->setAutoincrement(false); - - $sql[] = 'ALTER TABLE ' . $table . ' MODIFY ' . - $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); - - // Restore the autoincrement attribute as it might be needed later on - // by other parts of the table alteration. - $column->setAutoincrement(true); - } - } - - return $sql; - } - - /** - * @param TableDiff $diff The table diff to gather the SQL for. - * - * @return string[] - */ - protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) - { - $sql = []; - $tableName = $diff->getName($this)->getQuotedName($this); - - foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { - if (in_array($foreignKey, $diff->changedForeignKeys, true)) { - continue; - } - - $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); - } - - return $sql; - } - - /** - * Returns the remaining foreign key constraints that require one of the renamed indexes. - * - * "Remaining" here refers to the diff between the foreign keys currently defined in the associated - * table and the foreign keys to be removed. - * - * @param TableDiff $diff The table diff to evaluate. - * - * @return ForeignKeyConstraint[] - */ - private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff) - { - if (empty($diff->renamedIndexes) || ! $diff->fromTable instanceof Table) { - return []; - } - - $foreignKeys = []; - /** @var ForeignKeyConstraint[] $remainingForeignKeys */ - $remainingForeignKeys = array_diff_key( - $diff->fromTable->getForeignKeys(), - $diff->removedForeignKeys - ); - - foreach ($remainingForeignKeys as $foreignKey) { - foreach ($diff->renamedIndexes as $index) { - if ($foreignKey->intersectsIndexColumns($index)) { - $foreignKeys[] = $foreignKey; - - break; - } - } - } - - return $foreignKeys; - } - - /** - * {@inheritdoc} - */ - protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) - { - return array_merge( - parent::getPostAlterTableIndexForeignKeySQL($diff), - $this->getPostAlterTableRenameIndexForeignKeySQL($diff) - ); - } - - /** - * @param TableDiff $diff The table diff to gather the SQL for. - * - * @return string[] - */ - protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) - { - $sql = []; - $newName = $diff->getNewName(); - - if ($newName !== false) { - $tableName = $newName->getQuotedName($this); - } else { - $tableName = $diff->getName($this)->getQuotedName($this); - } - - foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { - if (in_array($foreignKey, $diff->changedForeignKeys, true)) { - continue; - } - - $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); - } - - return $sql; - } - - /** - * {@inheritDoc} - */ - protected function getCreateIndexSQLFlags(Index $index) - { - $type = ''; - if ($index->isUnique()) { - $type .= 'UNIQUE '; - } elseif ($index->hasFlag('fulltext')) { - $type .= 'FULLTEXT '; - } elseif ($index->hasFlag('spatial')) { - $type .= 'SPATIAL '; - } - - return $type; - } - - /** - * {@inheritDoc} - */ - public function getIntegerTypeDeclarationSQL(array $column) - { - return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getBigIntTypeDeclarationSQL(array $column) - { - return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getSmallIntTypeDeclarationSQL(array $column) - { - return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritdoc} - */ - public function getFloatDeclarationSQL(array $column) - { - return 'DOUBLE PRECISION' . $this->getUnsignedDeclaration($column); - } - - /** - * {@inheritdoc} - */ - public function getDecimalTypeDeclarationSQL(array $column) - { - return parent::getDecimalTypeDeclarationSQL($column) . $this->getUnsignedDeclaration($column); - } - - /** - * Get unsigned declaration for a column. - * - * @param mixed[] $columnDef - * - * @return string - */ - private function getUnsignedDeclaration(array $columnDef) - { - return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : ''; - } - - /** - * {@inheritDoc} - */ - protected function _getCommonIntegerTypeDeclarationSQL(array $column) - { - $autoinc = ''; - if (! empty($column['autoincrement'])) { - $autoinc = ' AUTO_INCREMENT'; - } - - return $this->getUnsignedDeclaration($column) . $autoinc; - } - - /** - * {@inheritDoc} - */ - public function getColumnCharsetDeclarationSQL($charset) - { - return 'CHARACTER SET ' . $charset; - } - - /** - * {@inheritDoc} - */ - public function getColumnCollationDeclarationSQL($collation) - { - return 'COLLATE ' . $this->quoteSingleIdentifier($collation); - } - - /** - * {@inheritDoc} - */ - public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) - { - $query = ''; - if ($foreignKey->hasOption('match')) { - $query .= ' MATCH ' . $foreignKey->getOption('match'); - } - - $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); - - return $query; - } - - /** - * {@inheritDoc} - */ - public function getDropIndexSQL($index, $table = null) - { - if ($index instanceof Index) { - $indexName = $index->getQuotedName($this); - } elseif (is_string($index)) { - $indexName = $index; - } else { - throw new InvalidArgumentException( - __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.' - ); - } - - if ($table instanceof Table) { - $table = $table->getQuotedName($this); - } elseif (! is_string($table)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.' - ); - } - - if ($index instanceof Index && $index->isPrimary()) { - // mysql primary keys are always named "PRIMARY", - // so we cannot use them in statements because of them being keyword. - return $this->getDropPrimaryKeySQL($table); - } - - return 'DROP INDEX ' . $indexName . ' ON ' . $table; - } - - /** - * @param string $table - * - * @return string - */ - protected function getDropPrimaryKeySQL($table) - { - return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; - } - - /** - * {@inheritDoc} - */ - public function getSetTransactionIsolationSQL($level) - { - return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return 'mysql'; - } - - /** - * {@inheritDoc} - */ - public function getReadLockSQL() - { - return 'LOCK IN SHARE MODE'; - } - - /** - * {@inheritDoc} - */ - protected function initializeDoctrineTypeMappings() - { - $this->doctrineTypeMapping = [ - 'tinyint' => 'boolean', - 'smallint' => 'smallint', - 'mediumint' => 'integer', - 'int' => 'integer', - 'integer' => 'integer', - 'bigint' => 'bigint', - 'tinytext' => 'text', - 'mediumtext' => 'text', - 'longtext' => 'text', - 'text' => 'text', - 'varchar' => 'string', - 'string' => 'string', - 'char' => 'string', - 'date' => 'date', - 'datetime' => 'datetime', - 'timestamp' => 'datetime', - 'time' => 'time', - 'float' => 'float', - 'double' => 'float', - 'real' => 'float', - 'decimal' => 'decimal', - 'numeric' => 'decimal', - 'year' => 'date', - 'longblob' => 'blob', - 'blob' => 'blob', - 'mediumblob' => 'blob', - 'tinyblob' => 'blob', - 'binary' => 'binary', - 'varbinary' => 'binary', - 'set' => 'simple_array', - ]; - } - - /** - * {@inheritDoc} - */ - public function getVarcharMaxLength() - { - return 65535; - } - - /** - * {@inheritdoc} - */ - public function getBinaryMaxLength() - { - return 65535; - } - - /** - * {@inheritDoc} - */ - protected function getReservedKeywordsClass() - { - return Keywords\MySQLKeywords::class; - } - - /** - * {@inheritDoc} - * - * MySQL commits a transaction implicitly when DROP TABLE is executed, however not - * if DROP TEMPORARY TABLE is executed. - */ - public function getDropTemporaryTableSQL($table) - { - if ($table instanceof Table) { - $table = $table->getQuotedName($this); - } elseif (! is_string($table)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.' - ); - } - - return 'DROP TEMPORARY TABLE ' . $table; - } - - /** - * Gets the SQL Snippet used to declare a BLOB column type. - * TINYBLOB : 2 ^ 8 - 1 = 255 - * BLOB : 2 ^ 16 - 1 = 65535 - * MEDIUMBLOB : 2 ^ 24 - 1 = 16777215 - * LONGBLOB : 2 ^ 32 - 1 = 4294967295 - * - * {@inheritDoc} - */ - public function getBlobTypeDeclarationSQL(array $column) - { - if (! empty($column['length']) && is_numeric($column['length'])) { - $length = $column['length']; - - if ($length <= static::LENGTH_LIMIT_TINYBLOB) { - return 'TINYBLOB'; - } - - if ($length <= static::LENGTH_LIMIT_BLOB) { - return 'BLOB'; - } - - if ($length <= static::LENGTH_LIMIT_MEDIUMBLOB) { - return 'MEDIUMBLOB'; - } - } - - return 'LONGBLOB'; - } - - /** - * {@inheritdoc} - */ - public function quoteStringLiteral($str) - { - $str = str_replace('\\', '\\\\', $str); // MySQL requires backslashes to be escaped aswell. - - return parent::quoteStringLiteral($str); - } - - /** - * {@inheritdoc} - */ - public function getDefaultTransactionIsolationLevel() - { - return TransactionIsolationLevel::REPEATABLE_READ; - } - - public function supportsColumnLengthIndexes(): bool - { - return true; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL100Platform.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL100Platform.php deleted file mode 100644 index f56100562..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL100Platform.php +++ /dev/null @@ -1,33 +0,0 @@ -quoteStringLiteral($database) . " - AND sequence_schema NOT LIKE 'pg\_%' - AND sequence_schema != 'information_schema'"; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php deleted file mode 100644 index 494a69c25..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php +++ /dev/null @@ -1,50 +0,0 @@ -quoteSingleIdentifier($collation); - } - - /** - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - $sql = parent::getListTableColumnsSQL($table, $database); - $parts = explode('AS complete_type,', $sql, 2); - - return $parts[0] . 'AS complete_type, ' - . '(SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation,' - . $parts[1]; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php deleted file mode 100644 index 9a1784a55..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php +++ /dev/null @@ -1,72 +0,0 @@ -doctrineTypeMapping['json'] = Types::JSON; - } - - /** - * {@inheritdoc} - */ - public function getCloseActiveDatabaseConnectionsSQL($database) - { - return sprintf( - 'SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = %s', - $this->quoteStringLiteral($database) - ); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL94Platform.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL94Platform.php deleted file mode 100644 index c17020f74..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL94Platform.php +++ /dev/null @@ -1,41 +0,0 @@ -doctrineTypeMapping['jsonb'] = Types::JSON; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php deleted file mode 100644 index 7888030d7..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php +++ /dev/null @@ -1,1310 +0,0 @@ - [ - 't', - 'true', - 'y', - 'yes', - 'on', - '1', - ], - 'false' => [ - 'f', - 'false', - 'n', - 'no', - 'off', - '0', - ], - ]; - - /** - * PostgreSQL has different behavior with some drivers - * with regard to how booleans have to be handled. - * - * Enables use of 'true'/'false' or otherwise 1 and 0 instead. - * - * @param bool $flag - * - * @return void - */ - public function setUseBooleanTrueFalseStrings($flag) - { - $this->useBooleanTrueFalseStrings = (bool) $flag; - } - - /** - * {@inheritDoc} - */ - public function getSubstringExpression($string, $start, $length = null) - { - if ($length === null) { - return 'SUBSTRING(' . $string . ' FROM ' . $start . ')'; - } - - return 'SUBSTRING(' . $string . ' FROM ' . $start . ' FOR ' . $length . ')'; - } - - /** - * {@inheritDoc} - */ - public function getNowExpression() - { - return 'LOCALTIMESTAMP(0)'; - } - - /** - * {@inheritDoc} - */ - public function getRegexpExpression() - { - return 'SIMILAR TO'; - } - - /** - * {@inheritDoc} - */ - public function getLocateExpression($str, $substr, $startPos = false) - { - if ($startPos !== false) { - $str = $this->getSubstringExpression($str, $startPos); - - return 'CASE WHEN (POSITION(' . $substr . ' IN ' . $str . ') = 0) THEN 0' - . ' ELSE (POSITION(' . $substr . ' IN ' . $str . ') + ' . ($startPos - 1) . ') END'; - } - - return 'POSITION(' . $substr . ' IN ' . $str . ')'; - } - - /** - * {@inheritdoc} - */ - protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) - { - if ($unit === DateIntervalUnit::QUARTER) { - $interval *= 3; - $unit = DateIntervalUnit::MONTH; - } - - return '(' . $date . ' ' . $operator . ' (' . $interval . " || ' " . $unit . "')::interval)"; - } - - /** - * {@inheritDoc} - */ - public function getDateDiffExpression($date1, $date2) - { - return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))'; - } - - /** - * {@inheritDoc} - */ - public function supportsSequences() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function supportsSchemas() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function getDefaultSchemaName() - { - return 'public'; - } - - /** - * {@inheritDoc} - */ - public function supportsIdentityColumns() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function supportsPartialIndexes() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function usesSequenceEmulatedIdentityColumns() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function getIdentitySequenceName($tableName, $columnName) - { - return $tableName . '_' . $columnName . '_seq'; - } - - /** - * {@inheritDoc} - */ - public function supportsCommentOnStatement() - { - return true; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function prefersSequences() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4229', - 'AbstractPlatform::prefersSequences() is deprecated without replacement and removed in DBAL 3.0' - ); - - return true; - } - - /** - * {@inheritDoc} - */ - public function hasNativeGuidType() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function getListDatabasesSQL() - { - return 'SELECT datname FROM pg_database'; - } - - /** - * {@inheritDoc} - */ - public function getListNamespacesSQL() - { - return "SELECT schema_name AS nspname - FROM information_schema.schemata - WHERE schema_name NOT LIKE 'pg\_%' - AND schema_name != 'information_schema'"; - } - - /** - * {@inheritDoc} - */ - public function getListSequencesSQL($database) - { - return "SELECT sequence_name AS relname, - sequence_schema AS schemaname - FROM information_schema.sequences - WHERE sequence_schema NOT LIKE 'pg\_%' - AND sequence_schema != 'information_schema'"; - } - - /** - * {@inheritDoc} - */ - public function getListTablesSQL() - { - return "SELECT quote_ident(table_name) AS table_name, - table_schema AS schema_name - FROM information_schema.tables - WHERE table_schema NOT LIKE 'pg\_%' - AND table_schema != 'information_schema' - AND table_name != 'geometry_columns' - AND table_name != 'spatial_ref_sys' - AND table_type != 'VIEW'"; - } - - /** - * {@inheritDoc} - */ - public function getListViewsSQL($database) - { - return 'SELECT quote_ident(table_name) AS viewname, - table_schema AS schemaname, - view_definition AS definition - FROM information_schema.views - WHERE view_definition IS NOT NULL'; - } - - /** - * @param string $table - * @param string|null $database - * - * @return string - */ - public function getListTableForeignKeysSQL($table, $database = null) - { - return 'SELECT quote_ident(r.conname) as conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef - FROM pg_catalog.pg_constraint r - WHERE r.conrelid = - ( - SELECT c.oid - FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n - WHERE ' . $this->getTableWhereClause($table) . " AND n.oid = c.relnamespace - ) - AND r.contype = 'f'"; - } - - /** - * {@inheritDoc} - */ - public function getCreateViewSQL($name, $sql) - { - return 'CREATE VIEW ' . $name . ' AS ' . $sql; - } - - /** - * {@inheritDoc} - */ - public function getDropViewSQL($name) - { - return 'DROP VIEW ' . $name; - } - - /** - * {@inheritDoc} - */ - public function getListTableConstraintsSQL($table) - { - $table = new Identifier($table); - $table = $this->quoteStringLiteral($table->getName()); - - return sprintf( - <<<'SQL' -SELECT - quote_ident(relname) as relname -FROM - pg_class -WHERE oid IN ( - SELECT indexrelid - FROM pg_index, pg_class - WHERE pg_class.relname = %s - AND pg_class.oid = pg_index.indrelid - AND (indisunique = 't' OR indisprimary = 't') - ) -SQL - , - $table - ); - } - - /** - * {@inheritDoc} - * - * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html - */ - public function getListTableIndexesSQL($table, $database = null) - { - return 'SELECT quote_ident(relname) as relname, pg_index.indisunique, pg_index.indisprimary, - pg_index.indkey, pg_index.indrelid, - pg_get_expr(indpred, indrelid) AS where - FROM pg_class, pg_index - WHERE oid IN ( - SELECT indexrelid - FROM pg_index si, pg_class sc, pg_namespace sn - WHERE ' . $this->getTableWhereClause($table, 'sc', 'sn') . ' - AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid - ) AND pg_index.indexrelid = oid'; - } - - /** - * @param string $table - * @param string $classAlias - * @param string $namespaceAlias - * - * @return string - */ - private function getTableWhereClause($table, $classAlias = 'c', $namespaceAlias = 'n') - { - $whereClause = $namespaceAlias . ".nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND "; - if (strpos($table, '.') !== false) { - [$schema, $table] = explode('.', $table); - $schema = $this->quoteStringLiteral($schema); - } else { - $schema = 'ANY(current_schemas(false))'; - } - - $table = new Identifier($table); - $table = $this->quoteStringLiteral($table->getName()); - - return $whereClause . sprintf( - '%s.relname = %s AND %s.nspname = %s', - $classAlias, - $table, - $namespaceAlias, - $schema - ); - } - - /** - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - return "SELECT - a.attnum, - quote_ident(a.attname) AS field, - t.typname AS type, - format_type(a.atttypid, a.atttypmod) AS complete_type, - (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type, - (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM - pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type, - a.attnotnull AS isnotnull, - (SELECT 't' - FROM pg_index - WHERE c.oid = pg_index.indrelid - AND pg_index.indkey[0] = a.attnum - AND pg_index.indisprimary = 't' - ) AS pri, - (SELECT pg_get_expr(adbin, adrelid) - FROM pg_attrdef - WHERE c.oid = pg_attrdef.adrelid - AND pg_attrdef.adnum=a.attnum - ) AS default, - (SELECT pg_description.description - FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid - ) AS comment - FROM pg_attribute a, pg_class c, pg_type t, pg_namespace n - WHERE " . $this->getTableWhereClause($table, 'c', 'n') . ' - AND a.attnum > 0 - AND a.attrelid = c.oid - AND a.atttypid = t.oid - AND n.oid = c.relnamespace - ORDER BY a.attnum'; - } - - /** - * {@inheritDoc} - */ - public function getCreateDatabaseSQL($name) - { - return 'CREATE DATABASE ' . $name; - } - - /** - * Returns the SQL statement for disallowing new connections on the given database. - * - * This is useful to force DROP DATABASE operations which could fail because of active connections. - * - * @deprecated - * - * @param string $database The name of the database to disallow new connections for. - * - * @return string - */ - public function getDisallowDatabaseConnectionsSQL($database) - { - return "UPDATE pg_database SET datallowconn = 'false' WHERE datname = " . $this->quoteStringLiteral($database); - } - - /** - * Returns the SQL statement for closing currently active connections on the given database. - * - * This is useful to force DROP DATABASE operations which could fail because of active connections. - * - * @deprecated - * - * @param string $database The name of the database to close currently active connections for. - * - * @return string - */ - public function getCloseActiveDatabaseConnectionsSQL($database) - { - return 'SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = ' - . $this->quoteStringLiteral($database); - } - - /** - * {@inheritDoc} - */ - public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) - { - $query = ''; - - if ($foreignKey->hasOption('match')) { - $query .= ' MATCH ' . $foreignKey->getOption('match'); - } - - $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); - - if ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) { - $query .= ' DEFERRABLE'; - } else { - $query .= ' NOT DEFERRABLE'; - } - - if ( - ($foreignKey->hasOption('feferred') && $foreignKey->getOption('feferred') !== false) - || ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) - ) { - $query .= ' INITIALLY DEFERRED'; - } else { - $query .= ' INITIALLY IMMEDIATE'; - } - - return $query; - } - - /** - * {@inheritDoc} - */ - public function getAlterTableSQL(TableDiff $diff) - { - $sql = []; - $commentsSQL = []; - $columnSql = []; - - foreach ($diff->addedColumns as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { - continue; - } - - $query = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; - - $comment = $this->getColumnComment($column); - - if ($comment === null || $comment === '') { - continue; - } - - $commentsSQL[] = $this->getCommentOnColumnSQL( - $diff->getName($this)->getQuotedName($this), - $column->getQuotedName($this), - $comment - ); - } - - foreach ($diff->removedColumns as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { - continue; - } - - $query = 'DROP ' . $column->getQuotedName($this); - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; - } - - foreach ($diff->changedColumns as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - - if ($this->isUnchangedBinaryColumn($columnDiff)) { - continue; - } - - $oldColumnName = $columnDiff->getOldColumnName()->getQuotedName($this); - $column = $columnDiff->column; - - if ( - $columnDiff->hasChanged('type') - || $columnDiff->hasChanged('precision') - || $columnDiff->hasChanged('scale') - || $columnDiff->hasChanged('fixed') - ) { - $type = $column->getType(); - - // SERIAL/BIGSERIAL are not "real" types and we can't alter a column to that type - $columnDefinition = $column->toArray(); - $columnDefinition['autoincrement'] = false; - - // here was a server version check before, but DBAL API does not support this anymore. - $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $type->getSQLDeclaration($columnDefinition, $this); - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; - } - - if ($columnDiff->hasChanged('default') || $this->typeChangeBreaksDefaultValue($columnDiff)) { - $defaultClause = $column->getDefault() === null - ? ' DROP DEFAULT' - : ' SET' . $this->getDefaultValueDeclarationSQL($column->toArray()); - $query = 'ALTER ' . $oldColumnName . $defaultClause; - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; - } - - if ($columnDiff->hasChanged('notnull')) { - $query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotnull() ? 'SET' : 'DROP') . ' NOT NULL'; - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; - } - - if ($columnDiff->hasChanged('autoincrement')) { - if ($column->getAutoincrement()) { - // add autoincrement - $seqName = $this->getIdentitySequenceName($diff->name, $oldColumnName); - - $sql[] = 'CREATE SEQUENCE ' . $seqName; - $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ') FROM ' - . $diff->getName($this)->getQuotedName($this) . '))'; - $query = 'ALTER ' . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')"; - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; - } else { - // Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have - $query = 'ALTER ' . $oldColumnName . ' DROP DEFAULT'; - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; - } - } - - $newComment = $this->getColumnComment($column); - $oldComment = $this->getOldColumnComment($columnDiff); - - if ( - $columnDiff->hasChanged('comment') - || ($columnDiff->fromColumn !== null && $oldComment !== $newComment) - ) { - $commentsSQL[] = $this->getCommentOnColumnSQL( - $diff->getName($this)->getQuotedName($this), - $column->getQuotedName($this), - $newComment - ); - } - - if (! $columnDiff->hasChanged('length')) { - continue; - } - - $query = 'ALTER ' . $oldColumnName . ' TYPE ' - . $column->getType()->getSQLDeclaration($column->toArray(), $this); - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; - } - - foreach ($diff->renamedColumns as $oldColumnName => $column) { - if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { - continue; - } - - $oldColumnName = new Identifier($oldColumnName); - - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . - ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); - } - - $tableSql = []; - - if (! $this->onSchemaAlterTable($diff, $tableSql)) { - $sql = array_merge($sql, $commentsSQL); - - $newName = $diff->getNewName(); - - if ($newName !== false) { - $sql[] = sprintf( - 'ALTER TABLE %s RENAME TO %s', - $diff->getName($this)->getQuotedName($this), - $newName->getQuotedName($this) - ); - } - - $sql = array_merge( - $this->getPreAlterTableIndexForeignKeySQL($diff), - $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff) - ); - } - - return array_merge($sql, $tableSql, $columnSql); - } - - /** - * Checks whether a given column diff is a logically unchanged binary type column. - * - * Used to determine whether a column alteration for a binary type column can be skipped. - * Doctrine's {@link BinaryType} and {@link BlobType} are mapped to the same database column type on this platform - * as this platform does not have a native VARBINARY/BINARY column type. Therefore the comparator - * might detect differences for binary type columns which do not have to be propagated - * to database as there actually is no difference at database level. - * - * @param ColumnDiff $columnDiff The column diff to check against. - * - * @return bool True if the given column diff is an unchanged binary type column, false otherwise. - */ - private function isUnchangedBinaryColumn(ColumnDiff $columnDiff) - { - $columnType = $columnDiff->column->getType(); - - if (! $columnType instanceof BinaryType && ! $columnType instanceof BlobType) { - return false; - } - - $fromColumn = $columnDiff->fromColumn instanceof Column ? $columnDiff->fromColumn : null; - - if ($fromColumn) { - $fromColumnType = $fromColumn->getType(); - - if (! $fromColumnType instanceof BinaryType && ! $fromColumnType instanceof BlobType) { - return false; - } - - return count(array_diff($columnDiff->changedProperties, ['type', 'length', 'fixed'])) === 0; - } - - if ($columnDiff->hasChanged('type')) { - return false; - } - - return count(array_diff($columnDiff->changedProperties, ['length', 'fixed'])) === 0; - } - - /** - * {@inheritdoc} - */ - protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) - { - if (strpos($tableName, '.') !== false) { - [$schema] = explode('.', $tableName); - $oldIndexName = $schema . '.' . $oldIndexName; - } - - return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)]; - } - - /** - * {@inheritdoc} - */ - public function getCommentOnColumnSQL($tableName, $columnName, $comment) - { - $tableName = new Identifier($tableName); - $columnName = new Identifier($columnName); - $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment); - - return sprintf( - 'COMMENT ON COLUMN %s.%s IS %s', - $tableName->getQuotedName($this), - $columnName->getQuotedName($this), - $comment - ); - } - - /** - * {@inheritDoc} - */ - public function getCreateSequenceSQL(Sequence $sequence) - { - return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . - ' INCREMENT BY ' . $sequence->getAllocationSize() . - ' MINVALUE ' . $sequence->getInitialValue() . - ' START ' . $sequence->getInitialValue() . - $this->getSequenceCacheSQL($sequence); - } - - /** - * {@inheritDoc} - */ - public function getAlterSequenceSQL(Sequence $sequence) - { - return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . - ' INCREMENT BY ' . $sequence->getAllocationSize() . - $this->getSequenceCacheSQL($sequence); - } - - /** - * Cache definition for sequences - * - * @return string - */ - private function getSequenceCacheSQL(Sequence $sequence) - { - if ($sequence->getCache() > 1) { - return ' CACHE ' . $sequence->getCache(); - } - - return ''; - } - - /** - * {@inheritDoc} - */ - public function getDropSequenceSQL($sequence) - { - if ($sequence instanceof Sequence) { - $sequence = $sequence->getQuotedName($this); - } - - return 'DROP SEQUENCE ' . $sequence . ' CASCADE'; - } - - /** - * {@inheritDoc} - */ - public function getCreateSchemaSQL($schemaName) - { - return 'CREATE SCHEMA ' . $schemaName; - } - - /** - * {@inheritDoc} - */ - public function getDropForeignKeySQL($foreignKey, $table) - { - return $this->getDropConstraintSQL($foreignKey, $table); - } - - /** - * {@inheritDoc} - */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) - { - $queryFields = $this->getColumnDeclarationListSQL($columns); - - if (isset($options['primary']) && ! empty($options['primary'])) { - $keyColumns = array_unique(array_values($options['primary'])); - $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; - } - - $query = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')'; - - $sql = [$query]; - - if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach ($options['indexes'] as $index) { - $sql[] = $this->getCreateIndexSQL($index, $name); - } - } - - if (isset($options['foreignKeys'])) { - foreach ((array) $options['foreignKeys'] as $definition) { - $sql[] = $this->getCreateForeignKeySQL($definition, $name); - } - } - - return $sql; - } - - /** - * Converts a single boolean value. - * - * First converts the value to its native PHP boolean type - * and passes it to the given callback function to be reconverted - * into any custom representation. - * - * @param mixed $value The value to convert. - * @param callable $callback The callback function to use for converting the real boolean value. - * - * @return mixed - * - * @throws UnexpectedValueException - */ - private function convertSingleBooleanValue($value, $callback) - { - if ($value === null) { - return $callback(null); - } - - if (is_bool($value) || is_numeric($value)) { - return $callback((bool) $value); - } - - if (! is_string($value)) { - return $callback(true); - } - - /** - * Better safe than sorry: http://php.net/in_array#106319 - */ - if (in_array(strtolower(trim($value)), $this->booleanLiterals['false'], true)) { - return $callback(false); - } - - if (in_array(strtolower(trim($value)), $this->booleanLiterals['true'], true)) { - return $callback(true); - } - - throw new UnexpectedValueException("Unrecognized boolean literal '${value}'"); - } - - /** - * Converts one or multiple boolean values. - * - * First converts the value(s) to their native PHP boolean type - * and passes them to the given callback function to be reconverted - * into any custom representation. - * - * @param mixed $item The value(s) to convert. - * @param callable $callback The callback function to use for converting the real boolean value(s). - * - * @return mixed - */ - private function doConvertBooleans($item, $callback) - { - if (is_array($item)) { - foreach ($item as $key => $value) { - $item[$key] = $this->convertSingleBooleanValue($value, $callback); - } - - return $item; - } - - return $this->convertSingleBooleanValue($item, $callback); - } - - /** - * {@inheritDoc} - * - * Postgres wants boolean values converted to the strings 'true'/'false'. - */ - public function convertBooleans($item) - { - if (! $this->useBooleanTrueFalseStrings) { - return parent::convertBooleans($item); - } - - return $this->doConvertBooleans( - $item, - /** - * @param mixed $value - */ - static function ($value) { - if ($value === null) { - return 'NULL'; - } - - return $value === true ? 'true' : 'false'; - } - ); - } - - /** - * {@inheritDoc} - */ - public function convertBooleansToDatabaseValue($item) - { - if (! $this->useBooleanTrueFalseStrings) { - return parent::convertBooleansToDatabaseValue($item); - } - - return $this->doConvertBooleans( - $item, - /** - * @param mixed $value - */ - static function ($value) { - return $value === null ? null : (int) $value; - } - ); - } - - /** - * {@inheritDoc} - */ - public function convertFromBoolean($item) - { - if ($item !== null && in_array(strtolower($item), $this->booleanLiterals['false'], true)) { - return false; - } - - return parent::convertFromBoolean($item); - } - - /** - * {@inheritDoc} - */ - public function getSequenceNextValSQL($sequence) - { - return "SELECT NEXTVAL('" . $sequence . "')"; - } - - /** - * {@inheritDoc} - */ - public function getSetTransactionIsolationSQL($level) - { - return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ' - . $this->_getTransactionIsolationLevelSQL($level); - } - - /** - * {@inheritDoc} - */ - public function getBooleanTypeDeclarationSQL(array $column) - { - return 'BOOLEAN'; - } - - /** - * {@inheritDoc} - */ - public function getIntegerTypeDeclarationSQL(array $column) - { - if (! empty($column['autoincrement'])) { - return 'SERIAL'; - } - - return 'INT'; - } - - /** - * {@inheritDoc} - */ - public function getBigIntTypeDeclarationSQL(array $column) - { - if (! empty($column['autoincrement'])) { - return 'BIGSERIAL'; - } - - return 'BIGINT'; - } - - /** - * {@inheritDoc} - */ - public function getSmallIntTypeDeclarationSQL(array $column) - { - return 'SMALLINT'; - } - - /** - * {@inheritDoc} - */ - public function getGuidTypeDeclarationSQL(array $column) - { - return 'UUID'; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTypeDeclarationSQL(array $column) - { - return 'TIMESTAMP(0) WITHOUT TIME ZONE'; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTzTypeDeclarationSQL(array $column) - { - return 'TIMESTAMP(0) WITH TIME ZONE'; - } - - /** - * {@inheritDoc} - */ - public function getDateTypeDeclarationSQL(array $column) - { - return 'DATE'; - } - - /** - * {@inheritDoc} - */ - public function getTimeTypeDeclarationSQL(array $column) - { - return 'TIME(0) WITHOUT TIME ZONE'; - } - - /** - * {@inheritDoc} - * - * @deprecated Use application-generated UUIDs instead - */ - public function getGuidExpression() - { - return 'UUID_GENERATE_V4()'; - } - - /** - * {@inheritDoc} - */ - protected function _getCommonIntegerTypeDeclarationSQL(array $column) - { - return ''; - } - - /** - * {@inheritDoc} - */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) - { - return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') - : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); - } - - /** - * {@inheritdoc} - */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) - { - return 'BYTEA'; - } - - /** - * {@inheritDoc} - */ - public function getClobTypeDeclarationSQL(array $column) - { - return 'TEXT'; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return 'postgresql'; - } - - /** - * {@inheritDoc} - * - * PostgreSQL returns all column names in SQL result sets in lowercase. - * - * @deprecated - */ - public function getSQLResultCasing($column) - { - return strtolower($column); - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTzFormatString() - { - return 'Y-m-d H:i:sO'; - } - - /** - * {@inheritDoc} - */ - public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) - { - return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)'; - } - - /** - * {@inheritDoc} - */ - public function getTruncateTableSQL($tableName, $cascade = false) - { - $tableIdentifier = new Identifier($tableName); - $sql = 'TRUNCATE ' . $tableIdentifier->getQuotedName($this); - - if ($cascade) { - $sql .= ' CASCADE'; - } - - return $sql; - } - - /** - * {@inheritDoc} - */ - public function getReadLockSQL() - { - return 'FOR SHARE'; - } - - /** - * {@inheritDoc} - */ - protected function initializeDoctrineTypeMappings() - { - $this->doctrineTypeMapping = [ - 'smallint' => 'smallint', - 'int2' => 'smallint', - 'serial' => 'integer', - 'serial4' => 'integer', - 'int' => 'integer', - 'int4' => 'integer', - 'integer' => 'integer', - 'bigserial' => 'bigint', - 'serial8' => 'bigint', - 'bigint' => 'bigint', - 'int8' => 'bigint', - 'bool' => 'boolean', - 'boolean' => 'boolean', - 'text' => 'text', - 'tsvector' => 'text', - 'varchar' => 'string', - 'interval' => 'string', - '_varchar' => 'string', - 'char' => 'string', - 'bpchar' => 'string', - 'inet' => 'string', - 'date' => 'date', - 'datetime' => 'datetime', - 'timestamp' => 'datetime', - 'timestamptz' => 'datetimetz', - 'time' => 'time', - 'timetz' => 'time', - 'float' => 'float', - 'float4' => 'float', - 'float8' => 'float', - 'double' => 'float', - 'double precision' => 'float', - 'real' => 'float', - 'decimal' => 'decimal', - 'money' => 'decimal', - 'numeric' => 'decimal', - 'year' => 'date', - 'uuid' => 'guid', - 'bytea' => 'blob', - ]; - } - - /** - * {@inheritDoc} - */ - public function getVarcharMaxLength() - { - return 65535; - } - - /** - * {@inheritdoc} - */ - public function getBinaryMaxLength() - { - return 0; - } - - /** - * {@inheritdoc} - */ - public function getBinaryDefaultLength() - { - return 0; - } - - /** - * {@inheritDoc} - */ - protected function getReservedKeywordsClass() - { - return Keywords\PostgreSQLKeywords::class; - } - - /** - * {@inheritDoc} - */ - public function getBlobTypeDeclarationSQL(array $column) - { - return 'BYTEA'; - } - - /** - * {@inheritdoc} - */ - public function getDefaultValueDeclarationSQL($column) - { - if ($this->isSerialColumn($column)) { - return ''; - } - - return parent::getDefaultValueDeclarationSQL($column); - } - - /** - * @param mixed[] $column - */ - private function isSerialColumn(array $column): bool - { - return isset($column['type'], $column['autoincrement']) - && $column['autoincrement'] === true - && $this->isNumericType($column['type']); - } - - /** - * Check whether the type of a column is changed in a way that invalidates the default value for the column - */ - private function typeChangeBreaksDefaultValue(ColumnDiff $columnDiff): bool - { - if (! $columnDiff->fromColumn) { - return $columnDiff->hasChanged('type'); - } - - $oldTypeIsNumeric = $this->isNumericType($columnDiff->fromColumn->getType()); - $newTypeIsNumeric = $this->isNumericType($columnDiff->column->getType()); - - // default should not be changed when switching between numeric types and the default comes from a sequence - return $columnDiff->hasChanged('type') - && ! ($oldTypeIsNumeric && $newTypeIsNumeric && $columnDiff->column->getAutoincrement()); - } - - private function isNumericType(Type $type): bool - { - return $type instanceof IntegerType || $type instanceof BigIntType; - } - - private function getOldColumnComment(ColumnDiff $columnDiff): ?string - { - return $columnDiff->fromColumn ? $this->getColumnComment($columnDiff->fromColumn) : null; - } - - public function getListTableMetadataSQL(string $table, ?string $schema = null): string - { - if ($schema !== null) { - $table = $schema . '.' . $table; - } - - return sprintf( - <<<'SQL' -SELECT obj_description(%s::regclass) AS table_comment; -SQL - , - $this->quoteStringLiteral($table) - ); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php deleted file mode 100644 index 944871833..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php +++ /dev/null @@ -1,28 +0,0 @@ -getQuotedName($this) . - ' INCREMENT BY ' . $sequence->getAllocationSize() . - ' START WITH ' . $sequence->getInitialValue() . - ' MINVALUE ' . $sequence->getInitialValue(); - } - - /** - * {@inheritdoc} - */ - public function getAlterSequenceSQL(Sequence $sequence) - { - return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . - ' INCREMENT BY ' . $sequence->getAllocationSize(); - } - - /** - * {@inheritdoc} - */ - public function getDateTimeTzFormatString() - { - return 'Y-m-d H:i:s.uP'; - } - - /** - * {@inheritdoc} - */ - public function getDateTimeTzTypeDeclarationSQL(array $column) - { - return 'TIMESTAMP WITH TIME ZONE'; - } - - /** - * {@inheritdoc} - */ - public function getDropSequenceSQL($sequence) - { - if ($sequence instanceof Sequence) { - $sequence = $sequence->getQuotedName($this); - } - - return 'DROP SEQUENCE ' . $sequence; - } - - /** - * {@inheritdoc} - */ - public function getListSequencesSQL($database) - { - return 'SELECT sequence_name, increment_by, start_with, min_value FROM SYS.SYSSEQUENCE'; - } - - /** - * {@inheritdoc} - */ - public function getSequenceNextValSQL($sequence) - { - return 'SELECT ' . $sequence . '.NEXTVAL'; - } - - /** - * {@inheritdoc} - */ - public function supportsSequences() - { - return true; - } - - /** - * {@inheritdoc} - */ - protected function getAdvancedIndexOptionsSQL(Index $index) - { - if (! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_not_distinct')) { - return ' WITH NULLS NOT DISTINCT' . parent::getAdvancedIndexOptionsSQL($index); - } - - return parent::getAdvancedIndexOptionsSQL($index); - } - - /** - * {@inheritdoc} - */ - protected function getReservedKeywordsClass() - { - return Keywords\SQLAnywhere12Keywords::class; - } - - /** - * {@inheritDoc} - */ - protected function initializeDoctrineTypeMappings() - { - parent::initializeDoctrineTypeMappings(); - $this->doctrineTypeMapping['timestamp with time zone'] = 'datetime'; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php deleted file mode 100644 index c278cf6f0..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php +++ /dev/null @@ -1,41 +0,0 @@ -hasFlag('with_nulls_distinct') && $index->hasFlag('with_nulls_not_distinct')) { - throw new UnexpectedValueException( - 'An Index can either have a "with_nulls_distinct" or "with_nulls_not_distinct" flag but not both.' - ); - } - - if (! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_distinct')) { - return ' WITH NULLS DISTINCT' . parent::getAdvancedIndexOptionsSQL($index); - } - - return parent::getAdvancedIndexOptionsSQL($index); - } - - /** - * {@inheritdoc} - */ - protected function getReservedKeywordsClass() - { - return Keywords\SQLAnywhere16Keywords::class; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php deleted file mode 100644 index 49eecfd5f..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php +++ /dev/null @@ -1,1532 +0,0 @@ -getMaxIdentifierLength(); - - if (strlen($schemaElementName) > $maxIdentifierLength) { - return substr($schemaElementName, 0, $maxIdentifierLength); - } - - return $schemaElementName; - } - - /** - * {@inheritdoc} - */ - public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) - { - $query = ''; - - if ($foreignKey->hasOption('match')) { - $query = ' MATCH ' . $this->getForeignKeyMatchClauseSQL($foreignKey->getOption('match')); - } - - $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); - - if ($foreignKey->hasOption('check_on_commit') && (bool) $foreignKey->getOption('check_on_commit')) { - $query .= ' CHECK ON COMMIT'; - } - - if ($foreignKey->hasOption('clustered') && (bool) $foreignKey->getOption('clustered')) { - $query .= ' CLUSTERED'; - } - - if ($foreignKey->hasOption('for_olap_workload') && (bool) $foreignKey->getOption('for_olap_workload')) { - $query .= ' FOR OLAP WORKLOAD'; - } - - return $query; - } - - /** - * {@inheritdoc} - */ - public function getAlterTableSQL(TableDiff $diff) - { - $sql = []; - $columnSql = []; - $commentsSQL = []; - $tableSql = []; - $alterClauses = []; - - foreach ($diff->addedColumns as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { - continue; - } - - $alterClauses[] = $this->getAlterTableAddColumnClause($column); - - $comment = $this->getColumnComment($column); - - if ($comment === null || $comment === '') { - continue; - } - - $commentsSQL[] = $this->getCommentOnColumnSQL( - $diff->getName($this)->getQuotedName($this), - $column->getQuotedName($this), - $comment - ); - } - - foreach ($diff->removedColumns as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { - continue; - } - - $alterClauses[] = $this->getAlterTableRemoveColumnClause($column); - } - - foreach ($diff->changedColumns as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - - $alterClause = $this->getAlterTableChangeColumnClause($columnDiff); - - if ($alterClause !== null) { - $alterClauses[] = $alterClause; - } - - if (! $columnDiff->hasChanged('comment')) { - continue; - } - - $column = $columnDiff->column; - - $commentsSQL[] = $this->getCommentOnColumnSQL( - $diff->getName($this)->getQuotedName($this), - $column->getQuotedName($this), - $this->getColumnComment($column) - ); - } - - foreach ($diff->renamedColumns as $oldColumnName => $column) { - if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { - continue; - } - - $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . - $this->getAlterTableRenameColumnClause($oldColumnName, $column); - } - - if (! $this->onSchemaAlterTable($diff, $tableSql)) { - if (! empty($alterClauses)) { - $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . implode(', ', $alterClauses); - } - - $sql = array_merge($sql, $commentsSQL); - - $newName = $diff->getNewName(); - - if ($newName !== false) { - $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . - $this->getAlterTableRenameTableClause($newName); - } - - $sql = array_merge( - $this->getPreAlterTableIndexForeignKeySQL($diff), - $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff) - ); - } - - return array_merge($sql, $tableSql, $columnSql); - } - - /** - * Returns the SQL clause for creating a column in a table alteration. - * - * @param Column $column The column to add. - * - * @return string - */ - protected function getAlterTableAddColumnClause(Column $column) - { - return 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); - } - - /** - * Returns the SQL clause for altering a table. - * - * @param Identifier $tableName The quoted name of the table to alter. - * - * @return string - */ - protected function getAlterTableClause(Identifier $tableName) - { - return 'ALTER TABLE ' . $tableName->getQuotedName($this); - } - - /** - * Returns the SQL clause for dropping a column in a table alteration. - * - * @param Column $column The column to drop. - * - * @return string - */ - protected function getAlterTableRemoveColumnClause(Column $column) - { - return 'DROP ' . $column->getQuotedName($this); - } - - /** - * Returns the SQL clause for renaming a column in a table alteration. - * - * @param string $oldColumnName The quoted name of the column to rename. - * @param Column $column The column to rename to. - * - * @return string - */ - protected function getAlterTableRenameColumnClause($oldColumnName, Column $column) - { - $oldColumnName = new Identifier($oldColumnName); - - return 'RENAME ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); - } - - /** - * Returns the SQL clause for renaming a table in a table alteration. - * - * @param Identifier $newTableName The quoted name of the table to rename to. - * - * @return string - */ - protected function getAlterTableRenameTableClause(Identifier $newTableName) - { - return 'RENAME ' . $newTableName->getQuotedName($this); - } - - /** - * Returns the SQL clause for altering a column in a table alteration. - * - * This method returns null in case that only the column comment has changed. - * Changes in column comments have to be handled differently. - * - * @param ColumnDiff $columnDiff The diff of the column to alter. - * - * @return string|null - */ - protected function getAlterTableChangeColumnClause(ColumnDiff $columnDiff) - { - $column = $columnDiff->column; - - // Do not return alter clause if only comment has changed. - if (! ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1)) { - $columnAlterationClause = 'ALTER ' . - $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); - - if ($columnDiff->hasChanged('default') && $column->getDefault() === null) { - $columnAlterationClause .= ', ALTER ' . $column->getQuotedName($this) . ' DROP DEFAULT'; - } - - return $columnAlterationClause; - } - - return null; - } - - /** - * {@inheritdoc} - */ - public function getBigIntTypeDeclarationSQL(array $column) - { - $column['integer_type'] = 'BIGINT'; - - return $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritdoc} - */ - public function getBinaryDefaultLength() - { - return 1; - } - - /** - * {@inheritdoc} - */ - public function getBinaryMaxLength() - { - return 32767; - } - - /** - * {@inheritdoc} - */ - public function getBlobTypeDeclarationSQL(array $column) - { - return 'LONG BINARY'; - } - - /** - * {@inheritdoc} - * - * BIT type columns require an explicit NULL declaration - * in SQL Anywhere if they shall be nullable. - * Otherwise by just omitting the NOT NULL clause, - * SQL Anywhere will declare them NOT NULL nonetheless. - */ - public function getBooleanTypeDeclarationSQL(array $column) - { - $nullClause = isset($column['notnull']) && (bool) $column['notnull'] === false ? ' NULL' : ''; - - return 'BIT' . $nullClause; - } - - /** - * {@inheritdoc} - */ - public function getClobTypeDeclarationSQL(array $column) - { - return 'TEXT'; - } - - /** - * {@inheritdoc} - */ - public function getCommentOnColumnSQL($tableName, $columnName, $comment) - { - $tableName = new Identifier($tableName); - $columnName = new Identifier($columnName); - $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment); - - return sprintf( - 'COMMENT ON COLUMN %s.%s IS %s', - $tableName->getQuotedName($this), - $columnName->getQuotedName($this), - $comment - ); - } - - /** - * {@inheritdoc} - */ - public function getConcatExpression() - { - return 'STRING(' . implode(', ', func_get_args()) . ')'; - } - - /** - * {@inheritdoc} - */ - public function getCreateConstraintSQL(Constraint $constraint, $table) - { - if ($constraint instanceof ForeignKeyConstraint) { - return $this->getCreateForeignKeySQL($constraint, $table); - } - - if ($table instanceof Table) { - $table = $table->getQuotedName($this); - } - - return 'ALTER TABLE ' . $table . - ' ADD ' . $this->getTableConstraintDeclarationSQL($constraint, $constraint->getQuotedName($this)); - } - - /** - * {@inheritdoc} - */ - public function getCreateDatabaseSQL($name) - { - $name = new Identifier($name); - - return "CREATE DATABASE '" . $name->getName() . "'"; - } - - /** - * {@inheritdoc} - * - * Appends SQL Anywhere specific flags if given. - */ - public function getCreateIndexSQL(Index $index, $table) - { - return parent::getCreateIndexSQL($index, $table) . $this->getAdvancedIndexOptionsSQL($index); - } - - /** - * {@inheritdoc} - */ - public function getCreatePrimaryKeySQL(Index $index, $table) - { - if ($table instanceof Table) { - $table = $table->getQuotedName($this); - } - - return 'ALTER TABLE ' . $table . ' ADD ' . $this->getPrimaryKeyDeclarationSQL($index); - } - - /** - * {@inheritdoc} - */ - public function getCreateTemporaryTableSnippetSQL() - { - return 'CREATE ' . $this->getTemporaryTableSQL() . ' TABLE'; - } - - /** - * {@inheritdoc} - */ - public function getCreateViewSQL($name, $sql) - { - return 'CREATE VIEW ' . $name . ' AS ' . $sql; - } - - /** - * {@inheritdoc} - */ - public function getCurrentDateSQL() - { - return 'CURRENT DATE'; - } - - /** - * {@inheritdoc} - */ - public function getCurrentTimeSQL() - { - return 'CURRENT TIME'; - } - - /** - * {@inheritdoc} - */ - public function getCurrentTimestampSQL() - { - return 'CURRENT TIMESTAMP'; - } - - /** - * {@inheritdoc} - */ - protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) - { - $factorClause = ''; - - if ($operator === '-') { - $factorClause = '-1 * '; - } - - return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')'; - } - - /** - * {@inheritdoc} - */ - public function getDateDiffExpression($date1, $date2) - { - return 'DATEDIFF(day, ' . $date2 . ', ' . $date1 . ')'; - } - - /** - * {@inheritdoc} - */ - public function getDateTimeFormatString() - { - return 'Y-m-d H:i:s.u'; - } - - /** - * {@inheritdoc} - */ - public function getDateTimeTypeDeclarationSQL(array $column) - { - return 'DATETIME'; - } - - /** - * {@inheritdoc} - */ - public function getDateTimeTzFormatString() - { - return $this->getDateTimeFormatString(); - } - - /** - * {@inheritdoc} - */ - public function getDateTypeDeclarationSQL(array $column) - { - return 'DATE'; - } - - /** - * {@inheritdoc} - */ - public function getDefaultTransactionIsolationLevel() - { - return TransactionIsolationLevel::READ_UNCOMMITTED; - } - - /** - * {@inheritdoc} - */ - public function getDropDatabaseSQL($name) - { - $name = new Identifier($name); - - return "DROP DATABASE '" . $name->getName() . "'"; - } - - /** - * {@inheritdoc} - */ - public function getDropIndexSQL($index, $table = null) - { - if ($index instanceof Index) { - $index = $index->getQuotedName($this); - } - - if (! is_string($index)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.' - ); - } - - if (! isset($table)) { - return 'DROP INDEX ' . $index; - } - - if ($table instanceof Table) { - $table = $table->getQuotedName($this); - } - - if (! is_string($table)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Index::class . '.' - ); - } - - return 'DROP INDEX ' . $table . '.' . $index; - } - - /** - * {@inheritdoc} - */ - public function getDropViewSQL($name) - { - return 'DROP VIEW ' . $name; - } - - /** - * {@inheritdoc} - */ - public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) - { - $sql = ''; - $foreignKeyName = $foreignKey->getName(); - $localColumns = $foreignKey->getQuotedLocalColumns($this); - $foreignColumns = $foreignKey->getQuotedForeignColumns($this); - $foreignTableName = $foreignKey->getQuotedForeignTableName($this); - - if (! empty($foreignKeyName)) { - $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' '; - } - - if (empty($localColumns)) { - throw new InvalidArgumentException("Incomplete definition. 'local' required."); - } - - if (empty($foreignColumns)) { - throw new InvalidArgumentException("Incomplete definition. 'foreign' required."); - } - - if (empty($foreignTableName)) { - throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required."); - } - - if ($foreignKey->hasOption('notnull') && (bool) $foreignKey->getOption('notnull')) { - $sql .= 'NOT NULL '; - } - - return $sql . - 'FOREIGN KEY (' . $this->getIndexFieldDeclarationListSQL($localColumns) . ') ' . - 'REFERENCES ' . $foreignKey->getQuotedForeignTableName($this) . - ' (' . $this->getIndexFieldDeclarationListSQL($foreignColumns) . ')'; - } - - /** - * Returns foreign key MATCH clause for given type. - * - * @param int $type The foreign key match type - * - * @return string - * - * @throws InvalidArgumentException If unknown match type given. - */ - public function getForeignKeyMatchClauseSQL($type) - { - switch ((int) $type) { - case self::FOREIGN_KEY_MATCH_SIMPLE: - return 'SIMPLE'; - - case self::FOREIGN_KEY_MATCH_FULL: - return 'FULL'; - - case self::FOREIGN_KEY_MATCH_SIMPLE_UNIQUE: - return 'UNIQUE SIMPLE'; - - case self::FOREIGN_KEY_MATCH_FULL_UNIQUE: - return 'UNIQUE FULL'; - - default: - throw new InvalidArgumentException('Invalid foreign key match type: ' . $type); - } - } - - /** - * {@inheritdoc} - */ - public function getForeignKeyReferentialActionSQL($action) - { - // NO ACTION is not supported, therefore falling back to RESTRICT. - if (strtoupper($action) === 'NO ACTION') { - return 'RESTRICT'; - } - - return parent::getForeignKeyReferentialActionSQL($action); - } - - /** - * {@inheritdoc} - */ - public function getForUpdateSQL() - { - return ''; - } - - /** - * {@inheritdoc} - * - * @deprecated Use application-generated UUIDs instead - */ - public function getGuidExpression() - { - return 'NEWID()'; - } - - /** - * {@inheritdoc} - */ - public function getGuidTypeDeclarationSQL(array $column) - { - return 'UNIQUEIDENTIFIER'; - } - - /** - * {@inheritdoc} - */ - public function getIndexDeclarationSQL($name, Index $index) - { - // Index declaration in statements like CREATE TABLE is not supported. - throw Exception::notSupported(__METHOD__); - } - - /** - * {@inheritdoc} - */ - public function getIntegerTypeDeclarationSQL(array $column) - { - $column['integer_type'] = 'INT'; - - return $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritdoc} - */ - public function getListDatabasesSQL() - { - return 'SELECT db_name(number) AS name FROM sa_db_list()'; - } - - /** - * {@inheritdoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - $user = 'USER_NAME()'; - - if (strpos($table, '.') !== false) { - [$user, $table] = explode('.', $table); - $user = $this->quoteStringLiteral($user); - } - - return sprintf( - <<<'SQL' -SELECT col.column_name, - COALESCE(def.user_type_name, def.domain_name) AS 'type', - def.declared_width AS 'length', - def.scale, - CHARINDEX('unsigned', def.domain_name) AS 'unsigned', - IF col.nulls = 'Y' THEN 0 ELSE 1 ENDIF AS 'notnull', - col."default", - def.is_autoincrement AS 'autoincrement', - rem.remarks AS 'comment' -FROM sa_describe_query('SELECT * FROM "%s"') AS def -JOIN SYS.SYSTABCOL AS col -ON col.table_id = def.base_table_id AND col.column_id = def.base_column_id -LEFT JOIN SYS.SYSREMARK AS rem -ON col.object_id = rem.object_id -WHERE def.base_owner_name = %s -ORDER BY def.base_column_id ASC -SQL - , - $table, - $user - ); - } - - /** - * {@inheritdoc} - * - * @todo Where is this used? Which information should be retrieved? - */ - public function getListTableConstraintsSQL($table) - { - $user = ''; - - if (strpos($table, '.') !== false) { - [$user, $table] = explode('.', $table); - $user = $this->quoteStringLiteral($user); - $table = $this->quoteStringLiteral($table); - } else { - $table = $this->quoteStringLiteral($table); - } - - return sprintf( - <<<'SQL' -SELECT con.* -FROM SYS.SYSCONSTRAINT AS con -JOIN SYS.SYSTAB AS tab ON con.table_object_id = tab.object_id -WHERE tab.table_name = %s -AND tab.creator = USER_ID(%s) -SQL - , - $table, - $user - ); - } - - /** - * {@inheritdoc} - */ - public function getListTableForeignKeysSQL($table) - { - $user = ''; - - if (strpos($table, '.') !== false) { - [$user, $table] = explode('.', $table); - $user = $this->quoteStringLiteral($user); - $table = $this->quoteStringLiteral($table); - } else { - $table = $this->quoteStringLiteral($table); - } - - return sprintf( - <<<'SQL' -SELECT fcol.column_name AS local_column, - ptbl.table_name AS foreign_table, - pcol.column_name AS foreign_column, - idx.index_name, - IF fk.nulls = 'N' - THEN 1 - ELSE NULL - ENDIF AS notnull, - CASE ut.referential_action - WHEN 'C' THEN 'CASCADE' - WHEN 'D' THEN 'SET DEFAULT' - WHEN 'N' THEN 'SET NULL' - WHEN 'R' THEN 'RESTRICT' - ELSE NULL - END AS on_update, - CASE dt.referential_action - WHEN 'C' THEN 'CASCADE' - WHEN 'D' THEN 'SET DEFAULT' - WHEN 'N' THEN 'SET NULL' - WHEN 'R' THEN 'RESTRICT' - ELSE NULL - END AS on_delete, - IF fk.check_on_commit = 'Y' - THEN 1 - ELSE NULL - ENDIF AS check_on_commit, -- check_on_commit flag - IF ftbl.clustered_index_id = idx.index_id - THEN 1 - ELSE NULL - ENDIF AS 'clustered', -- clustered flag - IF fk.match_type = 0 - THEN NULL - ELSE fk.match_type - ENDIF AS 'match', -- match option - IF pidx.max_key_distance = 1 - THEN 1 - ELSE NULL - ENDIF AS for_olap_workload -- for_olap_workload flag -FROM SYS.SYSFKEY AS fk -JOIN SYS.SYSIDX AS idx -ON fk.foreign_table_id = idx.table_id -AND fk.foreign_index_id = idx.index_id -JOIN SYS.SYSPHYSIDX pidx -ON idx.table_id = pidx.table_id -AND idx.phys_index_id = pidx.phys_index_id -JOIN SYS.SYSTAB AS ptbl -ON fk.primary_table_id = ptbl.table_id -JOIN SYS.SYSTAB AS ftbl -ON fk.foreign_table_id = ftbl.table_id -JOIN SYS.SYSIDXCOL AS idxcol -ON idx.table_id = idxcol.table_id -AND idx.index_id = idxcol.index_id -JOIN SYS.SYSTABCOL AS pcol -ON ptbl.table_id = pcol.table_id -AND idxcol.primary_column_id = pcol.column_id -JOIN SYS.SYSTABCOL AS fcol -ON ftbl.table_id = fcol.table_id -AND idxcol.column_id = fcol.column_id -LEFT JOIN SYS.SYSTRIGGER ut -ON fk.foreign_table_id = ut.foreign_table_id -AND fk.foreign_index_id = ut.foreign_key_id -AND ut.event = 'C' -LEFT JOIN SYS.SYSTRIGGER dt -ON fk.foreign_table_id = dt.foreign_table_id -AND fk.foreign_index_id = dt.foreign_key_id -AND dt.event = 'D' -WHERE ftbl.table_name = %s -AND ftbl.creator = USER_ID(%s) -ORDER BY fk.foreign_index_id ASC, idxcol.sequence ASC -SQL - , - $table, - $user - ); - } - - /** - * {@inheritdoc} - */ - public function getListTableIndexesSQL($table, $database = null) - { - $user = ''; - - if (strpos($table, '.') !== false) { - [$user, $table] = explode('.', $table); - $user = $this->quoteStringLiteral($user); - $table = $this->quoteStringLiteral($table); - } else { - $table = $this->quoteStringLiteral($table); - } - - return sprintf( - <<<'SQL' -SELECT idx.index_name AS key_name, - IF idx.index_category = 1 - THEN 1 - ELSE 0 - ENDIF AS 'primary', - col.column_name, - IF idx."unique" IN(1, 2, 5) - THEN 0 - ELSE 1 - ENDIF AS non_unique, - IF tbl.clustered_index_id = idx.index_id - THEN 1 - ELSE NULL - ENDIF AS 'clustered', -- clustered flag - IF idx."unique" = 5 - THEN 1 - ELSE NULL - ENDIF AS with_nulls_not_distinct, -- with_nulls_not_distinct flag - IF pidx.max_key_distance = 1 - THEN 1 - ELSE NULL - ENDIF AS for_olap_workload -- for_olap_workload flag -FROM SYS.SYSIDX AS idx -JOIN SYS.SYSPHYSIDX pidx -ON idx.table_id = pidx.table_id -AND idx.phys_index_id = pidx.phys_index_id -JOIN SYS.SYSIDXCOL AS idxcol -ON idx.table_id = idxcol.table_id AND idx.index_id = idxcol.index_id -JOIN SYS.SYSTABCOL AS col -ON idxcol.table_id = col.table_id AND idxcol.column_id = col.column_id -JOIN SYS.SYSTAB AS tbl -ON idx.table_id = tbl.table_id -WHERE tbl.table_name = %s -AND tbl.creator = USER_ID(%s) -AND idx.index_category != 2 -- exclude indexes implicitly created by foreign key constraints -ORDER BY idx.index_id ASC, idxcol.sequence ASC -SQL - , - $table, - $user - ); - } - - /** - * {@inheritdoc} - */ - public function getListTablesSQL() - { - return "SELECT tbl.table_name - FROM SYS.SYSTAB AS tbl - JOIN SYS.SYSUSER AS usr ON tbl.creator = usr.user_id - JOIN dbo.SYSOBJECTS AS obj ON tbl.object_id = obj.id - WHERE tbl.table_type IN(1, 3) -- 'BASE', 'GBL TEMP' - AND usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users - AND obj.type = 'U' -- user created tables only - ORDER BY tbl.table_name ASC"; - } - - /** - * {@inheritdoc} - * - * @todo Where is this used? Which information should be retrieved? - */ - public function getListUsersSQL() - { - return 'SELECT * FROM SYS.SYSUSER ORDER BY user_name ASC'; - } - - /** - * {@inheritdoc} - */ - public function getListViewsSQL($database) - { - return "SELECT tbl.table_name, v.view_def - FROM SYS.SYSVIEW v - JOIN SYS.SYSTAB tbl ON v.view_object_id = tbl.object_id - JOIN SYS.SYSUSER usr ON tbl.creator = usr.user_id - JOIN dbo.SYSOBJECTS obj ON tbl.object_id = obj.id - WHERE usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users - ORDER BY tbl.table_name ASC"; - } - - /** - * {@inheritdoc} - */ - public function getLocateExpression($str, $substr, $startPos = false) - { - if ($startPos === false) { - return 'LOCATE(' . $str . ', ' . $substr . ')'; - } - - return 'LOCATE(' . $str . ', ' . $substr . ', ' . $startPos . ')'; - } - - /** - * {@inheritdoc} - */ - public function getMaxIdentifierLength() - { - return 128; - } - - /** - * {@inheritdoc} - */ - public function getMd5Expression($column) - { - return 'HASH(' . $column . ", 'MD5')"; - } - - /** - * {@inheritdoc} - */ - public function getName() - { - return 'sqlanywhere'; - } - - /** - * Obtain DBMS specific SQL code portion needed to set a primary key - * declaration to be used in statements like ALTER TABLE. - * - * @param Index $index Index definition - * @param string $name Name of the primary key - * - * @return string DBMS specific SQL code portion needed to set a primary key - * - * @throws InvalidArgumentException If the given index is not a primary key. - */ - public function getPrimaryKeyDeclarationSQL(Index $index, $name = null) - { - if (! $index->isPrimary()) { - throw new InvalidArgumentException( - 'Can only create primary key declarations with getPrimaryKeyDeclarationSQL()' - ); - } - - return $this->getTableConstraintDeclarationSQL($index, $name); - } - - /** - * {@inheritdoc} - */ - public function getSetTransactionIsolationSQL($level) - { - return 'SET TEMPORARY OPTION isolation_level = ' . $this->_getTransactionIsolationLevelSQL($level); - } - - /** - * {@inheritdoc} - */ - public function getSmallIntTypeDeclarationSQL(array $column) - { - $column['integer_type'] = 'SMALLINT'; - - return $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * Returns the SQL statement for starting an existing database. - * - * In SQL Anywhere you can start and stop databases on a - * database server instance. - * This is a required statement after having created a new database - * as it has to be explicitly started to be usable. - * SQL Anywhere does not automatically start a database after creation! - * - * @param string $database Name of the database to start. - * - * @return string - */ - public function getStartDatabaseSQL($database) - { - $database = new Identifier($database); - - return "START DATABASE '" . $database->getName() . "' AUTOSTOP OFF"; - } - - /** - * Returns the SQL statement for stopping a running database. - * - * In SQL Anywhere you can start and stop databases on a - * database server instance. - * This is a required statement before dropping an existing database - * as it has to be explicitly stopped before it can be dropped. - * - * @param string $database Name of the database to stop. - * - * @return string - */ - public function getStopDatabaseSQL($database) - { - $database = new Identifier($database); - - return 'STOP DATABASE "' . $database->getName() . '" UNCONDITIONALLY'; - } - - /** - * {@inheritdoc} - */ - public function getSubstringExpression($string, $start, $length = null) - { - if ($length === null) { - return 'SUBSTRING(' . $string . ', ' . $start . ')'; - } - - return 'SUBSTRING(' . $string . ', ' . $start . ', ' . $length . ')'; - } - - /** - * {@inheritdoc} - */ - public function getTemporaryTableSQL() - { - return 'GLOBAL TEMPORARY'; - } - - /** - * {@inheritdoc} - */ - public function getTimeFormatString() - { - return 'H:i:s.u'; - } - - /** - * {@inheritdoc} - */ - public function getTimeTypeDeclarationSQL(array $column) - { - return 'TIME'; - } - - /** - * {@inheritdoc} - */ - public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false) - { - if (! $char) { - switch ($mode) { - case TrimMode::LEADING: - return $this->getLtrimExpression($str); - - case TrimMode::TRAILING: - return $this->getRtrimExpression($str); - - default: - return 'TRIM(' . $str . ')'; - } - } - - $pattern = "'%[^' + " . $char . " + ']%'"; - - switch ($mode) { - case TrimMode::LEADING: - return 'SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))'; - - case TrimMode::TRAILING: - return 'REVERSE(SUBSTR(REVERSE(' . $str . '), PATINDEX(' . $pattern . ', REVERSE(' . $str . '))))'; - - default: - return 'REVERSE(SUBSTR(REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))), ' . - 'PATINDEX(' . $pattern . ', ' . - 'REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))))))'; - } - } - - /** - * {@inheritdoc} - */ - public function getTruncateTableSQL($tableName, $cascade = false) - { - $tableIdentifier = new Identifier($tableName); - - return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this); - } - - /** - * {@inheritdoc} - */ - public function getUniqueConstraintDeclarationSQL($name, Index $index) - { - if ($index->isPrimary()) { - throw new InvalidArgumentException( - 'Cannot create primary key constraint declarations with getUniqueConstraintDeclarationSQL().' - ); - } - - if (! $index->isUnique()) { - throw new InvalidArgumentException( - 'Can only create unique constraint declarations, no common index declarations with ' . - 'getUniqueConstraintDeclarationSQL().' - ); - } - - return $this->getTableConstraintDeclarationSQL($index, $name); - } - - /** - * {@inheritdoc} - */ - public function getVarcharDefaultLength() - { - return 1; - } - - /** - * {@inheritdoc} - */ - public function getVarcharMaxLength() - { - return 32767; - } - - /** - * {@inheritdoc} - */ - public function hasNativeGuidType() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function prefersIdentityColumns() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function supportsCommentOnStatement() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function supportsIdentityColumns() - { - return true; - } - - /** - * {@inheritdoc} - */ - protected function _getCommonIntegerTypeDeclarationSQL(array $column) - { - $unsigned = ! empty($column['unsigned']) ? 'UNSIGNED ' : ''; - $autoincrement = ! empty($column['autoincrement']) ? ' IDENTITY' : ''; - - return $unsigned . $column['integer_type'] . $autoincrement; - } - - /** - * {@inheritdoc} - */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) - { - $columnListSql = $this->getColumnDeclarationListSQL($columns); - $indexSql = []; - - if (! empty($options['uniqueConstraints'])) { - foreach ((array) $options['uniqueConstraints'] as $name => $definition) { - $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); - } - } - - if (! empty($options['indexes'])) { - foreach ((array) $options['indexes'] as $index) { - assert($index instanceof Index); - $indexSql[] = $this->getCreateIndexSQL($index, $name); - } - } - - if (! empty($options['primary'])) { - $flags = ''; - - if (isset($options['primary_index']) && $options['primary_index']->hasFlag('clustered')) { - $flags = ' CLUSTERED '; - } - - $columnListSql .= ', PRIMARY KEY' . $flags - . ' (' . implode(', ', array_unique(array_values((array) $options['primary']))) . ')'; - } - - if (! empty($options['foreignKeys'])) { - foreach ((array) $options['foreignKeys'] as $definition) { - $columnListSql .= ', ' . $this->getForeignKeyDeclarationSQL($definition); - } - } - - $query = 'CREATE TABLE ' . $name . ' (' . $columnListSql; - $check = $this->getCheckDeclarationSQL($columns); - - if (! empty($check)) { - $query .= ', ' . $check; - } - - $query .= ')'; - - return array_merge([$query], $indexSql); - } - - /** - * {@inheritdoc} - */ - protected function _getTransactionIsolationLevelSQL($level) - { - switch ($level) { - case TransactionIsolationLevel::READ_UNCOMMITTED: - return '0'; - - case TransactionIsolationLevel::READ_COMMITTED: - return '1'; - - case TransactionIsolationLevel::REPEATABLE_READ: - return '2'; - - case TransactionIsolationLevel::SERIALIZABLE: - return '3'; - - default: - throw new InvalidArgumentException('Invalid isolation level:' . $level); - } - } - - /** - * {@inheritdoc} - */ - protected function doModifyLimitQuery($query, $limit, $offset) - { - $limitOffsetClause = $this->getTopClauseSQL($limit, $offset); - - if ($limitOffsetClause === '') { - return $query; - } - - if (! preg_match('/^\s*(SELECT\s+(DISTINCT\s+)?)(.*)/i', $query, $matches)) { - return $query; - } - - return $matches[1] . $limitOffsetClause . ' ' . $matches[3]; - } - - private function getTopClauseSQL(?int $limit, ?int $offset): string - { - if ($offset > 0) { - return sprintf('TOP %s START AT %d', $limit ?? 'ALL', $offset + 1); - } - - return $limit === null ? '' : 'TOP ' . $limit; - } - - /** - * Return the INDEX query section dealing with non-standard - * SQL Anywhere options. - * - * @param Index $index Index definition - * - * @return string - */ - protected function getAdvancedIndexOptionsSQL(Index $index) - { - $sql = ''; - - if (! $index->isPrimary() && $index->hasFlag('for_olap_workload')) { - $sql .= ' FOR OLAP WORKLOAD'; - } - - return $sql; - } - - /** - * {@inheritdoc} - */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) - { - return $fixed - ? 'BINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')' - : 'VARBINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')'; - } - - /** - * Returns the SQL snippet for creating a table constraint. - * - * @param Constraint $constraint The table constraint to create the SQL snippet for. - * @param string|null $name The table constraint name to use if any. - * - * @return string - * - * @throws InvalidArgumentException If the given table constraint type is not supported by this method. - */ - protected function getTableConstraintDeclarationSQL(Constraint $constraint, $name = null) - { - if ($constraint instanceof ForeignKeyConstraint) { - return $this->getForeignKeyDeclarationSQL($constraint); - } - - if (! $constraint instanceof Index) { - throw new InvalidArgumentException('Unsupported constraint type: ' . get_class($constraint)); - } - - if (! $constraint->isPrimary() && ! $constraint->isUnique()) { - throw new InvalidArgumentException( - 'Can only create primary, unique or foreign key constraint declarations, no common index declarations' - . ' with getTableConstraintDeclarationSQL().' - ); - } - - $constraintColumns = $constraint->getQuotedColumns($this); - - if (empty($constraintColumns)) { - throw new InvalidArgumentException("Incomplete definition. 'columns' required."); - } - - $sql = ''; - $flags = ''; - - if (! empty($name)) { - $name = new Identifier($name); - $sql .= 'CONSTRAINT ' . $name->getQuotedName($this) . ' '; - } - - if ($constraint->hasFlag('clustered')) { - $flags = 'CLUSTERED '; - } - - if ($constraint->isPrimary()) { - return $sql . 'PRIMARY KEY ' . $flags - . '(' . $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')'; - } - - return $sql . 'UNIQUE ' . $flags . '(' . $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')'; - } - - /** - * {@inheritdoc} - */ - protected function getCreateIndexSQLFlags(Index $index) - { - $type = ''; - if ($index->hasFlag('virtual')) { - $type .= 'VIRTUAL '; - } - - if ($index->isUnique()) { - $type .= 'UNIQUE '; - } - - if ($index->hasFlag('clustered')) { - $type .= 'CLUSTERED '; - } - - return $type; - } - - /** - * {@inheritdoc} - */ - protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) - { - return ['ALTER INDEX ' . $oldIndexName . ' ON ' . $tableName . ' RENAME TO ' . $index->getQuotedName($this)]; - } - - /** - * {@inheritdoc} - */ - protected function getReservedKeywordsClass() - { - return Keywords\SQLAnywhereKeywords::class; - } - - /** - * {@inheritdoc} - */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) - { - return $fixed - ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(' . $this->getVarcharDefaultLength() . ')') - : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(' . $this->getVarcharDefaultLength() . ')'); - } - - /** - * {@inheritdoc} - */ - protected function initializeDoctrineTypeMappings() - { - $this->doctrineTypeMapping = [ - 'char' => 'string', - 'long nvarchar' => 'text', - 'long varchar' => 'text', - 'nchar' => 'string', - 'ntext' => 'text', - 'nvarchar' => 'string', - 'text' => 'text', - 'uniqueidentifierstr' => 'guid', - 'varchar' => 'string', - 'xml' => 'text', - 'bigint' => 'bigint', - 'unsigned bigint' => 'bigint', - 'bit' => 'boolean', - 'decimal' => 'decimal', - 'double' => 'float', - 'float' => 'float', - 'int' => 'integer', - 'integer' => 'integer', - 'unsigned int' => 'integer', - 'numeric' => 'decimal', - 'smallint' => 'smallint', - 'unsigned smallint' => 'smallint', - 'tinyint' => 'smallint', - 'unsigned tinyint' => 'smallint', - 'money' => 'decimal', - 'smallmoney' => 'decimal', - 'long varbit' => 'text', - 'varbit' => 'string', - 'date' => 'date', - 'datetime' => 'datetime', - 'smalldatetime' => 'datetime', - 'time' => 'time', - 'timestamp' => 'datetime', - 'binary' => 'binary', - 'image' => 'blob', - 'long binary' => 'blob', - 'uniqueidentifier' => 'guid', - 'varbinary' => 'binary', - ]; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php deleted file mode 100644 index f281f4fc4..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php +++ /dev/null @@ -1,35 +0,0 @@ -hasOption('azure.federatedOnColumnName')) { - $distributionName = $table->getOption('azure.federatedOnDistributionName'); - $columnName = $table->getOption('azure.federatedOnColumnName'); - $stmt = ' FEDERATED ON (' . $distributionName . ' = ' . $columnName . ')'; - - $sql[0] .= $stmt; - } - - return $sql; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php deleted file mode 100644 index 6f02bb8ff..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php +++ /dev/null @@ -1,48 +0,0 @@ -doctrineTypeMapping['datetime2'] = 'datetime'; - $this->doctrineTypeMapping['date'] = 'date'; - $this->doctrineTypeMapping['time'] = 'time'; - $this->doctrineTypeMapping['datetimeoffset'] = 'datetimetz'; - } - - /** - * {@inheritdoc} - * - * Returns Microsoft SQL Server 2008 specific keywords class - */ - protected function getReservedKeywordsClass() - { - return Keywords\SQLServer2008Keywords::class; - } - - protected function getLikeWildcardCharacters(): string - { - return parent::getLikeWildcardCharacters() . '[]^'; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php deleted file mode 100644 index 0bd70d31f..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php +++ /dev/null @@ -1,165 +0,0 @@ -getQuotedName($this) . - ' INCREMENT BY ' . $sequence->getAllocationSize(); - } - - /** - * {@inheritdoc} - */ - public function getCreateSequenceSQL(Sequence $sequence) - { - return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . - ' START WITH ' . $sequence->getInitialValue() . - ' INCREMENT BY ' . $sequence->getAllocationSize() . - ' MINVALUE ' . $sequence->getInitialValue(); - } - - /** - * {@inheritdoc} - */ - public function getDropSequenceSQL($sequence) - { - if ($sequence instanceof Sequence) { - $sequence = $sequence->getQuotedName($this); - } - - return 'DROP SEQUENCE ' . $sequence; - } - - /** - * {@inheritdoc} - */ - public function getListSequencesSQL($database) - { - return 'SELECT seq.name, - CAST( - seq.increment AS VARCHAR(MAX) - ) AS increment, -- CAST avoids driver error for sql_variant type - CAST( - seq.start_value AS VARCHAR(MAX) - ) AS start_value -- CAST avoids driver error for sql_variant type - FROM sys.sequences AS seq'; - } - - /** - * {@inheritdoc} - */ - public function getSequenceNextValSQL($sequence) - { - return 'SELECT NEXT VALUE FOR ' . $sequence; - } - - /** - * {@inheritdoc} - */ - public function supportsSequences() - { - return true; - } - - /** - * {@inheritdoc} - * - * Returns Microsoft SQL Server 2012 specific keywords class - */ - protected function getReservedKeywordsClass() - { - return Keywords\SQLServer2012Keywords::class; - } - - /** - * {@inheritdoc} - */ - protected function doModifyLimitQuery($query, $limit, $offset = null) - { - if ($limit === null && $offset <= 0) { - return $query; - } - - // Queries using OFFSET... FETCH MUST have an ORDER BY clause - if ($this->shouldAddOrderBy($query)) { - if (preg_match('/^SELECT\s+DISTINCT/im', $query)) { - // SQL Server won't let us order by a non-selected column in a DISTINCT query, - // so we have to do this madness. This says, order by the first column in the - // result. SQL Server's docs say that a nonordered query's result order is non- - // deterministic anyway, so this won't do anything that a bunch of update and - // deletes to the table wouldn't do anyway. - $query .= ' ORDER BY 1'; - } else { - // In another DBMS, we could do ORDER BY 0, but SQL Server gets angry if you - // use constant expressions in the order by list. - $query .= ' ORDER BY (SELECT 0)'; - } - } - - if ($offset === null) { - $offset = 0; - } - - // This looks somewhat like MYSQL, but limit/offset are in inverse positions - // Supposedly SQL:2008 core standard. - // Per TSQL spec, FETCH NEXT n ROWS ONLY is not valid without OFFSET n ROWS. - $query .= ' OFFSET ' . (int) $offset . ' ROWS'; - - if ($limit !== null) { - $query .= ' FETCH NEXT ' . (int) $limit . ' ROWS ONLY'; - } - - return $query; - } - - /** - * @param string $query - */ - private function shouldAddOrderBy($query): bool - { - // Find the position of the last instance of ORDER BY and ensure it is not within a parenthetical statement - // but can be in a newline - $matches = []; - $matchesCount = preg_match_all('/[\\s]+order\\s+by\\s/im', $query, $matches, PREG_OFFSET_CAPTURE); - if ($matchesCount === 0) { - return true; - } - - // ORDER BY instance may be in a subquery after ORDER BY - // e.g. SELECT col1 FROM test ORDER BY (SELECT col2 from test ORDER BY col2) - // if in the searched query ORDER BY clause was found where - // number of open parentheses after the occurrence of the clause is equal to - // number of closed brackets after the occurrence of the clause, - // it means that ORDER BY is included in the query being checked - while ($matchesCount > 0) { - $orderByPos = $matches[0][--$matchesCount][1]; - $openBracketsCount = substr_count($query, '(', $orderByPos); - $closedBracketsCount = substr_count($query, ')', $orderByPos); - if ($openBracketsCount === $closedBracketsCount) { - return false; - } - } - - return true; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php deleted file mode 100644 index 2b9580186..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php +++ /dev/null @@ -1,1718 +0,0 @@ -getConvertExpression('date', 'GETDATE()'); - } - - /** - * {@inheritdoc} - */ - public function getCurrentTimeSQL() - { - return $this->getConvertExpression('time', 'GETDATE()'); - } - - /** - * Returns an expression that converts an expression of one data type to another. - * - * @param string $dataType The target native data type. Alias data types cannot be used. - * @param string $expression The SQL expression to convert. - * - * @return string - */ - private function getConvertExpression($dataType, $expression) - { - return sprintf('CONVERT(%s, %s)', $dataType, $expression); - } - - /** - * {@inheritdoc} - */ - protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) - { - $factorClause = ''; - - if ($operator === '-') { - $factorClause = '-1 * '; - } - - return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')'; - } - - /** - * {@inheritDoc} - */ - public function getDateDiffExpression($date1, $date2) - { - return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')'; - } - - /** - * {@inheritDoc} - * - * Microsoft SQL Server prefers "autoincrement" identity columns - * since sequences can only be emulated with a table. - */ - public function prefersIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - * - * Microsoft SQL Server supports this through AUTO_INCREMENT columns. - */ - public function supportsIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function supportsReleaseSavepoints() - { - return false; - } - - /** - * {@inheritdoc} - */ - public function supportsSchemas() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function getDefaultSchemaName() - { - return 'dbo'; - } - - /** - * {@inheritDoc} - */ - public function supportsColumnCollation() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function hasNativeGuidType() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function getCreateDatabaseSQL($name) - { - return 'CREATE DATABASE ' . $name; - } - - /** - * {@inheritDoc} - */ - public function getDropDatabaseSQL($name) - { - return 'DROP DATABASE ' . $name; - } - - /** - * {@inheritDoc} - */ - public function supportsCreateDropDatabase() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function getCreateSchemaSQL($schemaName) - { - return 'CREATE SCHEMA ' . $schemaName; - } - - /** - * {@inheritDoc} - */ - public function getDropForeignKeySQL($foreignKey, $table) - { - if (! $foreignKey instanceof ForeignKeyConstraint) { - $foreignKey = new Identifier($foreignKey); - } - - if (! $table instanceof Table) { - $table = new Identifier($table); - } - - $foreignKey = $foreignKey->getQuotedName($this); - $table = $table->getQuotedName($this); - - return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; - } - - /** - * {@inheritDoc} - */ - public function getDropIndexSQL($index, $table = null) - { - if ($index instanceof Index) { - $index = $index->getQuotedName($this); - } elseif (! is_string($index)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.' - ); - } - - if (! isset($table)) { - return 'DROP INDEX ' . $index; - } - - if ($table instanceof Table) { - $table = $table->getQuotedName($this); - } - - return sprintf( - " - IF EXISTS (SELECT * FROM sysobjects WHERE name = '%s') - ALTER TABLE %s DROP CONSTRAINT %s - ELSE - DROP INDEX %s ON %s - ", - $index, - $table, - $index, - $index, - $table - ); - } - - /** - * {@inheritDoc} - */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) - { - $defaultConstraintsSql = []; - $commentsSql = []; - - $tableComment = $options['comment'] ?? null; - if ($tableComment !== null) { - $commentsSql[] = $this->getCommentOnTableSQL($name, $tableComment); - } - - // @todo does other code breaks because of this? - // force primary keys to be not null - foreach ($columns as &$column) { - if (isset($column['primary']) && $column['primary']) { - $column['notnull'] = true; - } - - // Build default constraints SQL statements. - if (isset($column['default'])) { - $defaultConstraintsSql[] = 'ALTER TABLE ' . $name . - ' ADD' . $this->getDefaultConstraintDeclarationSQL($name, $column); - } - - if (empty($column['comment']) && ! is_numeric($column['comment'])) { - continue; - } - - $commentsSql[] = $this->getCreateColumnCommentSQL($name, $column['name'], $column['comment']); - } - - $columnListSql = $this->getColumnDeclarationListSQL($columns); - - if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { - foreach ($options['uniqueConstraints'] as $name => $definition) { - $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); - } - } - - if (isset($options['primary']) && ! empty($options['primary'])) { - $flags = ''; - if (isset($options['primary_index']) && $options['primary_index']->hasFlag('nonclustered')) { - $flags = ' NONCLUSTERED'; - } - - $columnListSql .= ', PRIMARY KEY' . $flags - . ' (' . implode(', ', array_unique(array_values($options['primary']))) . ')'; - } - - $query = 'CREATE TABLE ' . $name . ' (' . $columnListSql; - - $check = $this->getCheckDeclarationSQL($columns); - if (! empty($check)) { - $query .= ', ' . $check; - } - - $query .= ')'; - - $sql = [$query]; - - if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach ($options['indexes'] as $index) { - $sql[] = $this->getCreateIndexSQL($index, $name); - } - } - - if (isset($options['foreignKeys'])) { - foreach ((array) $options['foreignKeys'] as $definition) { - $sql[] = $this->getCreateForeignKeySQL($definition, $name); - } - } - - return array_merge($sql, $commentsSql, $defaultConstraintsSql); - } - - /** - * {@inheritDoc} - */ - public function getCreatePrimaryKeySQL(Index $index, $table) - { - if ($table instanceof Table) { - $identifier = $table->getQuotedName($this); - } else { - $identifier = $table; - } - - $sql = 'ALTER TABLE ' . $identifier . ' ADD PRIMARY KEY'; - - if ($index->hasFlag('nonclustered')) { - $sql .= ' NONCLUSTERED'; - } - - return $sql . ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')'; - } - - /** - * Returns the SQL statement for creating a column comment. - * - * SQL Server does not support native column comments, - * therefore the extended properties functionality is used - * as a workaround to store them. - * The property name used to store column comments is "MS_Description" - * which provides compatibility with SQL Server Management Studio, - * as column comments are stored in the same property there when - * specifying a column's "Description" attribute. - * - * @param string $tableName The quoted table name to which the column belongs. - * @param string $columnName The quoted column name to create the comment for. - * @param string|null $comment The column's comment. - * - * @return string - */ - protected function getCreateColumnCommentSQL($tableName, $columnName, $comment) - { - if (strpos($tableName, '.') !== false) { - [$schemaSQL, $tableSQL] = explode('.', $tableName); - $schemaSQL = $this->quoteStringLiteral($schemaSQL); - $tableSQL = $this->quoteStringLiteral($tableSQL); - } else { - $schemaSQL = "'dbo'"; - $tableSQL = $this->quoteStringLiteral($tableName); - } - - return $this->getAddExtendedPropertySQL( - 'MS_Description', - $comment, - 'SCHEMA', - $schemaSQL, - 'TABLE', - $tableSQL, - 'COLUMN', - $columnName - ); - } - - /** - * Returns the SQL snippet for declaring a default constraint. - * - * @param string $table Name of the table to return the default constraint declaration for. - * @param mixed[] $column Column definition. - * - * @return string - * - * @throws InvalidArgumentException - */ - public function getDefaultConstraintDeclarationSQL($table, array $column) - { - if (! isset($column['default'])) { - throw new InvalidArgumentException("Incomplete column definition. 'default' required."); - } - - $columnName = new Identifier($column['name']); - - return ' CONSTRAINT ' . - $this->generateDefaultConstraintName($table, $column['name']) . - $this->getDefaultValueDeclarationSQL($column) . - ' FOR ' . $columnName->getQuotedName($this); - } - - /** - * {@inheritDoc} - */ - public function getUniqueConstraintDeclarationSQL($name, Index $index) - { - $constraint = parent::getUniqueConstraintDeclarationSQL($name, $index); - - $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); - - return $constraint; - } - - /** - * {@inheritDoc} - */ - public function getCreateIndexSQL(Index $index, $table) - { - $constraint = parent::getCreateIndexSQL($index, $table); - - if ($index->isUnique() && ! $index->isPrimary()) { - $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); - } - - return $constraint; - } - - /** - * {@inheritDoc} - */ - protected function getCreateIndexSQLFlags(Index $index) - { - $type = ''; - if ($index->isUnique()) { - $type .= 'UNIQUE '; - } - - if ($index->hasFlag('clustered')) { - $type .= 'CLUSTERED '; - } elseif ($index->hasFlag('nonclustered')) { - $type .= 'NONCLUSTERED '; - } - - return $type; - } - - /** - * Extend unique key constraint with required filters - * - * @param string $sql - * - * @return string - */ - private function _appendUniqueConstraintDefinition($sql, Index $index) - { - $fields = []; - - foreach ($index->getQuotedColumns($this) as $field) { - $fields[] = $field . ' IS NOT NULL'; - } - - return $sql . ' WHERE ' . implode(' AND ', $fields); - } - - /** - * {@inheritDoc} - */ - public function getAlterTableSQL(TableDiff $diff) - { - $queryParts = []; - $sql = []; - $columnSql = []; - $commentsSql = []; - - foreach ($diff->addedColumns as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { - continue; - } - - $columnDef = $column->toArray(); - $addColumnSql = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); - if (isset($columnDef['default'])) { - $addColumnSql .= ' CONSTRAINT ' . - $this->generateDefaultConstraintName($diff->name, $column->getQuotedName($this)) . - $this->getDefaultValueDeclarationSQL($columnDef); - } - - $queryParts[] = $addColumnSql; - - $comment = $this->getColumnComment($column); - - if (empty($comment) && ! is_numeric($comment)) { - continue; - } - - $commentsSql[] = $this->getCreateColumnCommentSQL( - $diff->name, - $column->getQuotedName($this), - $comment - ); - } - - foreach ($diff->removedColumns as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { - continue; - } - - $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); - } - - foreach ($diff->changedColumns as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - - $column = $columnDiff->column; - $comment = $this->getColumnComment($column); - $hasComment = ! empty($comment) || is_numeric($comment); - - if ($columnDiff->fromColumn instanceof Column) { - $fromComment = $this->getColumnComment($columnDiff->fromColumn); - $hasFromComment = ! empty($fromComment) || is_numeric($fromComment); - - if ($hasFromComment && $hasComment && $fromComment !== $comment) { - $commentsSql[] = $this->getAlterColumnCommentSQL( - $diff->name, - $column->getQuotedName($this), - $comment - ); - } elseif ($hasFromComment && ! $hasComment) { - $commentsSql[] = $this->getDropColumnCommentSQL($diff->name, $column->getQuotedName($this)); - } elseif (! $hasFromComment && $hasComment) { - $commentsSql[] = $this->getCreateColumnCommentSQL( - $diff->name, - $column->getQuotedName($this), - $comment - ); - } - } - - // Do not add query part if only comment has changed. - if ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1) { - continue; - } - - $requireDropDefaultConstraint = $this->alterColumnRequiresDropDefaultConstraint($columnDiff); - - if ($requireDropDefaultConstraint) { - $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( - $diff->name, - $columnDiff->oldColumnName - ); - } - - $columnDef = $column->toArray(); - - $queryParts[] = 'ALTER COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); - - if ( - ! isset($columnDef['default']) - || (! $requireDropDefaultConstraint && ! $columnDiff->hasChanged('default')) - ) { - continue; - } - - $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); - } - - foreach ($diff->renamedColumns as $oldColumnName => $column) { - if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { - continue; - } - - $oldColumnName = new Identifier($oldColumnName); - - $sql[] = "sp_rename '" . - $diff->getName($this)->getQuotedName($this) . '.' . $oldColumnName->getQuotedName($this) . - "', '" . $column->getQuotedName($this) . "', 'COLUMN'"; - - // Recreate default constraint with new column name if necessary (for future reference). - if ($column->getDefault() === null) { - continue; - } - - $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( - $diff->name, - $oldColumnName->getQuotedName($this) - ); - $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); - } - - $tableSql = []; - - if ($this->onSchemaAlterTable($diff, $tableSql)) { - return array_merge($tableSql, $columnSql); - } - - foreach ($queryParts as $query) { - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; - } - - $sql = array_merge($sql, $commentsSql); - - $newName = $diff->getNewName(); - - if ($newName !== false) { - $sql[] = "sp_rename '" . $diff->getName($this)->getQuotedName($this) . "', '" . $newName->getName() . "'"; - - /** - * Rename table's default constraints names - * to match the new table name. - * This is necessary to ensure that the default - * constraints can be referenced in future table - * alterations as the table name is encoded in - * default constraints' names. - */ - $sql[] = "DECLARE @sql NVARCHAR(MAX) = N''; " . - "SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' " . - "+ REPLACE(dc.name, '" . $this->generateIdentifierName($diff->name) . "', " . - "'" . $this->generateIdentifierName($newName->getName()) . "') + ''', ''OBJECT'';' " . - 'FROM sys.default_constraints dc ' . - 'JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id ' . - "WHERE tbl.name = '" . $newName->getName() . "';" . - 'EXEC sp_executesql @sql'; - } - - $sql = array_merge( - $this->getPreAlterTableIndexForeignKeySQL($diff), - $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff) - ); - - return array_merge($sql, $tableSql, $columnSql); - } - - /** - * Returns the SQL clause for adding a default constraint in an ALTER TABLE statement. - * - * @param string $tableName The name of the table to generate the clause for. - * @param Column $column The column to generate the clause for. - * - * @return string - */ - private function getAlterTableAddDefaultConstraintClause($tableName, Column $column) - { - $columnDef = $column->toArray(); - $columnDef['name'] = $column->getQuotedName($this); - - return 'ADD' . $this->getDefaultConstraintDeclarationSQL($tableName, $columnDef); - } - - /** - * Returns the SQL clause for dropping an existing default constraint in an ALTER TABLE statement. - * - * @param string $tableName The name of the table to generate the clause for. - * @param string $columnName The name of the column to generate the clause for. - * - * @return string - */ - private function getAlterTableDropDefaultConstraintClause($tableName, $columnName) - { - return 'DROP CONSTRAINT ' . $this->generateDefaultConstraintName($tableName, $columnName); - } - - /** - * Checks whether a column alteration requires dropping its default constraint first. - * - * Different to other database vendors SQL Server implements column default values - * as constraints and therefore changes in a column's default value as well as changes - * in a column's type require dropping the default constraint first before being to - * alter the particular column to the new definition. - * - * @param ColumnDiff $columnDiff The column diff to evaluate. - * - * @return bool True if the column alteration requires dropping its default constraint first, false otherwise. - */ - private function alterColumnRequiresDropDefaultConstraint(ColumnDiff $columnDiff) - { - // We can only decide whether to drop an existing default constraint - // if we know the original default value. - if (! $columnDiff->fromColumn instanceof Column) { - return false; - } - - // We only need to drop an existing default constraint if we know the - // column was defined with a default value before. - if ($columnDiff->fromColumn->getDefault() === null) { - return false; - } - - // We need to drop an existing default constraint if the column was - // defined with a default value before and it has changed. - if ($columnDiff->hasChanged('default')) { - return true; - } - - // We need to drop an existing default constraint if the column was - // defined with a default value before and the native column type has changed. - return $columnDiff->hasChanged('type') || $columnDiff->hasChanged('fixed'); - } - - /** - * Returns the SQL statement for altering a column comment. - * - * SQL Server does not support native column comments, - * therefore the extended properties functionality is used - * as a workaround to store them. - * The property name used to store column comments is "MS_Description" - * which provides compatibility with SQL Server Management Studio, - * as column comments are stored in the same property there when - * specifying a column's "Description" attribute. - * - * @param string $tableName The quoted table name to which the column belongs. - * @param string $columnName The quoted column name to alter the comment for. - * @param string|null $comment The column's comment. - * - * @return string - */ - protected function getAlterColumnCommentSQL($tableName, $columnName, $comment) - { - if (strpos($tableName, '.') !== false) { - [$schemaSQL, $tableSQL] = explode('.', $tableName); - $schemaSQL = $this->quoteStringLiteral($schemaSQL); - $tableSQL = $this->quoteStringLiteral($tableSQL); - } else { - $schemaSQL = "'dbo'"; - $tableSQL = $this->quoteStringLiteral($tableName); - } - - return $this->getUpdateExtendedPropertySQL( - 'MS_Description', - $comment, - 'SCHEMA', - $schemaSQL, - 'TABLE', - $tableSQL, - 'COLUMN', - $columnName - ); - } - - /** - * Returns the SQL statement for dropping a column comment. - * - * SQL Server does not support native column comments, - * therefore the extended properties functionality is used - * as a workaround to store them. - * The property name used to store column comments is "MS_Description" - * which provides compatibility with SQL Server Management Studio, - * as column comments are stored in the same property there when - * specifying a column's "Description" attribute. - * - * @param string $tableName The quoted table name to which the column belongs. - * @param string $columnName The quoted column name to drop the comment for. - * - * @return string - */ - protected function getDropColumnCommentSQL($tableName, $columnName) - { - if (strpos($tableName, '.') !== false) { - [$schemaSQL, $tableSQL] = explode('.', $tableName); - $schemaSQL = $this->quoteStringLiteral($schemaSQL); - $tableSQL = $this->quoteStringLiteral($tableSQL); - } else { - $schemaSQL = "'dbo'"; - $tableSQL = $this->quoteStringLiteral($tableName); - } - - return $this->getDropExtendedPropertySQL( - 'MS_Description', - 'SCHEMA', - $schemaSQL, - 'TABLE', - $tableSQL, - 'COLUMN', - $columnName - ); - } - - /** - * {@inheritdoc} - */ - protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) - { - return [sprintf( - "EXEC sp_rename N'%s.%s', N'%s', N'INDEX'", - $tableName, - $oldIndexName, - $index->getQuotedName($this) - ), - ]; - } - - /** - * Returns the SQL statement for adding an extended property to a database object. - * - * @link http://msdn.microsoft.com/en-us/library/ms180047%28v=sql.90%29.aspx - * - * @param string $name The name of the property to add. - * @param string|null $value The value of the property to add. - * @param string|null $level0Type The type of the object at level 0 the property belongs to. - * @param string|null $level0Name The name of the object at level 0 the property belongs to. - * @param string|null $level1Type The type of the object at level 1 the property belongs to. - * @param string|null $level1Name The name of the object at level 1 the property belongs to. - * @param string|null $level2Type The type of the object at level 2 the property belongs to. - * @param string|null $level2Name The name of the object at level 2 the property belongs to. - * - * @return string - */ - public function getAddExtendedPropertySQL( - $name, - $value = null, - $level0Type = null, - $level0Name = null, - $level1Type = null, - $level1Name = null, - $level2Type = null, - $level2Name = null - ) { - return 'EXEC sp_addextendedproperty ' . - 'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value) . ', ' . - 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; - } - - /** - * Returns the SQL statement for dropping an extended property from a database object. - * - * @link http://technet.microsoft.com/en-gb/library/ms178595%28v=sql.90%29.aspx - * - * @param string $name The name of the property to drop. - * @param string|null $level0Type The type of the object at level 0 the property belongs to. - * @param string|null $level0Name The name of the object at level 0 the property belongs to. - * @param string|null $level1Type The type of the object at level 1 the property belongs to. - * @param string|null $level1Name The name of the object at level 1 the property belongs to. - * @param string|null $level2Type The type of the object at level 2 the property belongs to. - * @param string|null $level2Name The name of the object at level 2 the property belongs to. - * - * @return string - */ - public function getDropExtendedPropertySQL( - $name, - $level0Type = null, - $level0Name = null, - $level1Type = null, - $level1Name = null, - $level2Type = null, - $level2Name = null - ) { - return 'EXEC sp_dropextendedproperty ' . - 'N' . $this->quoteStringLiteral($name) . ', ' . - 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; - } - - /** - * Returns the SQL statement for updating an extended property of a database object. - * - * @link http://msdn.microsoft.com/en-us/library/ms186885%28v=sql.90%29.aspx - * - * @param string $name The name of the property to update. - * @param string|null $value The value of the property to update. - * @param string|null $level0Type The type of the object at level 0 the property belongs to. - * @param string|null $level0Name The name of the object at level 0 the property belongs to. - * @param string|null $level1Type The type of the object at level 1 the property belongs to. - * @param string|null $level1Name The name of the object at level 1 the property belongs to. - * @param string|null $level2Type The type of the object at level 2 the property belongs to. - * @param string|null $level2Name The name of the object at level 2 the property belongs to. - * - * @return string - */ - public function getUpdateExtendedPropertySQL( - $name, - $value = null, - $level0Type = null, - $level0Name = null, - $level1Type = null, - $level1Name = null, - $level2Type = null, - $level2Name = null - ) { - return 'EXEC sp_updateextendedproperty ' . - 'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value) . ', ' . - 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; - } - - /** - * {@inheritDoc} - */ - public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) - { - return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES'; - } - - /** - * {@inheritDoc} - */ - public function getListTablesSQL() - { - // "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams - // Category 2 must be ignored as it is "MS SQL Server 'pseudo-system' object[s]" for replication - return "SELECT name FROM sysobjects WHERE type = 'U' AND name != 'sysdiagrams' AND category != 2 ORDER BY name"; - } - - /** - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - return "SELECT col.name, - type.name AS type, - col.max_length AS length, - ~col.is_nullable AS notnull, - def.definition AS [default], - col.scale, - col.precision, - col.is_identity AS autoincrement, - col.collation_name AS collation, - CAST(prop.value AS NVARCHAR(MAX)) AS comment -- CAST avoids driver error for sql_variant type - FROM sys.columns AS col - JOIN sys.types AS type - ON col.user_type_id = type.user_type_id - JOIN sys.objects AS obj - ON col.object_id = obj.object_id - JOIN sys.schemas AS scm - ON obj.schema_id = scm.schema_id - LEFT JOIN sys.default_constraints def - ON col.default_object_id = def.object_id - AND col.object_id = def.parent_object_id - LEFT JOIN sys.extended_properties AS prop - ON obj.object_id = prop.major_id - AND col.column_id = prop.minor_id - AND prop.name = 'MS_Description' - WHERE obj.type = 'U' - AND " . $this->getTableWhereClause($table, 'scm.name', 'obj.name'); - } - - /** - * @param string $table - * @param string|null $database - * - * @return string - */ - public function getListTableForeignKeysSQL($table, $database = null) - { - return 'SELECT f.name AS ForeignKey, - SCHEMA_NAME (f.SCHEMA_ID) AS SchemaName, - OBJECT_NAME (f.parent_object_id) AS TableName, - COL_NAME (fc.parent_object_id,fc.parent_column_id) AS ColumnName, - SCHEMA_NAME (o.SCHEMA_ID) ReferenceSchemaName, - OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName, - COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS ReferenceColumnName, - f.delete_referential_action_desc, - f.update_referential_action_desc - FROM sys.foreign_keys AS f - INNER JOIN sys.foreign_key_columns AS fc - INNER JOIN sys.objects AS o ON o.OBJECT_ID = fc.referenced_object_id - ON f.OBJECT_ID = fc.constraint_object_id - WHERE ' . - $this->getTableWhereClause($table, 'SCHEMA_NAME (f.schema_id)', 'OBJECT_NAME (f.parent_object_id)'); - } - - /** - * {@inheritDoc} - */ - public function getListTableIndexesSQL($table, $database = null) - { - return "SELECT idx.name AS key_name, - col.name AS column_name, - ~idx.is_unique AS non_unique, - idx.is_primary_key AS [primary], - CASE idx.type - WHEN '1' THEN 'clustered' - WHEN '2' THEN 'nonclustered' - ELSE NULL - END AS flags - FROM sys.tables AS tbl - JOIN sys.schemas AS scm ON tbl.schema_id = scm.schema_id - JOIN sys.indexes AS idx ON tbl.object_id = idx.object_id - JOIN sys.index_columns AS idxcol ON idx.object_id = idxcol.object_id AND idx.index_id = idxcol.index_id - JOIN sys.columns AS col ON idxcol.object_id = col.object_id AND idxcol.column_id = col.column_id - WHERE " . $this->getTableWhereClause($table, 'scm.name', 'tbl.name') . ' - ORDER BY idx.index_id ASC, idxcol.key_ordinal ASC'; - } - - /** - * {@inheritDoc} - */ - public function getCreateViewSQL($name, $sql) - { - return 'CREATE VIEW ' . $name . ' AS ' . $sql; - } - - /** - * {@inheritDoc} - */ - public function getListViewsSQL($database) - { - return "SELECT name FROM sysobjects WHERE type = 'V' ORDER BY name"; - } - - /** - * Returns the where clause to filter schema and table name in a query. - * - * @param string $table The full qualified name of the table. - * @param string $schemaColumn The name of the column to compare the schema to in the where clause. - * @param string $tableColumn The name of the column to compare the table to in the where clause. - * - * @return string - */ - private function getTableWhereClause($table, $schemaColumn, $tableColumn) - { - if (strpos($table, '.') !== false) { - [$schema, $table] = explode('.', $table); - $schema = $this->quoteStringLiteral($schema); - $table = $this->quoteStringLiteral($table); - } else { - $schema = 'SCHEMA_NAME()'; - $table = $this->quoteStringLiteral($table); - } - - return sprintf('(%s = %s AND %s = %s)', $tableColumn, $table, $schemaColumn, $schema); - } - - /** - * {@inheritDoc} - */ - public function getDropViewSQL($name) - { - return 'DROP VIEW ' . $name; - } - - /** - * {@inheritDoc} - * - * @deprecated Use application-generated UUIDs instead - */ - public function getGuidExpression() - { - return 'NEWID()'; - } - - /** - * {@inheritDoc} - */ - public function getLocateExpression($str, $substr, $startPos = false) - { - if ($startPos === false) { - return 'CHARINDEX(' . $substr . ', ' . $str . ')'; - } - - return 'CHARINDEX(' . $substr . ', ' . $str . ', ' . $startPos . ')'; - } - - /** - * {@inheritDoc} - */ - public function getModExpression($expression1, $expression2) - { - return $expression1 . ' % ' . $expression2; - } - - /** - * {@inheritDoc} - */ - public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false) - { - if (! $char) { - switch ($mode) { - case TrimMode::LEADING: - $trimFn = 'LTRIM'; - break; - - case TrimMode::TRAILING: - $trimFn = 'RTRIM'; - break; - - default: - return 'LTRIM(RTRIM(' . $str . '))'; - } - - return $trimFn . '(' . $str . ')'; - } - - $pattern = "'%[^' + " . $char . " + ']%'"; - - if ($mode === TrimMode::LEADING) { - return 'stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)'; - } - - if ($mode === TrimMode::TRAILING) { - return 'reverse(stuff(reverse(' . $str . '), 1, ' - . 'patindex(' . $pattern . ', reverse(' . $str . ')) - 1, null))'; - } - - return 'reverse(stuff(reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)), 1, ' - . 'patindex(' . $pattern . ', reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str - . ') - 1, null))) - 1, null))'; - } - - /** - * {@inheritDoc} - */ - public function getConcatExpression() - { - $args = func_get_args(); - - return '(' . implode(' + ', $args) . ')'; - } - - /** - * {@inheritDoc} - */ - public function getListDatabasesSQL() - { - return 'SELECT * FROM sys.databases'; - } - - /** - * {@inheritDoc} - */ - public function getListNamespacesSQL() - { - return "SELECT name FROM sys.schemas WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys')"; - } - - /** - * {@inheritDoc} - */ - public function getSubstringExpression($string, $start, $length = null) - { - if ($length !== null) { - return 'SUBSTRING(' . $string . ', ' . $start . ', ' . $length . ')'; - } - - return 'SUBSTRING(' . $string . ', ' . $start . ', LEN(' . $string . ') - ' . $start . ' + 1)'; - } - - /** - * {@inheritDoc} - */ - public function getLengthExpression($column) - { - return 'LEN(' . $column . ')'; - } - - /** - * {@inheritDoc} - */ - public function getSetTransactionIsolationSQL($level) - { - return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); - } - - /** - * {@inheritDoc} - */ - public function getIntegerTypeDeclarationSQL(array $column) - { - return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getBigIntTypeDeclarationSQL(array $column) - { - return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getSmallIntTypeDeclarationSQL(array $column) - { - return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getGuidTypeDeclarationSQL(array $column) - { - return 'UNIQUEIDENTIFIER'; - } - - /** - * {@inheritDoc} - */ - public function getAsciiStringTypeDeclarationSQL(array $column): string - { - $length = $column['length'] ?? null; - - if (! isset($column['fixed'])) { - return sprintf('VARCHAR(%d)', $length ?? 255); - } - - return sprintf('CHAR(%d)', $length ?? 255); - } - - /** - * {@inheritDoc} - */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) - { - return $fixed - ? ($length ? 'NCHAR(' . $length . ')' : 'CHAR(255)') - : ($length ? 'NVARCHAR(' . $length . ')' : 'NVARCHAR(255)'); - } - - /** - * {@inheritdoc} - */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) - { - return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')'; - } - - /** - * {@inheritdoc} - */ - public function getBinaryMaxLength() - { - return 8000; - } - - /** - * {@inheritDoc} - */ - public function getClobTypeDeclarationSQL(array $column) - { - return 'VARCHAR(MAX)'; - } - - /** - * {@inheritDoc} - */ - protected function _getCommonIntegerTypeDeclarationSQL(array $column) - { - return ! empty($column['autoincrement']) ? ' IDENTITY' : ''; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTypeDeclarationSQL(array $column) - { - return 'DATETIME'; - } - - /** - * {@inheritDoc} - */ - public function getDateTypeDeclarationSQL(array $column) - { - return 'DATETIME'; - } - - /** - * {@inheritDoc} - */ - public function getTimeTypeDeclarationSQL(array $column) - { - return 'DATETIME'; - } - - /** - * {@inheritDoc} - */ - public function getBooleanTypeDeclarationSQL(array $column) - { - return 'BIT'; - } - - /** - * {@inheritDoc} - */ - protected function doModifyLimitQuery($query, $limit, $offset = null) - { - $where = []; - - if ($offset > 0) { - $where[] = sprintf('doctrine_rownum >= %d', $offset + 1); - } - - if ($limit !== null) { - $where[] = sprintf('doctrine_rownum <= %d', $offset + $limit); - $top = sprintf('TOP %d', $offset + $limit); - } else { - $top = 'TOP 9223372036854775807'; - } - - if (empty($where)) { - return $query; - } - - // We'll find a SELECT or SELECT distinct and prepend TOP n to it - // Even if the TOP n is very large, the use of a CTE will - // allow the SQL Server query planner to optimize it so it doesn't - // actually scan the entire range covered by the TOP clause. - if (! preg_match('/^(\s*SELECT\s+(?:DISTINCT\s+)?)(.*)$/is', $query, $matches)) { - return $query; - } - - $query = $matches[1] . $top . ' ' . $matches[2]; - - if (stristr($query, 'ORDER BY')) { - // Inner order by is not valid in SQL Server for our purposes - // unless it's in a TOP N subquery. - $query = $this->scrubInnerOrderBy($query); - } - - // Build a new limited query around the original, using a CTE - return sprintf( - 'WITH dctrn_cte AS (%s) ' - . 'SELECT * FROM (' - . 'SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS doctrine_rownum FROM dctrn_cte' - . ') AS doctrine_tbl ' - . 'WHERE %s ORDER BY doctrine_rownum ASC', - $query, - implode(' AND ', $where) - ); - } - - /** - * Remove ORDER BY clauses in subqueries - they're not supported by SQL Server. - * Caveat: will leave ORDER BY in TOP N subqueries. - * - * @param string $query - * - * @return string - */ - private function scrubInnerOrderBy($query) - { - $count = substr_count(strtoupper($query), 'ORDER BY'); - $offset = 0; - - while ($count-- > 0) { - $orderByPos = stripos($query, ' ORDER BY', $offset); - if ($orderByPos === false) { - break; - } - - $qLen = strlen($query); - $parenCount = 0; - $currentPosition = $orderByPos; - - while ($parenCount >= 0 && $currentPosition < $qLen) { - if ($query[$currentPosition] === '(') { - $parenCount++; - } elseif ($query[$currentPosition] === ')') { - $parenCount--; - } - - $currentPosition++; - } - - if ($this->isOrderByInTopNSubquery($query, $orderByPos)) { - // If the order by clause is in a TOP N subquery, do not remove - // it and continue iteration from the current position. - $offset = $currentPosition; - continue; - } - - if ($currentPosition >= $qLen - 1) { - continue; - } - - $query = substr($query, 0, $orderByPos) . substr($query, $currentPosition - 1); - $offset = $orderByPos; - } - - return $query; - } - - /** - * Check an ORDER BY clause to see if it is in a TOP N query or subquery. - * - * @param string $query The query - * @param int $currentPosition Start position of ORDER BY clause - * - * @return bool true if ORDER BY is in a TOP N query, false otherwise - */ - private function isOrderByInTopNSubquery($query, $currentPosition) - { - // Grab query text on the same nesting level as the ORDER BY clause we're examining. - $subQueryBuffer = ''; - $parenCount = 0; - - // If $parenCount goes negative, we've exited the subquery we're examining. - // If $currentPosition goes negative, we've reached the beginning of the query. - while ($parenCount >= 0 && $currentPosition >= 0) { - if ($query[$currentPosition] === '(') { - $parenCount--; - } elseif ($query[$currentPosition] === ')') { - $parenCount++; - } - - // Only yank query text on the same nesting level as the ORDER BY clause. - $subQueryBuffer = ($parenCount === 0 ? $query[$currentPosition] : ' ') . $subQueryBuffer; - - $currentPosition--; - } - - return (bool) preg_match('/SELECT\s+(DISTINCT\s+)?TOP\s/i', $subQueryBuffer); - } - - /** - * {@inheritDoc} - */ - public function supportsLimitOffset() - { - return false; - } - - /** - * {@inheritDoc} - */ - public function convertBooleans($item) - { - if (is_array($item)) { - foreach ($item as $key => $value) { - if (! is_bool($value) && ! is_numeric($value)) { - continue; - } - - $item[$key] = $value ? 1 : 0; - } - } elseif (is_bool($item) || is_numeric($item)) { - $item = $item ? 1 : 0; - } - - return $item; - } - - /** - * {@inheritDoc} - */ - public function getCreateTemporaryTableSnippetSQL() - { - return 'CREATE TABLE'; - } - - /** - * {@inheritDoc} - */ - public function getTemporaryTableName($tableName) - { - return '#' . $tableName; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeFormatString() - { - return 'Y-m-d H:i:s.000'; - } - - /** - * {@inheritDoc} - */ - public function getDateFormatString() - { - return 'Y-m-d H:i:s.000'; - } - - /** - * {@inheritDoc} - */ - public function getTimeFormatString() - { - return 'Y-m-d H:i:s.000'; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTzFormatString() - { - return $this->getDateTimeFormatString(); - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return 'mssql'; - } - - /** - * {@inheritDoc} - */ - protected function initializeDoctrineTypeMappings() - { - $this->doctrineTypeMapping = [ - 'bigint' => 'bigint', - 'numeric' => 'decimal', - 'bit' => 'boolean', - 'smallint' => 'smallint', - 'decimal' => 'decimal', - 'smallmoney' => 'integer', - 'int' => 'integer', - 'tinyint' => 'smallint', - 'money' => 'integer', - 'float' => 'float', - 'real' => 'float', - 'double' => 'float', - 'double precision' => 'float', - 'smalldatetime' => 'datetime', - 'datetime' => 'datetime', - 'char' => 'string', - 'varchar' => 'string', - 'text' => 'text', - 'nchar' => 'string', - 'nvarchar' => 'string', - 'ntext' => 'text', - 'binary' => 'binary', - 'varbinary' => 'binary', - 'image' => 'blob', - 'uniqueidentifier' => 'guid', - ]; - } - - /** - * {@inheritDoc} - */ - public function createSavePoint($savepoint) - { - return 'SAVE TRANSACTION ' . $savepoint; - } - - /** - * {@inheritDoc} - */ - public function releaseSavePoint($savepoint) - { - return ''; - } - - /** - * {@inheritDoc} - */ - public function rollbackSavePoint($savepoint) - { - return 'ROLLBACK TRANSACTION ' . $savepoint; - } - - /** - * {@inheritdoc} - */ - public function getForeignKeyReferentialActionSQL($action) - { - // RESTRICT is not supported, therefore falling back to NO ACTION. - if (strtoupper($action) === 'RESTRICT') { - return 'NO ACTION'; - } - - return parent::getForeignKeyReferentialActionSQL($action); - } - - /** - * {@inheritDoc} - */ - public function appendLockHint($fromClause, $lockMode) - { - switch (true) { - case $lockMode === LockMode::NONE: - return $fromClause; - - case $lockMode === LockMode::PESSIMISTIC_READ: - return $fromClause . ' WITH (HOLDLOCK, ROWLOCK)'; - - case $lockMode === LockMode::PESSIMISTIC_WRITE: - return $fromClause . ' WITH (UPDLOCK, ROWLOCK)'; - - default: - return $fromClause; - } - } - - /** - * {@inheritDoc} - */ - public function getForUpdateSQL() - { - return ' '; - } - - /** - * {@inheritDoc} - */ - protected function getReservedKeywordsClass() - { - return Keywords\SQLServerKeywords::class; - } - - /** - * {@inheritDoc} - */ - public function quoteSingleIdentifier($str) - { - return '[' . str_replace(']', ']]', $str) . ']'; - } - - /** - * {@inheritDoc} - */ - public function getTruncateTableSQL($tableName, $cascade = false) - { - $tableIdentifier = new Identifier($tableName); - - return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this); - } - - /** - * {@inheritDoc} - */ - public function getBlobTypeDeclarationSQL(array $column) - { - return 'VARBINARY(MAX)'; - } - - /** - * {@inheritdoc} - * - * Modifies column declaration order as it differs in Microsoft SQL Server. - */ - public function getColumnDeclarationSQL($name, array $column) - { - if (isset($column['columnDefinition'])) { - $columnDef = $this->getCustomTypeDeclarationSQL($column); - } else { - $collation = isset($column['collation']) && $column['collation'] ? - ' ' . $this->getColumnCollationDeclarationSQL($column['collation']) : ''; - - $notnull = isset($column['notnull']) && $column['notnull'] ? ' NOT NULL' : ''; - - $unique = isset($column['unique']) && $column['unique'] ? - ' ' . $this->getUniqueFieldDeclarationSQL() : ''; - - $check = isset($column['check']) && $column['check'] ? - ' ' . $column['check'] : ''; - - $typeDecl = $column['type']->getSQLDeclaration($column, $this); - $columnDef = $typeDecl . $collation . $notnull . $unique . $check; - } - - return $name . ' ' . $columnDef; - } - - /** - * Returns a unique default constraint name for a table and column. - * - * @param string $table Name of the table to generate the unique default constraint name for. - * @param string $column Name of the column in the table to generate the unique default constraint name for. - * - * @return string - */ - private function generateDefaultConstraintName($table, $column) - { - return 'DF_' . $this->generateIdentifierName($table) . '_' . $this->generateIdentifierName($column); - } - - /** - * Returns a hash value for a given identifier. - * - * @param string $identifier Identifier to generate a hash value for. - * - * @return string - */ - private function generateIdentifierName($identifier) - { - // Always generate name for unquoted identifiers to ensure consistency. - $identifier = new Identifier($identifier); - - return strtoupper(dechex(crc32($identifier->getName()))); - } - - protected function getCommentOnTableSQL(string $tableName, ?string $comment): string - { - return sprintf( - " - EXEC sys.sp_addextendedproperty @name=N'MS_Description', - @value=N%s, @level0type=N'SCHEMA', @level0name=N'dbo', - @level1type=N'TABLE', @level1name=N%s - ", - $this->quoteStringLiteral((string) $comment), - $this->quoteStringLiteral($tableName) - ); - } - - public function getListTableMetadataSQL(string $table): string - { - return sprintf( - " - SELECT - p.value AS [table_comment] - FROM - sys.tables AS tbl - INNER JOIN sys.extended_properties AS p ON p.major_id=tbl.object_id AND p.minor_id=0 AND p.class=1 - WHERE - (tbl.name=N%s and SCHEMA_NAME(tbl.schema_id)=N'dbo' and p.name=N'MS_Description') - ", - $this->quoteStringLiteral($table) - ); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php deleted file mode 100644 index bf973830d..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php +++ /dev/null @@ -1,131 +0,0 @@ -getParams(); - if (isset($params['portability'])) { - $this->portability = $params['portability'] = (new OptimizeFlags())( - $this->getDatabasePlatform(), - $params['portability'] - ); - } - - if (isset($params['fetch_case']) && $this->portability & self::PORTABILITY_FIX_CASE) { - if ($this->_conn instanceof PDOConnection) { - // make use of c-level support for case handling - $this->_conn->setAttribute(PDO::ATTR_CASE, $params['fetch_case']); - } else { - $this->case = $params['fetch_case'] === ColumnCase::LOWER ? CASE_LOWER : CASE_UPPER; - } - } - } - - return $ret; - } - - /** - * @return int - */ - public function getPortability() - { - return $this->portability; - } - - /** - * @return int|null - */ - public function getFetchCase() - { - return $this->case; - } - - /** - * {@inheritdoc} - */ - public function executeQuery($sql, array $params = [], $types = [], ?QueryCacheProfile $qcp = null) - { - $stmt = new Statement(parent::executeQuery($sql, $params, $types, $qcp), $this); - $stmt->setFetchMode($this->defaultFetchMode); - - return new ForwardCompatibility\Result($stmt); - } - - /** - * {@inheritdoc} - * - * @param string $sql - * - * @return Statement - */ - public function prepare($sql) - { - $stmt = new Statement(parent::prepare($sql), $this); - $stmt->setFetchMode($this->defaultFetchMode); - - return $stmt; - } - - /** - * {@inheritdoc} - */ - public function query() - { - $connection = $this->getWrappedConnection(); - - $stmt = $connection->query(...func_get_args()); - $stmt = new Statement($stmt, $this); - $stmt->setFetchMode($this->defaultFetchMode); - - return $stmt; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php deleted file mode 100644 index a10e00ad4..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php +++ /dev/null @@ -1,402 +0,0 @@ -Statement and applies portability measures. - * - * @param DriverStatement|ResultStatement $stmt - */ - public function __construct($stmt, Connection $conn) - { - $this->stmt = $stmt; - $this->portability = $conn->getPortability(); - $this->case = $conn->getFetchCase(); - } - - /** - * {@inheritdoc} - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) - { - assert($this->stmt instanceof DriverStatement); - - return $this->stmt->bindParam($param, $variable, $type, $length); - } - - /** - * {@inheritdoc} - */ - public function bindValue($param, $value, $type = ParameterType::STRING) - { - assert($this->stmt instanceof DriverStatement); - - return $this->stmt->bindValue($param, $value, $type); - } - - /** - * {@inheritdoc} - * - * @deprecated Use free() instead. - */ - public function closeCursor() - { - return $this->stmt->closeCursor(); - } - - /** - * {@inheritdoc} - */ - public function columnCount() - { - return $this->stmt->columnCount(); - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorCode() - { - assert($this->stmt instanceof DriverStatement); - - return $this->stmt->errorCode(); - } - - /** - * {@inheritdoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorInfo() - { - assert($this->stmt instanceof DriverStatement); - - return $this->stmt->errorInfo(); - } - - /** - * {@inheritdoc} - */ - public function execute($params = null) - { - assert($this->stmt instanceof DriverStatement); - - return $this->stmt->execute($params); - } - - /** - * {@inheritdoc} - * - * @deprecated Use one of the fetch- or iterate-related methods. - */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) - { - $this->defaultFetchMode = $fetchMode; - - return $this->stmt->setFetchMode($fetchMode, $arg2, $arg3); - } - - /** - * {@inheritdoc} - * - * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. - */ - #[ReturnTypeWillChange] - public function getIterator() - { - return new StatementIterator($this); - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. - */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) - { - $fetchMode = $fetchMode ?: $this->defaultFetchMode; - - $row = $this->stmt->fetch($fetchMode); - - $iterateRow = ( - $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL | Connection::PORTABILITY_RTRIM) - ) !== 0; - - $fixCase = $this->case !== null - && ($fetchMode === FetchMode::ASSOCIATIVE || $fetchMode === FetchMode::MIXED) - && ($this->portability & Connection::PORTABILITY_FIX_CASE); - - $row = $this->fixRow($row, $iterateRow, $fixCase); - - return $row; - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. - */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) - { - $fetchMode = $fetchMode ?: $this->defaultFetchMode; - - if ($fetchArgument) { - $rows = $this->stmt->fetchAll($fetchMode, $fetchArgument); - } else { - $rows = $this->stmt->fetchAll($fetchMode); - } - - $fixCase = $this->case !== null - && ($fetchMode === FetchMode::ASSOCIATIVE || $fetchMode === FetchMode::MIXED) - && ($this->portability & Connection::PORTABILITY_FIX_CASE); - - return $this->fixResultSet($rows, $fixCase, $fetchMode !== FetchMode::COLUMN); - } - - /** - * {@inheritdoc} - */ - public function fetchNumeric() - { - if ($this->stmt instanceof Result) { - $row = $this->stmt->fetchNumeric(); - } else { - $row = $this->stmt->fetch(FetchMode::NUMERIC); - } - - return $this->fixResult($row, false); - } - - /** - * {@inheritdoc} - */ - public function fetchAssociative() - { - if ($this->stmt instanceof Result) { - $row = $this->stmt->fetchAssociative(); - } else { - $row = $this->stmt->fetch(FetchMode::ASSOCIATIVE); - } - - return $this->fixResult($row, true); - } - - /** - * {@inheritdoc} - */ - public function fetchOne() - { - if ($this->stmt instanceof Result) { - $value = $this->stmt->fetchOne(); - } else { - $value = $this->stmt->fetch(FetchMode::COLUMN); - } - - if (($this->portability & Connection::PORTABILITY_EMPTY_TO_NULL) !== 0 && $value === '') { - $value = null; - } elseif (($this->portability & Connection::PORTABILITY_RTRIM) !== 0 && is_string($value)) { - $value = rtrim($value); - } - - return $value; - } - - /** - * {@inheritdoc} - */ - public function fetchAllNumeric(): array - { - if ($this->stmt instanceof Result) { - $data = $this->stmt->fetchAllNumeric(); - } else { - $data = $this->stmt->fetchAll(FetchMode::NUMERIC); - } - - return $this->fixResultSet($data, false, true); - } - - /** - * {@inheritdoc} - */ - public function fetchAllAssociative(): array - { - if ($this->stmt instanceof Result) { - $data = $this->stmt->fetchAllAssociative(); - } else { - $data = $this->stmt->fetchAll(FetchMode::ASSOCIATIVE); - } - - return $this->fixResultSet($data, true, true); - } - - /** - * {@inheritdoc} - */ - public function fetchFirstColumn(): array - { - if ($this->stmt instanceof Result) { - $data = $this->stmt->fetchFirstColumn(); - } else { - $data = $this->stmt->fetchAll(FetchMode::COLUMN); - } - - return $this->fixResultSet($data, true, false); - } - - public function free(): void - { - if ($this->stmt instanceof Result) { - $this->stmt->free(); - - return; - } - - $this->stmt->closeCursor(); - } - - /** - * @param mixed $result - * - * @return mixed - */ - private function fixResult($result, bool $fixCase) - { - $iterateRow = ( - $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL | Connection::PORTABILITY_RTRIM) - ) !== 0; - - $fixCase = $fixCase && $this->case !== null && ($this->portability & Connection::PORTABILITY_FIX_CASE) !== 0; - - return $this->fixRow($result, $iterateRow, $fixCase); - } - - /** - * @param array $resultSet - * - * @return array - */ - private function fixResultSet(array $resultSet, bool $fixCase, bool $isArray): array - { - $iterateRow = ( - $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL | Connection::PORTABILITY_RTRIM) - ) !== 0; - - $fixCase = $fixCase && $this->case !== null && ($this->portability & Connection::PORTABILITY_FIX_CASE) !== 0; - - if (! $iterateRow && ! $fixCase) { - return $resultSet; - } - - if (! $isArray) { - foreach ($resultSet as $num => $value) { - $resultSet[$num] = [$value]; - } - } - - foreach ($resultSet as $num => $row) { - $resultSet[$num] = $this->fixRow($row, $iterateRow, $fixCase); - } - - if (! $isArray) { - foreach ($resultSet as $num => $row) { - $resultSet[$num] = $row[0]; - } - } - - return $resultSet; - } - - /** - * @param mixed $row - * @param bool $iterateRow - * @param bool $fixCase - * - * @return mixed - */ - protected function fixRow($row, $iterateRow, $fixCase) - { - if (! $row) { - return $row; - } - - if ($fixCase) { - $row = array_change_key_case($row, $this->case); - } - - if ($iterateRow) { - foreach ($row as $k => $v) { - if (($this->portability & Connection::PORTABILITY_EMPTY_TO_NULL) && $v === '') { - $row[$k] = null; - } elseif (($this->portability & Connection::PORTABILITY_RTRIM) && is_string($v)) { - $row[$k] = rtrim($v); - } - } - } - - return $row; - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchOne() instead. - */ - public function fetchColumn($columnIndex = 0) - { - $value = $this->stmt->fetchColumn($columnIndex); - - if ($this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL | Connection::PORTABILITY_RTRIM)) { - if (($this->portability & Connection::PORTABILITY_EMPTY_TO_NULL) && $value === '') { - $value = null; - } elseif (($this->portability & Connection::PORTABILITY_RTRIM) && is_string($value)) { - $value = rtrim($value); - } - } - - return $value; - } - - /** - * {@inheritdoc} - */ - public function rowCount() - { - assert($this->stmt instanceof DriverStatement); - - return $this->stmt->rowCount(); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Result.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Result.php deleted file mode 100644 index 227310b3d..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Result.php +++ /dev/null @@ -1,52 +0,0 @@ - - * - * @throws Exception - */ - public function fetchAllKeyValue(): array; - - /** - * Returns an associative array with the keys mapped to the first column and the values being - * an associative array representing the rest of the columns and their values. - * - * @return array> - * - * @throws Exception - */ - public function fetchAllAssociativeIndexed(): array; - - /** - * Returns an iterator over the result set with the values of the first column of the result - * - * @return Traversable - * - * @throws Exception - */ - public function iterateKeyValue(): Traversable; - - /** - * Returns an iterator over the result set with the keys mapped to the first column and the values being - * an associative array representing the rest of the columns and their values. - * - * @return Traversable> - * - * @throws Exception - */ - public function iterateAssociativeIndexed(): Traversable; -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php deleted file mode 100644 index 604904dfc..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php +++ /dev/null @@ -1,311 +0,0 @@ - - */ - private static function getPositionalPlaceholderPositions(string $statement): array - { - return self::collectPlaceholders( - $statement, - '?', - self::POSITIONAL_TOKEN, - static function (string $_, int $placeholderPosition, int $fragmentPosition, array &$carry): void { - $carry[] = $placeholderPosition + $fragmentPosition; - } - ); - } - - /** - * Returns a map of placeholder positions to their parameter names. - * - * @return array - */ - private static function getNamedPlaceholderPositions(string $statement): array - { - return self::collectPlaceholders( - $statement, - ':', - self::NAMED_TOKEN, - static function ( - string $placeholder, - int $placeholderPosition, - int $fragmentPosition, - array &$carry - ): void { - $carry[$placeholderPosition + $fragmentPosition] = substr($placeholder, 1); - } - ); - } - - /** - * @return mixed[] - */ - private static function collectPlaceholders( - string $statement, - string $match, - string $token, - callable $collector - ): array { - if (strpos($statement, $match) === false) { - return []; - } - - $carry = []; - - foreach (self::getUnquotedStatementFragments($statement) as $fragment) { - preg_match_all('/' . $token . '/', $fragment[0], $matches, PREG_OFFSET_CAPTURE); - foreach ($matches[0] as $placeholder) { - $collector($placeholder[0], $placeholder[1], $fragment[1], $carry); - } - } - - return $carry; - } - - /** - * For a positional query this method can rewrite the sql statement with regard to array parameters. - * - * @param string $query SQL query - * @param mixed[] $params Query parameters - * @param array|array $types Parameter types - * - * @return mixed[] - * - * @throws SQLParserUtilsException - */ - public static function expandListParameters($query, $params, $types) - { - $isPositional = is_int(key($params)); - $arrayPositions = []; - $bindIndex = -1; - - if ($isPositional) { - // make sure that $types has the same keys as $params - // to allow omitting parameters with unspecified types - $types += array_fill_keys(array_keys($params), null); - - ksort($params); - ksort($types); - } - - foreach ($types as $name => $type) { - ++$bindIndex; - - if ($type !== Connection::PARAM_INT_ARRAY && $type !== Connection::PARAM_STR_ARRAY) { - continue; - } - - if ($isPositional) { - $name = $bindIndex; - } - - $arrayPositions[$name] = false; - } - - if (( ! $arrayPositions && $isPositional)) { - return [$query, $params, $types]; - } - - if ($isPositional) { - $paramOffset = 0; - $queryOffset = 0; - $params = array_values($params); - $types = array_values($types); - - $paramPos = self::getPositionalPlaceholderPositions($query); - - foreach ($paramPos as $needle => $needlePos) { - if (! isset($arrayPositions[$needle])) { - continue; - } - - $needle += $paramOffset; - $needlePos += $queryOffset; - $count = count($params[$needle]); - - $params = array_merge( - array_slice($params, 0, $needle), - $params[$needle], - array_slice($params, $needle + 1) - ); - - $types = array_merge( - array_slice($types, 0, $needle), - $count ? - // array needles are at {@link \Doctrine\DBAL\ParameterType} constants - // + {@link \Doctrine\DBAL\Connection::ARRAY_PARAM_OFFSET} - array_fill(0, $count, $types[$needle] - Connection::ARRAY_PARAM_OFFSET) : - [], - array_slice($types, $needle + 1) - ); - - $expandStr = $count ? implode(', ', array_fill(0, $count, '?')) : 'NULL'; - $query = substr($query, 0, $needlePos) . $expandStr . substr($query, $needlePos + 1); - - $paramOffset += $count - 1; // Grows larger by number of parameters minus the replaced needle. - $queryOffset += strlen($expandStr) - 1; - } - - return [$query, $params, $types]; - } - - $queryOffset = 0; - $typesOrd = []; - $paramsOrd = []; - - $paramPos = self::getNamedPlaceholderPositions($query); - - foreach ($paramPos as $pos => $paramName) { - $paramLen = strlen($paramName) + 1; - $value = static::extractParam($paramName, $params, true); - - if (! isset($arrayPositions[$paramName]) && ! isset($arrayPositions[':' . $paramName])) { - $pos += $queryOffset; - $queryOffset -= $paramLen - 1; - $paramsOrd[] = $value; - $typesOrd[] = static::extractParam($paramName, $types, false, ParameterType::STRING); - $query = substr($query, 0, $pos) . '?' . substr($query, $pos + $paramLen); - - continue; - } - - $count = count($value); - $expandStr = $count > 0 ? implode(', ', array_fill(0, $count, '?')) : 'NULL'; - - foreach ($value as $val) { - $paramsOrd[] = $val; - $typesOrd[] = static::extractParam($paramName, $types, false) - Connection::ARRAY_PARAM_OFFSET; - } - - $pos += $queryOffset; - $queryOffset += strlen($expandStr) - $paramLen; - $query = substr($query, 0, $pos) . $expandStr . substr($query, $pos + $paramLen); - } - - return [$query, $paramsOrd, $typesOrd]; - } - - /** - * Slice the SQL statement around pairs of quotes and - * return string fragments of SQL outside of quoted literals. - * Each fragment is captured as a 2-element array: - * - * 0 => matched fragment string, - * 1 => offset of fragment in $statement - * - * @param string $statement - * - * @return mixed[][] - */ - private static function getUnquotedStatementFragments($statement) - { - $literal = self::ESCAPED_SINGLE_QUOTED_TEXT . '|' . - self::ESCAPED_DOUBLE_QUOTED_TEXT . '|' . - self::ESCAPED_BACKTICK_QUOTED_TEXT . '|' . - self::ESCAPED_BRACKET_QUOTED_TEXT; - $expression = sprintf('/((.+(?i:ARRAY)\\[.+\\])|([^\'"`\\[]+))(?:%s)?/s', $literal); - - preg_match_all($expression, $statement, $fragments, PREG_OFFSET_CAPTURE); - - return $fragments[1]; - } - - /** - * @param string $paramName The name of the parameter (without a colon in front) - * @param mixed $paramsOrTypes A hash of parameters or types - * @param bool $isParam - * @param mixed $defaultValue An optional default value. If omitted, an exception is thrown - * - * @return mixed - * - * @throws SQLParserUtilsException - */ - private static function extractParam($paramName, $paramsOrTypes, $isParam, $defaultValue = null) - { - if (array_key_exists($paramName, $paramsOrTypes)) { - return $paramsOrTypes[$paramName]; - } - - // Hash keys can be prefixed with a colon for compatibility - if (array_key_exists(':' . $paramName, $paramsOrTypes)) { - return $paramsOrTypes[':' . $paramName]; - } - - if ($defaultValue !== null) { - return $defaultValue; - } - - if ($isParam) { - throw SQLParserUtilsException::missingParam($paramName); - } - - throw SQLParserUtilsException::missingType($paramName); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtilsException.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtilsException.php deleted file mode 100644 index 297b0761b..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtilsException.php +++ /dev/null @@ -1,37 +0,0 @@ -_platform->getDoctrineTypeMapping($dbType); - $type = $this->extractDoctrineTypeFromComment($tableColumn['COLUMN_COMMENT'], $type); - $tableColumn['COLUMN_COMMENT'] = $this->removeDoctrineTypeFromComment($tableColumn['COLUMN_COMMENT'], $type); - - $options = [ - 'notnull' => ! (bool) $tableColumn['IS_NULLABLE'], - 'length' => (int) $tableColumn['CHARACTER_MAXIMUM_LENGTH'], - 'default' => $tableColumn['COLUMN_DEFAULT'] ?? null, - 'autoincrement' => (bool) $tableColumn['IS_AUTO_INCREMENT'], - 'scale' => (int) $tableColumn['NUMERIC_SCALE'], - 'precision' => (int) $tableColumn['NUMERIC_PRECISION'], - 'comment' => isset($tableColumn['COLUMN_COMMENT']) && $tableColumn['COLUMN_COMMENT'] !== '' - ? $tableColumn['COLUMN_COMMENT'] - : null, - ]; - - $column = new Column($tableColumn['COLUMN_NAME'], Type::getType($type), $options); - - if (! empty($tableColumn['COLLATION_NAME'])) { - $column->setPlatformOption('collation', $tableColumn['COLLATION_NAME']); - } - - return $column; - } - - /** - * {@inheritdoc} - */ - protected function _getPortableDatabaseDefinition($database) - { - return $database['SCHEMA_NAME']; - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableDefinition($table) - { - return $table['TABLE_NAME']; - } - - /** - * {@inheritdoc} - */ - public function _getPortableTableForeignKeyDefinition($tableForeignKey) - { - $columns = []; - foreach (explode(',', $tableForeignKey['CONSTRAINT_COLUMNS']) as $value) { - $columns[] = trim($value, ' `'); - } - - $refColumns = []; - foreach (explode(',', $tableForeignKey['REFERENCED_TABLE_COLUMNS']) as $value) { - $refColumns[] = trim($value, ' `'); - } - - return new ForeignKeyConstraint( - $columns, - $tableForeignKey['REFERENCED_TABLE_NAME'], - $refColumns, - $tableForeignKey['CONSTRAINT_NAME'], - [ - 'onUpdate' => $tableForeignKey['UPDATE_RULE'], - 'onDelete' => $tableForeignKey['DELETE_RULE'], - ] - ); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) - { - $indexes = []; - foreach ($tableIndexes as $k) { - $k['primary'] = (bool) $k['primary']; - $indexes[] = $k; - } - - return parent::_getPortableTableIndexesList($indexes, $tableName); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php deleted file mode 100644 index 4a8e621c3..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ /dev/null @@ -1,375 +0,0 @@ - "\0", - "\\'" => "'", - '\\"' => '"', - '\\b' => "\b", - '\\n' => "\n", - '\\r' => "\r", - '\\t' => "\t", - '\\Z' => "\x1a", - '\\\\' => '\\', - '\\%' => '%', - '\\_' => '_', - - // Internally, MariaDB escapes single quotes using the standard syntax - "''" => "'", - ]; - - /** - * {@inheritdoc} - */ - protected function _getPortableViewDefinition($view) - { - return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableDefinition($table) - { - return array_shift($table); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableUserDefinition($user) - { - return [ - 'user' => $user['User'], - 'password' => $user['Password'], - ]; - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) - { - foreach ($tableIndexes as $k => $v) { - $v = array_change_key_case($v, CASE_LOWER); - if ($v['key_name'] === 'PRIMARY') { - $v['primary'] = true; - } else { - $v['primary'] = false; - } - - if (strpos($v['index_type'], 'FULLTEXT') !== false) { - $v['flags'] = ['FULLTEXT']; - } elseif (strpos($v['index_type'], 'SPATIAL') !== false) { - $v['flags'] = ['SPATIAL']; - } - - // Ignore prohibited prefix `length` for spatial index - if (strpos($v['index_type'], 'SPATIAL') === false) { - $v['length'] = isset($v['sub_part']) ? (int) $v['sub_part'] : null; - } - - $tableIndexes[$k] = $v; - } - - return parent::_getPortableTableIndexesList($tableIndexes, $tableName); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableDatabaseDefinition($database) - { - return $database['Database']; - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableColumnDefinition($tableColumn) - { - $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); - - $dbType = strtolower($tableColumn['type']); - $dbType = strtok($dbType, '(), '); - assert(is_string($dbType)); - - $length = $tableColumn['length'] ?? strtok('(), '); - - $fixed = null; - - if (! isset($tableColumn['name'])) { - $tableColumn['name'] = ''; - } - - $scale = null; - $precision = null; - - $type = $this->_platform->getDoctrineTypeMapping($dbType); - - // In cases where not connected to a database DESCRIBE $table does not return 'Comment' - if (isset($tableColumn['comment'])) { - $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); - $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); - } - - switch ($dbType) { - case 'char': - case 'binary': - $fixed = true; - break; - - case 'float': - case 'double': - case 'real': - case 'numeric': - case 'decimal': - if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['type'], $match)) { - $precision = $match[1]; - $scale = $match[2]; - $length = null; - } - - break; - - case 'tinytext': - $length = MySqlPlatform::LENGTH_LIMIT_TINYTEXT; - break; - - case 'text': - $length = MySqlPlatform::LENGTH_LIMIT_TEXT; - break; - - case 'mediumtext': - $length = MySqlPlatform::LENGTH_LIMIT_MEDIUMTEXT; - break; - - case 'tinyblob': - $length = MySqlPlatform::LENGTH_LIMIT_TINYBLOB; - break; - - case 'blob': - $length = MySqlPlatform::LENGTH_LIMIT_BLOB; - break; - - case 'mediumblob': - $length = MySqlPlatform::LENGTH_LIMIT_MEDIUMBLOB; - break; - - case 'tinyint': - case 'smallint': - case 'mediumint': - case 'int': - case 'integer': - case 'bigint': - case 'year': - $length = null; - break; - } - - if ($this->_platform instanceof MariaDb1027Platform) { - $columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default']); - } else { - $columnDefault = $tableColumn['default']; - } - - $options = [ - 'length' => $length !== null ? (int) $length : null, - 'unsigned' => strpos($tableColumn['type'], 'unsigned') !== false, - 'fixed' => (bool) $fixed, - 'default' => $columnDefault, - 'notnull' => $tableColumn['null'] !== 'YES', - 'scale' => null, - 'precision' => null, - 'autoincrement' => strpos($tableColumn['extra'], 'auto_increment') !== false, - 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' - ? $tableColumn['comment'] - : null, - ]; - - if ($scale !== null && $precision !== null) { - $options['scale'] = (int) $scale; - $options['precision'] = (int) $precision; - } - - $column = new Column($tableColumn['field'], Type::getType($type), $options); - - if (isset($tableColumn['characterset'])) { - $column->setPlatformOption('charset', $tableColumn['characterset']); - } - - if (isset($tableColumn['collation'])) { - $column->setPlatformOption('collation', $tableColumn['collation']); - } - - return $column; - } - - /** - * Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers. - * - * - Since MariaDb 10.2.7 column defaults stored in information_schema are now quoted - * to distinguish them from expressions (see MDEV-10134). - * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema - * as current_timestamp(), currdate(), currtime() - * - Quoted 'NULL' is not enforced by Maria, it is technically possible to have - * null in some circumstances (see https://jira.mariadb.org/browse/MDEV-14053) - * - \' is always stored as '' in information_schema (normalized) - * - * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ - * @link https://jira.mariadb.org/browse/MDEV-13132 - * - * @param string|null $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7 - */ - private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault): ?string - { - if ($columnDefault === 'NULL' || $columnDefault === null) { - return null; - } - - if (preg_match('/^\'(.*)\'$/', $columnDefault, $matches)) { - return strtr($matches[1], self::MARIADB_ESCAPE_SEQUENCES); - } - - switch ($columnDefault) { - case 'current_timestamp()': - return $platform->getCurrentTimestampSQL(); - - case 'curdate()': - return $platform->getCurrentDateSQL(); - - case 'curtime()': - return $platform->getCurrentTimeSQL(); - } - - return $columnDefault; - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableForeignKeysList($tableForeignKeys) - { - $list = []; - foreach ($tableForeignKeys as $value) { - $value = array_change_key_case($value, CASE_LOWER); - if (! isset($list[$value['constraint_name']])) { - if (! isset($value['delete_rule']) || $value['delete_rule'] === 'RESTRICT') { - $value['delete_rule'] = null; - } - - if (! isset($value['update_rule']) || $value['update_rule'] === 'RESTRICT') { - $value['update_rule'] = null; - } - - $list[$value['constraint_name']] = [ - 'name' => $value['constraint_name'], - 'local' => [], - 'foreign' => [], - 'foreignTable' => $value['referenced_table_name'], - 'onDelete' => $value['delete_rule'], - 'onUpdate' => $value['update_rule'], - ]; - } - - $list[$value['constraint_name']]['local'][] = $value['column_name']; - $list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name']; - } - - $result = []; - foreach ($list as $constraint) { - $result[] = new ForeignKeyConstraint( - array_values($constraint['local']), - $constraint['foreignTable'], - array_values($constraint['foreign']), - $constraint['name'], - [ - 'onDelete' => $constraint['onDelete'], - 'onUpdate' => $constraint['onUpdate'], - ] - ); - } - - return $result; - } - - /** - * {@inheritdoc} - */ - public function listTableDetails($name) - { - $table = parent::listTableDetails($name); - - $platform = $this->_platform; - assert($platform instanceof MySqlPlatform); - $sql = $platform->getListTableMetadataSQL($name); - - $tableOptions = $this->_conn->fetchAssociative($sql); - - if ($tableOptions === false) { - return $table; - } - - $table->addOption('engine', $tableOptions['ENGINE']); - - if ($tableOptions['TABLE_COLLATION'] !== null) { - $table->addOption('collation', $tableOptions['TABLE_COLLATION']); - } - - if ($tableOptions['AUTO_INCREMENT'] !== null) { - $table->addOption('autoincrement', $tableOptions['AUTO_INCREMENT']); - } - - $table->addOption('comment', $tableOptions['TABLE_COMMENT']); - $table->addOption('create_options', $this->parseCreateOptions($tableOptions['CREATE_OPTIONS'])); - - return $table; - } - - /** - * @return string[]|true[] - */ - private function parseCreateOptions(?string $string): array - { - $options = []; - - if ($string === null || $string === '') { - return $options; - } - - foreach (explode(' ', $string) as $pair) { - $parts = explode('=', $pair, 2); - - $options[$parts[0]] = $parts[1] ?? true; - } - - return $options; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php deleted file mode 100644 index 0f4597913..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php +++ /dev/null @@ -1,536 +0,0 @@ -_conn->executeQuery( - "SELECT nspname FROM pg_namespace WHERE nspname !~ '^pg_.*' AND nspname != 'information_schema'" - ); - - return $statement->fetchAll(FetchMode::COLUMN); - } - - /** - * Returns an array of schema search paths. - * - * This is a PostgreSQL only function. - * - * @return string[] - */ - public function getSchemaSearchPaths() - { - $params = $this->_conn->getParams(); - - $searchPaths = $this->_conn->fetchColumn('SHOW search_path'); - assert($searchPaths !== false); - - $schema = explode(',', $searchPaths); - - if (isset($params['user'])) { - $schema = str_replace('"$user"', $params['user'], $schema); - } - - return array_map('trim', $schema); - } - - /** - * Gets names of all existing schemas in the current users search path. - * - * This is a PostgreSQL only function. - * - * @return string[] - */ - public function getExistingSchemaSearchPaths() - { - if ($this->existingSchemaPaths === null) { - $this->determineExistingSchemaSearchPaths(); - } - - return $this->existingSchemaPaths; - } - - /** - * Sets or resets the order of the existing schemas in the current search path of the user. - * - * This is a PostgreSQL only function. - * - * @return void - */ - public function determineExistingSchemaSearchPaths() - { - $names = $this->getSchemaNames(); - $paths = $this->getSchemaSearchPaths(); - - $this->existingSchemaPaths = array_filter($paths, static function ($v) use ($names) { - return in_array($v, $names); - }); - } - - /** - * {@inheritdoc} - */ - public function dropDatabase($database) - { - try { - parent::dropDatabase($database); - } catch (DriverException $exception) { - // If we have a SQLSTATE 55006, the drop database operation failed - // because of active connections on the database. - // To force dropping the database, we first have to close all active connections - // on that database and issue the drop database operation again. - if ($exception->getSQLState() !== '55006') { - throw $exception; - } - - assert($this->_platform instanceof PostgreSqlPlatform); - - $this->_execSql( - [ - $this->_platform->getDisallowDatabaseConnectionsSQL($database), - $this->_platform->getCloseActiveDatabaseConnectionsSQL($database), - ] - ); - - parent::dropDatabase($database); - } - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableForeignKeyDefinition($tableForeignKey) - { - $onUpdate = null; - $onDelete = null; - $localColumns = []; - $foreignColumns = []; - $foreignTable = null; - - if (preg_match('(ON UPDATE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) { - $onUpdate = $match[1]; - } - - if (preg_match('(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) { - $onDelete = $match[1]; - } - - $result = preg_match('/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)/', $tableForeignKey['condef'], $values); - assert($result === 1); - - // PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get - // the idea to trim them here. - $localColumns = array_map('trim', explode(',', $values[1])); - $foreignColumns = array_map('trim', explode(',', $values[3])); - $foreignTable = $values[2]; - - return new ForeignKeyConstraint( - $localColumns, - $foreignTable, - $foreignColumns, - $tableForeignKey['conname'], - ['onUpdate' => $onUpdate, 'onDelete' => $onDelete] - ); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTriggerDefinition($trigger) - { - return $trigger['trigger_name']; - } - - /** - * {@inheritdoc} - */ - protected function _getPortableViewDefinition($view) - { - return new View($view['schemaname'] . '.' . $view['viewname'], $view['definition']); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableUserDefinition($user) - { - return [ - 'user' => $user['usename'], - 'password' => $user['passwd'], - ]; - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableDefinition($table) - { - $schemas = $this->getExistingSchemaSearchPaths(); - $firstSchema = array_shift($schemas); - - if ($table['schema_name'] === $firstSchema) { - return $table['table_name']; - } - - return $table['schema_name'] . '.' . $table['table_name']; - } - - /** - * {@inheritdoc} - * - * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html - */ - protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) - { - $buffer = []; - foreach ($tableIndexes as $row) { - $colNumbers = array_map('intval', explode(' ', $row['indkey'])); - $columnNameSql = sprintf( - 'SELECT attnum, attname FROM pg_attribute WHERE attrelid=%d AND attnum IN (%s) ORDER BY attnum ASC', - $row['indrelid'], - implode(' ,', $colNumbers) - ); - - $indexColumns = $this->_conn->fetchAllAssociative($columnNameSql); - - // required for getting the order of the columns right. - foreach ($colNumbers as $colNum) { - foreach ($indexColumns as $colRow) { - if ($colNum !== $colRow['attnum']) { - continue; - } - - $buffer[] = [ - 'key_name' => $row['relname'], - 'column_name' => trim($colRow['attname']), - 'non_unique' => ! $row['indisunique'], - 'primary' => $row['indisprimary'], - 'where' => $row['where'], - ]; - } - } - } - - return parent::_getPortableTableIndexesList($buffer, $tableName); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableDatabaseDefinition($database) - { - return $database['datname']; - } - - /** - * {@inheritdoc} - */ - protected function _getPortableSequencesList($sequences) - { - $sequenceDefinitions = []; - - foreach ($sequences as $sequence) { - if ($sequence['schemaname'] !== 'public') { - $sequenceName = $sequence['schemaname'] . '.' . $sequence['relname']; - } else { - $sequenceName = $sequence['relname']; - } - - $sequenceDefinitions[$sequenceName] = $sequence; - } - - $list = []; - - foreach ($this->filterAssetNames(array_keys($sequenceDefinitions)) as $sequenceName) { - $list[] = $this->_getPortableSequenceDefinition($sequenceDefinitions[$sequenceName]); - } - - return $list; - } - - /** - * {@inheritdoc} - */ - protected function getPortableNamespaceDefinition(array $namespace) - { - return $namespace['nspname']; - } - - /** - * {@inheritdoc} - */ - protected function _getPortableSequenceDefinition($sequence) - { - if ($sequence['schemaname'] !== 'public') { - $sequenceName = $sequence['schemaname'] . '.' . $sequence['relname']; - } else { - $sequenceName = $sequence['relname']; - } - - if (! isset($sequence['increment_by'], $sequence['min_value'])) { - /** @var string[] $data */ - $data = $this->_conn->fetchAssoc( - 'SELECT min_value, increment_by FROM ' . $this->_platform->quoteIdentifier($sequenceName) - ); - - $sequence += $data; - } - - return new Sequence($sequenceName, (int) $sequence['increment_by'], (int) $sequence['min_value']); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableColumnDefinition($tableColumn) - { - $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); - - if (strtolower($tableColumn['type']) === 'varchar' || strtolower($tableColumn['type']) === 'bpchar') { - // get length from varchar definition - $length = preg_replace('~.*\(([0-9]*)\).*~', '$1', $tableColumn['complete_type']); - $tableColumn['length'] = $length; - } - - $matches = []; - - $autoincrement = false; - if (preg_match("/^nextval\('(.*)'(::.*)?\)$/", $tableColumn['default'], $matches)) { - $tableColumn['sequence'] = $matches[1]; - $tableColumn['default'] = null; - $autoincrement = true; - } - - if (preg_match("/^['(](.*)[')]::/", $tableColumn['default'], $matches)) { - $tableColumn['default'] = $matches[1]; - } elseif (preg_match('/^NULL::/', $tableColumn['default'])) { - $tableColumn['default'] = null; - } - - $length = $tableColumn['length'] ?? null; - if ($length === '-1' && isset($tableColumn['atttypmod'])) { - $length = $tableColumn['atttypmod'] - 4; - } - - if ((int) $length <= 0) { - $length = null; - } - - $fixed = null; - - if (! isset($tableColumn['name'])) { - $tableColumn['name'] = ''; - } - - $precision = null; - $scale = null; - $jsonb = null; - - $dbType = strtolower($tableColumn['type']); - if ( - strlen($tableColumn['domain_type']) - && ! $this->_platform->hasDoctrineTypeMappingFor($tableColumn['type']) - ) { - $dbType = strtolower($tableColumn['domain_type']); - $tableColumn['complete_type'] = $tableColumn['domain_complete_type']; - } - - $type = $this->_platform->getDoctrineTypeMapping($dbType); - $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); - $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); - - switch ($dbType) { - case 'smallint': - case 'int2': - $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); - $length = null; - break; - - case 'int': - case 'int4': - case 'integer': - $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); - $length = null; - break; - - case 'bigint': - case 'int8': - $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); - $length = null; - break; - - case 'bool': - case 'boolean': - if ($tableColumn['default'] === 'true') { - $tableColumn['default'] = true; - } - - if ($tableColumn['default'] === 'false') { - $tableColumn['default'] = false; - } - - $length = null; - break; - - case 'text': - case '_varchar': - case 'varchar': - $tableColumn['default'] = $this->parseDefaultExpression($tableColumn['default']); - $fixed = false; - break; - case 'interval': - $fixed = false; - break; - - case 'char': - case 'bpchar': - $fixed = true; - break; - - case 'float': - case 'float4': - case 'float8': - case 'double': - case 'double precision': - case 'real': - case 'decimal': - case 'money': - case 'numeric': - $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); - - if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['complete_type'], $match)) { - $precision = $match[1]; - $scale = $match[2]; - $length = null; - } - - break; - - case 'year': - $length = null; - break; - - // PostgreSQL 9.4+ only - case 'jsonb': - $jsonb = true; - break; - } - - if ($tableColumn['default'] && preg_match("('([^']+)'::)", $tableColumn['default'], $match)) { - $tableColumn['default'] = $match[1]; - } - - $options = [ - 'length' => $length, - 'notnull' => (bool) $tableColumn['isnotnull'], - 'default' => $tableColumn['default'], - 'precision' => $precision, - 'scale' => $scale, - 'fixed' => $fixed, - 'unsigned' => false, - 'autoincrement' => $autoincrement, - 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' - ? $tableColumn['comment'] - : null, - ]; - - $column = new Column($tableColumn['field'], Type::getType($type), $options); - - if (isset($tableColumn['collation']) && ! empty($tableColumn['collation'])) { - $column->setPlatformOption('collation', $tableColumn['collation']); - } - - if (in_array($column->getType()->getName(), [Types::JSON_ARRAY, Types::JSON], true)) { - $column->setPlatformOption('jsonb', $jsonb); - } - - return $column; - } - - /** - * PostgreSQL 9.4 puts parentheses around negative numeric default values that need to be stripped eventually. - * - * @param mixed $defaultValue - * - * @return mixed - */ - private function fixVersion94NegativeNumericDefaultValue($defaultValue) - { - if (strpos($defaultValue, '(') === 0) { - return trim($defaultValue, '()'); - } - - return $defaultValue; - } - - /** - * Parses a default value expression as given by PostgreSQL - */ - private function parseDefaultExpression(?string $default): ?string - { - if ($default === null) { - return $default; - } - - return str_replace("''", "'", $default); - } - - /** - * {@inheritdoc} - */ - public function listTableDetails($name): Table - { - $table = parent::listTableDetails($name); - - $platform = $this->_platform; - assert($platform instanceof PostgreSqlPlatform); - $sql = $platform->getListTableMetadataSQL($name); - - $tableOptions = $this->_conn->fetchAssoc($sql); - - if ($tableOptions !== false) { - $table->addOption('comment', $tableOptions['table_comment']); - } - - return $table; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php deleted file mode 100644 index c909c7e58..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php +++ /dev/null @@ -1,238 +0,0 @@ -startDatabase($database); - } - - /** - * {@inheritdoc} - * - * Tries stopping a database before dropping - * as SQL Anywhere needs a database to be stopped - * before it can be dropped. - * - * @see stopDatabase - */ - public function dropDatabase($database) - { - $this->tryMethod('stopDatabase', $database); - parent::dropDatabase($database); - } - - /** - * Starts a database. - * - * @param string $database The name of the database to start. - * - * @return void - */ - public function startDatabase($database) - { - assert($this->_platform instanceof SQLAnywherePlatform); - $this->_execSql($this->_platform->getStartDatabaseSQL($database)); - } - - /** - * Stops a database. - * - * @param string $database The name of the database to stop. - * - * @return void - */ - public function stopDatabase($database) - { - assert($this->_platform instanceof SQLAnywherePlatform); - $this->_execSql($this->_platform->getStopDatabaseSQL($database)); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableDatabaseDefinition($database) - { - return $database['name']; - } - - /** - * {@inheritdoc} - */ - protected function _getPortableSequenceDefinition($sequence) - { - return new Sequence($sequence['sequence_name'], $sequence['increment_by'], $sequence['start_with']); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableColumnDefinition($tableColumn) - { - $type = $this->_platform->getDoctrineTypeMapping($tableColumn['type']); - $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); - $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); - $precision = null; - $scale = null; - $fixed = false; - $default = null; - - if ($tableColumn['default'] !== null) { - // Strip quotes from default value. - $default = preg_replace(["/^'(.*)'$/", "/''/"], ['$1', "'"], $tableColumn['default']); - - if ($default === 'autoincrement') { - $default = null; - } - } - - switch ($tableColumn['type']) { - case 'binary': - case 'char': - case 'nchar': - $fixed = true; - break; - } - - switch ($type) { - case 'decimal': - case 'float': - $precision = $tableColumn['length']; - $scale = $tableColumn['scale']; - break; - } - - return new Column( - $tableColumn['column_name'], - Type::getType($type), - [ - 'length' => $type === 'string' ? $tableColumn['length'] : null, - 'precision' => $precision, - 'scale' => $scale, - 'unsigned' => (bool) $tableColumn['unsigned'], - 'fixed' => $fixed, - 'notnull' => (bool) $tableColumn['notnull'], - 'default' => $default, - 'autoincrement' => (bool) $tableColumn['autoincrement'], - 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' - ? $tableColumn['comment'] - : null, - ] - ); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableDefinition($table) - { - return $table['table_name']; - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableForeignKeyDefinition($tableForeignKey) - { - return new ForeignKeyConstraint( - $tableForeignKey['local_columns'], - $tableForeignKey['foreign_table'], - $tableForeignKey['foreign_columns'], - $tableForeignKey['name'], - $tableForeignKey['options'] - ); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableForeignKeysList($tableForeignKeys) - { - $foreignKeys = []; - - foreach ($tableForeignKeys as $tableForeignKey) { - if (! isset($foreignKeys[$tableForeignKey['index_name']])) { - $foreignKeys[$tableForeignKey['index_name']] = [ - 'local_columns' => [$tableForeignKey['local_column']], - 'foreign_table' => $tableForeignKey['foreign_table'], - 'foreign_columns' => [$tableForeignKey['foreign_column']], - 'name' => $tableForeignKey['index_name'], - 'options' => [ - 'notnull' => $tableForeignKey['notnull'], - 'match' => $tableForeignKey['match'], - 'onUpdate' => $tableForeignKey['on_update'], - 'onDelete' => $tableForeignKey['on_delete'], - 'check_on_commit' => $tableForeignKey['check_on_commit'], - 'clustered' => $tableForeignKey['clustered'], - 'for_olap_workload' => $tableForeignKey['for_olap_workload'], - ], - ]; - } else { - $foreignKeys[$tableForeignKey['index_name']]['local_columns'][] = $tableForeignKey['local_column']; - $foreignKeys[$tableForeignKey['index_name']]['foreign_columns'][] = $tableForeignKey['foreign_column']; - } - } - - return parent::_getPortableTableForeignKeysList($foreignKeys); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) - { - foreach ($tableIndexes as &$tableIndex) { - $tableIndex['primary'] = (bool) $tableIndex['primary']; - $tableIndex['flags'] = []; - - if ($tableIndex['clustered']) { - $tableIndex['flags'][] = 'clustered'; - } - - if ($tableIndex['with_nulls_not_distinct']) { - $tableIndex['flags'][] = 'with_nulls_not_distinct'; - } - - if (! $tableIndex['for_olap_workload']) { - continue; - } - - $tableIndex['flags'][] = 'for_olap_workload'; - } - - return parent::_getPortableTableIndexesList($tableIndexes, $tableName); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableViewDefinition($view) - { - $definition = preg_replace('/^.*\s+as\s+SELECT(.*)/i', 'SELECT$1', $view['view_def']); - assert(is_string($definition)); - - return new View($view['table_name'], $definition); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php deleted file mode 100644 index 85f4ae6d3..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php +++ /dev/null @@ -1,56 +0,0 @@ -conn = $conn; - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4213', - 'SchemaSynchronizer API is deprecated without a replacement and will be removed in DBAL 3.0' - ); - } - - /** - * @param string[] $sql - * - * @return void - */ - protected function processSqlSafely(array $sql) - { - foreach ($sql as $s) { - try { - $this->conn->exec($s); - } catch (Throwable $e) { - } - } - } - - /** - * @param string[] $sql - * - * @return void - */ - protected function processSql(array $sql) - { - foreach ($sql as $s) { - $this->conn->exec($s); - } - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php deleted file mode 100644 index a10d3b7f3..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php +++ /dev/null @@ -1,74 +0,0 @@ -platform = $conn->getDatabasePlatform(); - } - - /** - * {@inheritdoc} - */ - public function getCreateSchema(Schema $createSchema) - { - return $createSchema->toSql($this->platform); - } - - /** - * {@inheritdoc} - */ - public function getUpdateSchema(Schema $toSchema, $noDrops = false) - { - $comparator = new Comparator(); - $sm = $this->conn->getSchemaManager(); - - $fromSchema = $sm->createSchema(); - $schemaDiff = $comparator->compare($fromSchema, $toSchema); - - if ($noDrops) { - return $schemaDiff->toSaveSql($this->platform); - } - - return $schemaDiff->toSql($this->platform); - } - - /** - * {@inheritdoc} - */ - public function getDropSchema(Schema $dropSchema) - { - $visitor = new DropSchemaSqlCollector($this->platform); - $sm = $this->conn->getSchemaManager(); - - $fullSchema = $sm->createSchema(); - - foreach ($fullSchema->getTables() as $table) { - if ($dropSchema->hasTable($table->getName())) { - $visitor->acceptTable($table); - } - - foreach ($table->getForeignKeys() as $foreignKey) { - if (! $dropSchema->hasTable($table->getName())) { - continue; - } - - if (! $dropSchema->hasTable($foreignKey->getForeignTableName())) { - continue; - } - - $visitor->acceptForeignKey($table, $foreignKey); - } - } - - if (! $this->platform->supportsSequences()) { - return $visitor->getQueries(); - } - - foreach ($dropSchema->getSequences() as $sequence) { - $visitor->acceptSequence($sequence); - } - - foreach ($dropSchema->getTables() as $table) { - $primaryKey = $table->getPrimaryKey(); - - if ($primaryKey === null) { - continue; - } - - $columns = $primaryKey->getColumns(); - - if (count($columns) > 1) { - continue; - } - - $checkSequence = $table->getName() . '_' . $columns[0] . '_seq'; - if (! $fullSchema->hasSequence($checkSequence)) { - continue; - } - - $visitor->acceptSequence($fullSchema->getSequence($checkSequence)); - } - - return $visitor->getQueries(); - } - - /** - * {@inheritdoc} - */ - public function getDropAllSchema() - { - $sm = $this->conn->getSchemaManager(); - $visitor = new DropSchemaSqlCollector($this->platform); - - $schema = $sm->createSchema(); - $schema->visit($visitor); - - return $visitor->getQueries(); - } - - /** - * {@inheritdoc} - */ - public function createSchema(Schema $createSchema) - { - $this->processSql($this->getCreateSchema($createSchema)); - } - - /** - * {@inheritdoc} - */ - public function updateSchema(Schema $toSchema, $noDrops = false) - { - $this->processSql($this->getUpdateSchema($toSchema, $noDrops)); - } - - /** - * {@inheritdoc} - */ - public function dropSchema(Schema $dropSchema) - { - $this->processSqlSafely($this->getDropSchema($dropSchema)); - } - - /** - * {@inheritdoc} - */ - public function dropAllSchema() - { - $this->processSql($this->getDropAllSchema()); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php deleted file mode 100644 index 9fe6dbdfa..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php +++ /dev/null @@ -1,909 +0,0 @@ - [], - ]; - - /** @var SchemaConfig|null */ - protected $_schemaConfig; - - /** - * @param string $name - * @param Column[] $columns - * @param Index[] $indexes - * @param ForeignKeyConstraint[] $fkConstraints - * @param int $idGeneratorType - * @param mixed[] $options - * - * @throws Exception - */ - public function __construct( - $name, - array $columns = [], - array $indexes = [], - array $fkConstraints = [], - $idGeneratorType = 0, - array $options = [] - ) { - if (strlen($name) === 0) { - throw Exception::invalidTableName($name); - } - - $this->_setName($name); - - foreach ($columns as $column) { - $this->_addColumn($column); - } - - foreach ($indexes as $idx) { - $this->_addIndex($idx); - } - - foreach ($fkConstraints as $constraint) { - $this->_addForeignKeyConstraint($constraint); - } - - $this->_options = array_merge($this->_options, $options); - } - - /** - * @return void - */ - public function setSchemaConfig(SchemaConfig $schemaConfig) - { - $this->_schemaConfig = $schemaConfig; - } - - /** - * @return int - */ - protected function _getMaxIdentifierLength() - { - if ($this->_schemaConfig instanceof SchemaConfig) { - return $this->_schemaConfig->getMaxIdentifierLength(); - } - - return 63; - } - - /** - * Sets the Primary Key. - * - * @param string[] $columnNames - * @param string|false $indexName - * - * @return self - */ - public function setPrimaryKey(array $columnNames, $indexName = false) - { - $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true)); - - foreach ($columnNames as $columnName) { - $column = $this->getColumn($columnName); - $column->setNotnull(true); - } - - return $this; - } - - /** - * @param string[] $columnNames - * @param string|null $indexName - * @param string[] $flags - * @param mixed[] $options - * - * @return self - */ - public function addIndex(array $columnNames, $indexName = null, array $flags = [], array $options = []) - { - if ($indexName === null) { - $indexName = $this->_generateIdentifierName( - array_merge([$this->getName()], $columnNames), - 'idx', - $this->_getMaxIdentifierLength() - ); - } - - return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options)); - } - - /** - * Drops the primary key from this table. - * - * @return void - */ - public function dropPrimaryKey() - { - if ($this->_primaryKeyName === false) { - return; - } - - $this->dropIndex($this->_primaryKeyName); - $this->_primaryKeyName = false; - } - - /** - * Drops an index from this table. - * - * @param string $name The index name. - * - * @return void - * - * @throws SchemaException If the index does not exist. - */ - public function dropIndex($name) - { - $name = $this->normalizeIdentifier($name); - if (! $this->hasIndex($name)) { - throw SchemaException::indexDoesNotExist($name, $this->_name); - } - - unset($this->_indexes[$name]); - } - - /** - * @param string[] $columnNames - * @param string|null $indexName - * @param mixed[] $options - * - * @return self - */ - public function addUniqueIndex(array $columnNames, $indexName = null, array $options = []) - { - if ($indexName === null) { - $indexName = $this->_generateIdentifierName( - array_merge([$this->getName()], $columnNames), - 'uniq', - $this->_getMaxIdentifierLength() - ); - } - - return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options)); - } - - /** - * Renames an index. - * - * @param string $oldName The name of the index to rename from. - * @param string|null $newName The name of the index to rename to. - * If null is given, the index name will be auto-generated. - * - * @return self This table instance. - * - * @throws SchemaException If no index exists for the given current name - * or if an index with the given new name already exists on this table. - */ - public function renameIndex($oldName, $newName = null) - { - $oldName = $this->normalizeIdentifier($oldName); - $normalizedNewName = $this->normalizeIdentifier($newName); - - if ($oldName === $normalizedNewName) { - return $this; - } - - if (! $this->hasIndex($oldName)) { - throw SchemaException::indexDoesNotExist($oldName, $this->_name); - } - - if ($this->hasIndex($normalizedNewName)) { - throw SchemaException::indexAlreadyExists($normalizedNewName, $this->_name); - } - - $oldIndex = $this->_indexes[$oldName]; - - if ($oldIndex->isPrimary()) { - $this->dropPrimaryKey(); - - return $this->setPrimaryKey($oldIndex->getColumns(), $newName ?? false); - } - - unset($this->_indexes[$oldName]); - - if ($oldIndex->isUnique()) { - return $this->addUniqueIndex($oldIndex->getColumns(), $newName, $oldIndex->getOptions()); - } - - return $this->addIndex($oldIndex->getColumns(), $newName, $oldIndex->getFlags(), $oldIndex->getOptions()); - } - - /** - * Checks if an index begins in the order of the given columns. - * - * @param string[] $columnNames - * - * @return bool - */ - public function columnsAreIndexed(array $columnNames) - { - foreach ($this->getIndexes() as $index) { - if ($index->spansColumns($columnNames)) { - return true; - } - } - - return false; - } - - /** - * @param string[] $columnNames - * @param string $indexName - * @param bool $isUnique - * @param bool $isPrimary - * @param string[] $flags - * @param mixed[] $options - * - * @return Index - * - * @throws SchemaException - */ - private function _createIndex( - array $columnNames, - $indexName, - $isUnique, - $isPrimary, - array $flags = [], - array $options = [] - ) { - if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) { - throw SchemaException::indexNameInvalid($indexName); - } - - foreach ($columnNames as $columnName) { - if (! $this->hasColumn($columnName)) { - throw SchemaException::columnDoesNotExist($columnName, $this->_name); - } - } - - return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options); - } - - /** - * @param string $name - * @param string $typeName - * @param mixed[] $options - * - * @return Column - */ - public function addColumn($name, $typeName, array $options = []) - { - $column = new Column($name, Type::getType($typeName), $options); - - $this->_addColumn($column); - - return $column; - } - - /** - * Renames a Column. - * - * @deprecated - * - * @param string $oldName - * @param string $name - * - * @return void - * - * @throws Exception - */ - public function renameColumn($oldName, $name) - { - throw new Exception('Table#renameColumn() was removed, because it drops and recreates ' . - 'the column instead. There is no fix available, because a schema diff cannot reliably detect if a ' . - 'column was renamed or one column was created and another one dropped.'); - } - - /** - * Change Column Details. - * - * @param string $name - * @param mixed[] $options - * - * @return self - */ - public function changeColumn($name, array $options) - { - $column = $this->getColumn($name); - $column->setOptions($options); - - return $this; - } - - /** - * Drops a Column from the Table. - * - * @param string $name - * - * @return self - */ - public function dropColumn($name) - { - $name = $this->normalizeIdentifier($name); - unset($this->_columns[$name]); - - return $this; - } - - /** - * Adds a foreign key constraint. - * - * Name is inferred from the local columns. - * - * @param Table|string $foreignTable Table schema instance or table name - * @param string[] $localColumnNames - * @param string[] $foreignColumnNames - * @param mixed[] $options - * @param string|null $constraintName - * - * @return self - */ - public function addForeignKeyConstraint( - $foreignTable, - array $localColumnNames, - array $foreignColumnNames, - array $options = [], - $constraintName = null - ) { - $constraintName = $constraintName ?: $this->_generateIdentifierName( - array_merge((array) $this->getName(), $localColumnNames), - 'fk', - $this->_getMaxIdentifierLength() - ); - - return $this->addNamedForeignKeyConstraint( - $constraintName, - $foreignTable, - $localColumnNames, - $foreignColumnNames, - $options - ); - } - - /** - * Adds a foreign key constraint. - * - * Name is to be generated by the database itself. - * - * @deprecated Use {@link addForeignKeyConstraint} - * - * @param Table|string $foreignTable Table schema instance or table name - * @param string[] $localColumnNames - * @param string[] $foreignColumnNames - * @param mixed[] $options - * - * @return self - */ - public function addUnnamedForeignKeyConstraint( - $foreignTable, - array $localColumnNames, - array $foreignColumnNames, - array $options = [] - ) { - return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options); - } - - /** - * Adds a foreign key constraint with a given name. - * - * @deprecated Use {@link addForeignKeyConstraint} - * - * @param string $name - * @param Table|string $foreignTable Table schema instance or table name - * @param string[] $localColumnNames - * @param string[] $foreignColumnNames - * @param mixed[] $options - * - * @return self - * - * @throws SchemaException - */ - public function addNamedForeignKeyConstraint( - $name, - $foreignTable, - array $localColumnNames, - array $foreignColumnNames, - array $options = [] - ) { - if ($foreignTable instanceof Table) { - foreach ($foreignColumnNames as $columnName) { - if (! $foreignTable->hasColumn($columnName)) { - throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName()); - } - } - } - - foreach ($localColumnNames as $columnName) { - if (! $this->hasColumn($columnName)) { - throw SchemaException::columnDoesNotExist($columnName, $this->_name); - } - } - - $constraint = new ForeignKeyConstraint( - $localColumnNames, - $foreignTable, - $foreignColumnNames, - $name, - $options - ); - $this->_addForeignKeyConstraint($constraint); - - return $this; - } - - /** - * @param string $name - * @param mixed $value - * - * @return self - */ - public function addOption($name, $value) - { - $this->_options[$name] = $value; - - return $this; - } - - /** - * @return void - * - * @throws SchemaException - */ - protected function _addColumn(Column $column) - { - $columnName = $column->getName(); - $columnName = $this->normalizeIdentifier($columnName); - - if (isset($this->_columns[$columnName])) { - throw SchemaException::columnAlreadyExists($this->getName(), $columnName); - } - - $this->_columns[$columnName] = $column; - } - - /** - * Adds an index to the table. - * - * @return self - * - * @throws SchemaException - */ - protected function _addIndex(Index $indexCandidate) - { - $indexName = $indexCandidate->getName(); - $indexName = $this->normalizeIdentifier($indexName); - $replacedImplicitIndexes = []; - - foreach ($this->implicitIndexes as $name => $implicitIndex) { - if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) { - continue; - } - - $replacedImplicitIndexes[] = $name; - } - - if ( - (isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) || - ($this->_primaryKeyName !== false && $indexCandidate->isPrimary()) - ) { - throw SchemaException::indexAlreadyExists($indexName, $this->_name); - } - - foreach ($replacedImplicitIndexes as $name) { - unset($this->_indexes[$name], $this->implicitIndexes[$name]); - } - - if ($indexCandidate->isPrimary()) { - $this->_primaryKeyName = $indexName; - } - - $this->_indexes[$indexName] = $indexCandidate; - - return $this; - } - - /** - * @return void - */ - protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) - { - $constraint->setLocalTable($this); - - if (strlen($constraint->getName())) { - $name = $constraint->getName(); - } else { - $name = $this->_generateIdentifierName( - array_merge((array) $this->getName(), $constraint->getLocalColumns()), - 'fk', - $this->_getMaxIdentifierLength() - ); - } - - $name = $this->normalizeIdentifier($name); - - $this->_fkConstraints[$name] = $constraint; - - /* Add an implicit index (defined by the DBAL) on the foreign key - columns. If there is already a user-defined index that fulfills these - requirements drop the request. In the case of __construct() calling - this method during hydration from schema-details, all the explicitly - added indexes lead to duplicates. This creates computation overhead in - this case, however no duplicate indexes are ever added (based on - columns). */ - $indexName = $this->_generateIdentifierName( - array_merge([$this->getName()], $constraint->getColumns()), - 'idx', - $this->_getMaxIdentifierLength() - ); - - $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false); - - foreach ($this->_indexes as $existingIndex) { - if ($indexCandidate->isFullfilledBy($existingIndex)) { - return; - } - } - - $this->_addIndex($indexCandidate); - $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; - } - - /** - * Returns whether this table has a foreign key constraint with the given name. - * - * @param string $name - * - * @return bool - */ - public function hasForeignKey($name) - { - $name = $this->normalizeIdentifier($name); - - return isset($this->_fkConstraints[$name]); - } - - /** - * Returns the foreign key constraint with the given name. - * - * @param string $name The constraint name. - * - * @return ForeignKeyConstraint - * - * @throws SchemaException If the foreign key does not exist. - */ - public function getForeignKey($name) - { - $name = $this->normalizeIdentifier($name); - if (! $this->hasForeignKey($name)) { - throw SchemaException::foreignKeyDoesNotExist($name, $this->_name); - } - - return $this->_fkConstraints[$name]; - } - - /** - * Removes the foreign key constraint with the given name. - * - * @param string $name The constraint name. - * - * @return void - * - * @throws SchemaException - */ - public function removeForeignKey($name) - { - $name = $this->normalizeIdentifier($name); - if (! $this->hasForeignKey($name)) { - throw SchemaException::foreignKeyDoesNotExist($name, $this->_name); - } - - unset($this->_fkConstraints[$name]); - } - - /** - * Returns ordered list of columns (primary keys are first, then foreign keys, then the rest) - * - * @return Column[] - */ - public function getColumns() - { - $primaryKey = $this->getPrimaryKey(); - $primaryKeyColumns = []; - - if ($primaryKey !== null) { - $primaryKeyColumns = $this->filterColumns($primaryKey->getColumns()); - } - - return array_merge($primaryKeyColumns, $this->getForeignKeyColumns(), $this->_columns); - } - - /** - * Returns foreign key columns - * - * @return Column[] - */ - private function getForeignKeyColumns() - { - $foreignKeyColumns = []; - foreach ($this->getForeignKeys() as $foreignKey) { - $foreignKeyColumns = array_merge($foreignKeyColumns, $foreignKey->getColumns()); - } - - return $this->filterColumns($foreignKeyColumns); - } - - /** - * Returns only columns that have specified names - * - * @param string[] $columnNames - * - * @return Column[] - */ - private function filterColumns(array $columnNames) - { - return array_filter($this->_columns, static function (string $columnName) use ($columnNames) { - return in_array($columnName, $columnNames, true); - }, ARRAY_FILTER_USE_KEY); - } - - /** - * Returns whether this table has a Column with the given name. - * - * @param string $name The column name. - * - * @return bool - */ - public function hasColumn($name) - { - $name = $this->normalizeIdentifier($name); - - return isset($this->_columns[$name]); - } - - /** - * Returns the Column with the given name. - * - * @param string $name The column name. - * - * @return Column - * - * @throws SchemaException If the column does not exist. - */ - public function getColumn($name) - { - $name = $this->normalizeIdentifier($name); - if (! $this->hasColumn($name)) { - throw SchemaException::columnDoesNotExist($name, $this->_name); - } - - return $this->_columns[$name]; - } - - /** - * Returns the primary key. - * - * @return Index|null The primary key, or null if this Table has no primary key. - */ - public function getPrimaryKey() - { - if ($this->_primaryKeyName !== false) { - return $this->getIndex($this->_primaryKeyName); - } - - return null; - } - - /** - * Returns the primary key columns. - * - * @return string[] - * - * @throws Exception - */ - public function getPrimaryKeyColumns() - { - $primaryKey = $this->getPrimaryKey(); - - if ($primaryKey === null) { - throw new Exception('Table ' . $this->getName() . ' has no primary key.'); - } - - return $primaryKey->getColumns(); - } - - /** - * Returns whether this table has a primary key. - * - * @return bool - */ - public function hasPrimaryKey() - { - return $this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName); - } - - /** - * Returns whether this table has an Index with the given name. - * - * @param string $name The index name. - * - * @return bool - */ - public function hasIndex($name) - { - $name = $this->normalizeIdentifier($name); - - return isset($this->_indexes[$name]); - } - - /** - * Returns the Index with the given name. - * - * @param string $name The index name. - * - * @return Index - * - * @throws SchemaException If the index does not exist. - */ - public function getIndex($name) - { - $name = $this->normalizeIdentifier($name); - if (! $this->hasIndex($name)) { - throw SchemaException::indexDoesNotExist($name, $this->_name); - } - - return $this->_indexes[$name]; - } - - /** - * @return Index[] - */ - public function getIndexes() - { - return $this->_indexes; - } - - /** - * Returns the foreign key constraints. - * - * @return ForeignKeyConstraint[] - */ - public function getForeignKeys() - { - return $this->_fkConstraints; - } - - /** - * @param string $name - * - * @return bool - */ - public function hasOption($name) - { - return isset($this->_options[$name]); - } - - /** - * @param string $name - * - * @return mixed - */ - public function getOption($name) - { - return $this->_options[$name]; - } - - /** - * @return mixed[] - */ - public function getOptions() - { - return $this->_options; - } - - /** - * @return void - */ - public function visit(Visitor $visitor) - { - $visitor->acceptTable($this); - - foreach ($this->getColumns() as $column) { - $visitor->acceptColumn($this, $column); - } - - foreach ($this->getIndexes() as $index) { - $visitor->acceptIndex($this, $index); - } - - foreach ($this->getForeignKeys() as $constraint) { - $visitor->acceptForeignKey($this, $constraint); - } - } - - /** - * Clone of a Table triggers a deep clone of all affected assets. - * - * @return void - */ - public function __clone() - { - foreach ($this->_columns as $k => $column) { - $this->_columns[$k] = clone $column; - } - - foreach ($this->_indexes as $k => $index) { - $this->_indexes[$k] = clone $index; - } - - foreach ($this->_fkConstraints as $k => $fk) { - $this->_fkConstraints[$k] = clone $fk; - $this->_fkConstraints[$k]->setLocalTable($this); - } - } - - /** - * Normalizes a given identifier. - * - * Trims quotes and lowercases the given identifier. - * - * @param string|null $identifier The identifier to normalize. - * - * @return string The normalized identifier. - */ - private function normalizeIdentifier($identifier) - { - if ($identifier === null) { - return ''; - } - - return $this->trimQuotes(strtolower($identifier)); - } - - public function setComment(?string $comment): self - { - // For keeping backward compatibility with MySQL in previous releases, table comments are stored as options. - $this->addOption('comment', $comment); - - return $this; - } - - public function getComment(): ?string - { - return $this->_options['comment'] ?? null; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php deleted file mode 100644 index 05c842830..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php +++ /dev/null @@ -1,46 +0,0 @@ - "client" to the ShardChoser interface. - * - An exception is thrown if trying to switch shards during an open - * transaction. - * - * Instantiation through the DriverManager looks like: - * - * @deprecated - * - * @example - * - * $conn = DriverManager::getConnection(array( - * 'wrapperClass' => 'Doctrine\DBAL\Sharding\PoolingShardConnection', - * 'driver' => 'pdo_mysql', - * 'global' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''), - * 'shards' => array( - * array('id' => 1, 'user' => 'slave1', 'password', 'host' => '', 'dbname' => ''), - * array('id' => 2, 'user' => 'slave2', 'password', 'host' => '', 'dbname' => ''), - * ), - * 'shardChoser' => 'Doctrine\DBAL\Sharding\ShardChoser\MultiTenantShardChoser', - * )); - * $shardManager = $conn->getShardManager(); - * $shardManager->selectGlobal(); - * $shardManager->selectShard($value); - */ -class PoolingShardConnection extends Connection -{ - /** @var DriverConnection[] */ - private $activeConnections = []; - - /** @var string|int|null */ - private $activeShardId; - - /** @var mixed[] */ - private $connectionParameters = []; - - /** - * {@inheritDoc} - * - * @internal The connection can be only instantiated by the driver manager. - * - * @throws InvalidArgumentException - */ - public function __construct( - array $params, - Driver $driver, - ?Configuration $config = null, - ?EventManager $eventManager = null - ) { - if (! isset($params['global'], $params['shards'])) { - throw new InvalidArgumentException("Connection Parameters require 'global' and 'shards' configurations."); - } - - if (! isset($params['shardChoser'])) { - throw new InvalidArgumentException("Missing Shard Choser configuration 'shardChoser'"); - } - - if (is_string($params['shardChoser'])) { - $params['shardChoser'] = new $params['shardChoser'](); - } - - if (! ($params['shardChoser'] instanceof ShardChoser)) { - throw new InvalidArgumentException( - "The 'shardChoser' configuration is not a valid instance of " . ShardChoser::class - ); - } - - $this->connectionParameters[0] = array_merge($params, $params['global']); - - foreach ($params['shards'] as $shard) { - if (! isset($shard['id'])) { - throw new InvalidArgumentException( - "Missing 'id' for one configured shard. Please specify a unique shard-id." - ); - } - - if (! is_numeric($shard['id']) || $shard['id'] < 1) { - throw new InvalidArgumentException('Shard Id has to be a non-negative number.'); - } - - if (isset($this->connectionParameters[$shard['id']])) { - throw new InvalidArgumentException('Shard ' . $shard['id'] . ' is duplicated in the configuration.'); - } - - $this->connectionParameters[$shard['id']] = array_merge($params, $shard); - } - - parent::__construct($params, $driver, $config, $eventManager); - } - - /** - * Get active shard id. - * - * @return string|int|null - */ - public function getActiveShardId() - { - return $this->activeShardId; - } - - /** - * {@inheritdoc} - */ - public function getParams() - { - return $this->activeShardId - ? $this->connectionParameters[$this->activeShardId] - : $this->connectionParameters[0]; - } - - /** - * {@inheritdoc} - */ - public function getHost() - { - $params = $this->getParams(); - - return $params['host'] ?? parent::getHost(); - } - - /** - * {@inheritdoc} - */ - public function getPort() - { - $params = $this->getParams(); - - return $params['port'] ?? parent::getPort(); - } - - /** - * {@inheritdoc} - */ - public function getUsername() - { - $params = $this->getParams(); - - return $params['user'] ?? parent::getUsername(); - } - - /** - * {@inheritdoc} - */ - public function getPassword() - { - $params = $this->getParams(); - - return $params['password'] ?? parent::getPassword(); - } - - /** - * Connects to a given shard. - * - * @param string|int|null $shardId - * - * @return bool - * - * @throws ShardingException - */ - public function connect($shardId = null) - { - if ($shardId === null && $this->_conn) { - return false; - } - - if ($shardId !== null && $shardId === $this->activeShardId) { - return false; - } - - if ($this->getTransactionNestingLevel() > 0) { - throw new ShardingException('Cannot switch shard when transaction is active.'); - } - - $activeShardId = $this->activeShardId = (int) $shardId; - - if (isset($this->activeConnections[$activeShardId])) { - $this->_conn = $this->activeConnections[$activeShardId]; - - return false; - } - - $this->_conn = $this->activeConnections[$activeShardId] = $this->connectTo($activeShardId); - - if ($this->_eventManager->hasListeners(Events::postConnect)) { - $eventArgs = new ConnectionEventArgs($this); - $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); - } - - return true; - } - - /** - * Connects to a specific connection. - * - * @param string|int $shardId - * - * @return \Doctrine\DBAL\Driver\Connection - */ - protected function connectTo($shardId) - { - $params = $this->getParams(); - - $driverOptions = $params['driverOptions'] ?? []; - - $connectionParams = $this->connectionParameters[$shardId]; - - $user = $connectionParams['user'] ?? null; - $password = $connectionParams['password'] ?? null; - - return $this->_driver->connect($connectionParams, $user, $password, $driverOptions); - } - - /** - * @param string|int|null $shardId - * - * @return bool - */ - public function isConnected($shardId = null) - { - if ($shardId === null) { - return $this->_conn !== null; - } - - return isset($this->activeConnections[$shardId]); - } - - /** - * @return void - */ - public function close() - { - $this->_conn = null; - $this->activeConnections = []; - $this->activeShardId = null; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php deleted file mode 100644 index 739cfaba6..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php +++ /dev/null @@ -1,110 +0,0 @@ -getParams(); - $this->conn = $conn; - $this->choser = $params['shardChoser']; - } - - /** - * {@inheritDoc} - */ - public function selectGlobal() - { - $this->conn->connect(0); - $this->currentDistributionValue = null; - } - - /** - * {@inheritDoc} - */ - public function selectShard($distributionValue) - { - $shardId = $this->choser->pickShard($distributionValue, $this->conn); - $this->conn->connect($shardId); - $this->currentDistributionValue = $distributionValue; - } - - /** - * {@inheritDoc} - */ - public function getCurrentDistributionValue() - { - return $this->currentDistributionValue; - } - - /** - * {@inheritDoc} - */ - public function getShards() - { - $params = $this->conn->getParams(); - $shards = []; - - foreach ($params['shards'] as $shard) { - $shards[] = ['id' => $shard['id']]; - } - - return $shards; - } - - /** - * {@inheritDoc} - * - * @throws RuntimeException - */ - public function queryAll($sql, array $params, array $types) - { - $shards = $this->getShards(); - if (! $shards) { - throw new RuntimeException('No shards found.'); - } - - $result = []; - $oldDistribution = $this->getCurrentDistributionValue(); - - foreach ($shards as $shard) { - $this->conn->connect($shard['id']); - foreach ($this->conn->fetchAllAssociative($sql, $params, $types) as $row) { - $result[] = $row; - } - } - - if ($oldDistribution === null) { - $this->selectGlobal(); - } else { - $this->selectShard($oldDistribution); - } - - return $result; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php deleted file mode 100644 index 5fa2aa782..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php +++ /dev/null @@ -1,284 +0,0 @@ -shardManager = $shardManager; - $this->synchronizer = $sync ?: new SingleDatabaseSynchronizer($conn); - } - - /** - * {@inheritdoc} - */ - public function getCreateSchema(Schema $createSchema) - { - $sql = []; - - [$global, $federation] = $this->partitionSchema($createSchema); - - $globalSql = $this->synchronizer->getCreateSchema($global); - if ($globalSql) { - $sql[] = "-- Create Root Federation\n" . - 'USE FEDERATION ROOT WITH RESET;'; - $sql = array_merge($sql, $globalSql); - } - - $federationSql = $this->synchronizer->getCreateSchema($federation); - - if ($federationSql) { - $defaultValue = $this->getFederationTypeDefaultValue(); - - $sql[] = $this->getCreateFederationStatement(); - $sql[] = 'USE FEDERATION ' . $this->shardManager->getFederationName() - . ' (' . $this->shardManager->getDistributionKey() . ' = ' . $defaultValue . ')' - . ' WITH RESET, FILTERING = OFF;'; - $sql = array_merge($sql, $federationSql); - } - - return $sql; - } - - /** - * {@inheritdoc} - */ - public function getUpdateSchema(Schema $toSchema, $noDrops = false) - { - return $this->work($toSchema, static function ($synchronizer, $schema) use ($noDrops) { - return $synchronizer->getUpdateSchema($schema, $noDrops); - }); - } - - /** - * {@inheritdoc} - */ - public function getDropSchema(Schema $dropSchema) - { - return $this->work($dropSchema, static function ($synchronizer, $schema) { - return $synchronizer->getDropSchema($schema); - }); - } - - /** - * {@inheritdoc} - */ - public function createSchema(Schema $createSchema) - { - $this->processSql($this->getCreateSchema($createSchema)); - } - - /** - * {@inheritdoc} - */ - public function updateSchema(Schema $toSchema, $noDrops = false) - { - $this->processSql($this->getUpdateSchema($toSchema, $noDrops)); - } - - /** - * {@inheritdoc} - */ - public function dropSchema(Schema $dropSchema) - { - $this->processSqlSafely($this->getDropSchema($dropSchema)); - } - - /** - * {@inheritdoc} - */ - public function getDropAllSchema() - { - $this->shardManager->selectGlobal(); - $globalSql = $this->synchronizer->getDropAllSchema(); - - $sql = []; - - if ($globalSql) { - $sql[] = "-- Work on Root Federation\nUSE FEDERATION ROOT WITH RESET;"; - $sql = array_merge($sql, $globalSql); - } - - $shards = $this->shardManager->getShards(); - foreach ($shards as $shard) { - $this->shardManager->selectShard($shard['rangeLow']); - - $federationSql = $this->synchronizer->getDropAllSchema(); - if (! $federationSql) { - continue; - } - - $sql[] = '-- Work on Federation ID ' . $shard['id'] . "\n" . - 'USE FEDERATION ' . $this->shardManager->getFederationName() - . ' (' . $this->shardManager->getDistributionKey() . ' = ' . $shard['rangeLow'] . ')' - . ' WITH RESET, FILTERING = OFF;'; - $sql = array_merge($sql, $federationSql); - } - - $sql[] = 'USE FEDERATION ROOT WITH RESET;'; - $sql[] = 'DROP FEDERATION ' . $this->shardManager->getFederationName(); - - return $sql; - } - - /** - * {@inheritdoc} - */ - public function dropAllSchema() - { - $this->processSqlSafely($this->getDropAllSchema()); - } - - /** - * @return Schema[] - */ - private function partitionSchema(Schema $schema) - { - return [ - $this->extractSchemaFederation($schema, false), - $this->extractSchemaFederation($schema, true), - ]; - } - - /** - * @param bool $isFederation - * - * @return Schema - * - * @throws RuntimeException - */ - private function extractSchemaFederation(Schema $schema, $isFederation) - { - $partitionedSchema = clone $schema; - - foreach ($partitionedSchema->getTables() as $table) { - if ($isFederation) { - $table->addOption(self::FEDERATION_DISTRIBUTION_NAME, $this->shardManager->getDistributionKey()); - } - - if ($table->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) { - $partitionedSchema->dropTable($table->getName()); - } else { - foreach ($table->getForeignKeys() as $fk) { - $foreignTable = $schema->getTable($fk->getForeignTableName()); - if ($foreignTable->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) { - throw new RuntimeException('Cannot have foreign key between global/federation.'); - } - } - } - } - - return $partitionedSchema; - } - - /** - * Work on the Global/Federation based on currently existing shards and - * perform the given operation on the underlying schema synchronizer given - * the different partitioned schema instances. - * - * @return string[] - */ - private function work(Schema $schema, Closure $operation) - { - [$global, $federation] = $this->partitionSchema($schema); - $sql = []; - - $this->shardManager->selectGlobal(); - $globalSql = $operation($this->synchronizer, $global); - - if ($globalSql) { - $sql[] = "-- Work on Root Federation\nUSE FEDERATION ROOT WITH RESET;"; - $sql = array_merge($sql, $globalSql); - } - - $shards = $this->shardManager->getShards(); - - foreach ($shards as $shard) { - $this->shardManager->selectShard($shard['rangeLow']); - - $federationSql = $operation($this->synchronizer, $federation); - if (! $federationSql) { - continue; - } - - $sql[] = '-- Work on Federation ID ' . $shard['id'] . "\n" - . 'USE FEDERATION ' . $this->shardManager->getFederationName() - . ' (' . $this->shardManager->getDistributionKey() . ' = ' . $shard['rangeLow'] . ')' - . ' WITH RESET, FILTERING = OFF;'; - $sql = array_merge($sql, $federationSql); - } - - return $sql; - } - - /** - * @return string - */ - private function getFederationTypeDefaultValue() - { - $federationType = Type::getType($this->shardManager->getDistributionType()); - - switch ($federationType->getName()) { - case Types::GUID: - $defaultValue = '00000000-0000-0000-0000-000000000000'; - break; - case Types::INTEGER: - case Types::SMALLINT: - case Types::BIGINT: - $defaultValue = '0'; - break; - default: - $defaultValue = ''; - break; - } - - return $defaultValue; - } - - /** - * @return string - */ - private function getCreateFederationStatement() - { - $federationType = Type::getType($this->shardManager->getDistributionType()); - $federationTypeSql = $federationType->getSQLDeclaration([], $this->conn->getDatabasePlatform()); - - return "--Create Federation\n" - . 'CREATE FEDERATION ' . $this->shardManager->getFederationName() - . ' (' . $this->shardManager->getDistributionKey() - . ' ' . $federationTypeSql . ' RANGE)'; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php deleted file mode 100644 index 83ad879f2..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php +++ /dev/null @@ -1,218 +0,0 @@ -conn = $conn; - $params = $conn->getParams(); - - if (! isset($params['sharding']['federationName'])) { - throw ShardingException::missingDefaultFederationName(); - } - - if (! isset($params['sharding']['distributionKey'])) { - throw ShardingException::missingDefaultDistributionKey(); - } - - if (! isset($params['sharding']['distributionType'])) { - throw ShardingException::missingDistributionType(); - } - - $this->federationName = $params['sharding']['federationName']; - $this->distributionKey = $params['sharding']['distributionKey']; - $this->distributionType = $params['sharding']['distributionType']; - $this->filteringEnabled = (bool) ($params['sharding']['filteringEnabled'] ?? false); - } - - /** - * Gets the name of the federation. - * - * @return string - */ - public function getFederationName() - { - return $this->federationName; - } - - /** - * Gets the distribution key. - * - * @return string - */ - public function getDistributionKey() - { - return $this->distributionKey; - } - - /** - * Gets the Doctrine Type name used for the distribution. - * - * @return string - */ - public function getDistributionType() - { - return $this->distributionType; - } - - /** - * Sets Enabled/Disable filtering on the fly. - * - * @param bool $flag - * - * @return void - */ - public function setFilteringEnabled($flag) - { - $this->filteringEnabled = (bool) $flag; - } - - /** - * {@inheritDoc} - */ - public function selectGlobal() - { - if ($this->conn->isTransactionActive()) { - throw ShardingException::activeTransaction(); - } - - $sql = 'USE FEDERATION ROOT WITH RESET'; - $this->conn->exec($sql); - $this->currentDistributionValue = null; - } - - /** - * {@inheritDoc} - */ - public function selectShard($distributionValue) - { - if ($this->conn->isTransactionActive()) { - throw ShardingException::activeTransaction(); - } - - $platform = $this->conn->getDatabasePlatform(); - $sql = sprintf( - 'USE FEDERATION %s (%s = %s) WITH RESET, FILTERING = %s;', - $platform->quoteIdentifier($this->federationName), - $platform->quoteIdentifier($this->distributionKey), - $this->conn->quote($distributionValue), - ($this->filteringEnabled ? 'ON' : 'OFF') - ); - - $this->conn->exec($sql); - $this->currentDistributionValue = $distributionValue; - } - - /** - * {@inheritDoc} - */ - public function getCurrentDistributionValue() - { - return $this->currentDistributionValue; - } - - /** - * {@inheritDoc} - */ - public function getShards() - { - $sql = 'SELECT member_id as id, - distribution_name as distribution_key, - CAST(range_low AS CHAR) AS rangeLow, - CAST(range_high AS CHAR) AS rangeHigh - FROM sys.federation_member_distributions d - INNER JOIN sys.federations f ON f.federation_id = d.federation_id - WHERE f.name = ' . $this->conn->quote($this->federationName); - - return $this->conn->fetchAllAssociative($sql); - } - - /** - * {@inheritDoc} - */ - public function queryAll($sql, array $params = [], array $types = []) - { - $shards = $this->getShards(); - if (! $shards) { - throw new RuntimeException('No shards found for ' . $this->federationName); - } - - $result = []; - $oldDistribution = $this->getCurrentDistributionValue(); - - foreach ($shards as $shard) { - $this->selectShard($shard['rangeLow']); - foreach ($this->conn->fetchAllAssociative($sql, $params, $types) as $row) { - $result[] = $row; - } - } - - if ($oldDistribution === null) { - $this->selectGlobal(); - } else { - $this->selectShard($oldDistribution); - } - - return $result; - } - - /** - * Splits Federation at a given distribution value. - * - * @param mixed $splitDistributionValue - * - * @return void - */ - public function splitFederation($splitDistributionValue) - { - $type = Type::getType($this->distributionType); - - $sql = 'ALTER FEDERATION ' . $this->getFederationName() . ' ' . - 'SPLIT AT (' . $this->getDistributionKey() . ' = ' . - $this->conn->quote($splitDistributionValue, $type->getBindingType()) . ')'; - $this->conn->exec($sql); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php deleted file mode 100644 index d9ef9e896..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php +++ /dev/null @@ -1,152 +0,0 @@ -excludedTables = $excludedTables; - $this->tenantColumnName = $tenantColumnName; - $this->distributionName = $distributionName ?: $tenantColumnName; - } - - /** - * {@inheritdoc} - */ - public function acceptTable(Table $table) - { - if (in_array($table->getName(), $this->excludedTables)) { - return; - } - - $table->addColumn($this->tenantColumnName, $this->tenantColumnType, [ - 'default' => "federation_filtering_value('" . $this->distributionName . "')", - ]); - - $clusteredIndex = $this->getClusteredIndex($table); - - $indexColumns = $clusteredIndex->getColumns(); - $indexColumns[] = $this->tenantColumnName; - - if ($clusteredIndex->isPrimary()) { - $table->dropPrimaryKey(); - $table->setPrimaryKey($indexColumns); - } else { - $table->dropIndex($clusteredIndex->getName()); - $table->addIndex($indexColumns, $clusteredIndex->getName()); - $table->getIndex($clusteredIndex->getName())->addFlag('clustered'); - } - } - - /** - * @param Table $table - * - * @return Index - * - * @throws RuntimeException - */ - private function getClusteredIndex($table) - { - foreach ($table->getIndexes() as $index) { - if ($index->isPrimary() && ! $index->hasFlag('nonclustered')) { - return $index; - } - - if ($index->hasFlag('clustered')) { - return $index; - } - } - - throw new RuntimeException('No clustered index found on table ' . $table->getName()); - } - - /** - * {@inheritdoc} - */ - public function acceptSchema(Schema $schema) - { - } - - /** - * {@inheritdoc} - */ - public function acceptColumn(Table $table, Column $column) - { - } - - /** - * {@inheritdoc} - */ - public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) - { - } - - /** - * {@inheritdoc} - */ - public function acceptIndex(Table $table, Index $index) - { - } - - /** - * {@inheritdoc} - */ - public function acceptSequence(Sequence $sequence) - { - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php deleted file mode 100644 index f44c3af43..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php +++ /dev/null @@ -1,22 +0,0 @@ -Statement for the given SQL and Connection. - * - * @internal The statement can be only instantiated by {@link Connection}. - * - * @param string $sql The SQL of the statement. - * @param Connection $conn The connection on which the statement should be executed. - */ - public function __construct($sql, Connection $conn) - { - $this->sql = $sql; - $this->stmt = $conn->getWrappedConnection()->prepare($sql); - $this->conn = $conn; - $this->platform = $conn->getDatabasePlatform(); - } - - /** - * Binds a parameter value to the statement. - * - * The value can optionally be bound with a PDO binding type or a DBAL mapping type. - * If bound with a DBAL mapping type, the binding type is derived from the mapping - * type and the value undergoes the conversion routines of the mapping type before - * being bound. - * - * @param string|int $param The name or position of the parameter. - * @param mixed $value The value of the parameter. - * @param mixed $type Either a PDO binding type or a DBAL mapping type name or instance. - * - * @return bool TRUE on success, FALSE on failure. - */ - public function bindValue($param, $value, $type = ParameterType::STRING) - { - $this->params[$param] = $value; - $this->types[$param] = $type; - if ($type !== null) { - if (is_string($type)) { - $type = Type::getType($type); - } - - if ($type instanceof Type) { - $value = $type->convertToDatabaseValue($value, $this->platform); - $bindingType = $type->getBindingType(); - } else { - $bindingType = $type; - } - - return $this->stmt->bindValue($param, $value, $bindingType); - } - - return $this->stmt->bindValue($param, $value); - } - - /** - * Binds a parameter to a value by reference. - * - * Binding a parameter by reference does not support DBAL mapping types. - * - * @param string|int $param The name or position of the parameter. - * @param mixed $variable The reference to the variable to bind. - * @param int $type The PDO binding type. - * @param int|null $length Must be specified when using an OUT bind - * so that PHP allocates enough memory to hold the returned value. - * - * @return bool TRUE on success, FALSE on failure. - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) - { - $this->params[$param] = $variable; - $this->types[$param] = $type; - - if ($this->stmt instanceof PDOStatement) { - $length = $length ?? 0; - } - - return $this->stmt->bindParam($param, $variable, $type, $length); - } - - /** - * Executes the statement with the currently bound parameters. - * - * @deprecated Statement::execute() is deprecated, use Statement::executeQuery() or executeStatement() instead - * - * @param mixed[]|null $params - * - * @return bool TRUE on success, FALSE on failure. - * - * @throws Exception - */ - public function execute($params = null) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4580', - 'Statement::execute() is deprecated, use Statement::executeQuery() or Statement::executeStatement() instead' - ); - - if (is_array($params)) { - $this->params = $params; - } - - $logger = $this->conn->getConfiguration()->getSQLLogger(); - if ($logger) { - $logger->startQuery($this->sql, $this->params, $this->types); - } - - try { - $stmt = $this->stmt->execute($params); - } catch (Throwable $ex) { - if ($logger) { - $logger->stopQuery(); - } - - $this->conn->handleExceptionDuringQuery($ex, $this->sql, $this->params, $this->types); - } - - if ($logger) { - $logger->stopQuery(); - } - - return $stmt; - } - - /** - * Executes the statement with the currently bound parameters and return result. - * - * @param mixed[] $params - * - * @throws Exception - */ - public function executeQuery(array $params = []): BaseResult - { - if ($params === []) { - $params = null; // Workaround as long execute() exists and used internally. - } - - $this->execute($params); - - return new ForwardCompatibility\Result($this); - } - - /** - * Executes the statement with the currently bound parameters and return affected rows. - * - * @param mixed[] $params - * - * @throws Exception - */ - public function executeStatement(array $params = []): int - { - if ($params === []) { - $params = null; // Workaround as long execute() exists and used internally. - } - - $this->execute($params); - - return $this->rowCount(); - } - - /** - * Closes the cursor, freeing the database resources used by this statement. - * - * @deprecated Use Result::free() instead. - * - * @return bool TRUE on success, FALSE on failure. - */ - public function closeCursor() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4049', - 'Statement::closeCursor() is deprecated, use Result::free() instead.' - ); - - return $this->stmt->closeCursor(); - } - - /** - * Returns the number of columns in the result set. - * - * @return int - */ - public function columnCount() - { - return $this->stmt->columnCount(); - } - - /** - * Fetches the SQLSTATE associated with the last operation on the statement. - * - * @deprecated The error information is available via exceptions. - * - * @return string|int|bool - */ - public function errorCode() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3507', - 'Connection::errorCode() is deprecated, use getCode() or getSQLState() on Exception instead.' - ); - - return $this->stmt->errorCode(); - } - - /** - * {@inheritDoc} - * - * @deprecated The error information is available via exceptions. - */ - public function errorInfo() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3507', - 'Connection::errorInfo() is deprecated, use getCode() or getSQLState() on Exception instead.' - ); - - return $this->stmt->errorInfo(); - } - - /** - * {@inheritdoc} - * - * @deprecated Use one of the fetch- or iterate-related methods. - */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Statement::setFetchMode() is deprecated, use explicit Result::fetch*() APIs instead.' - ); - - if ($arg2 === null) { - return $this->stmt->setFetchMode($fetchMode); - } - - if ($arg3 === null) { - return $this->stmt->setFetchMode($fetchMode, $arg2); - } - - return $this->stmt->setFetchMode($fetchMode, $arg2, $arg3); - } - - /** - * Required by interface IteratorAggregate. - * - * @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead. - * - * {@inheritdoc} - */ - #[ReturnTypeWillChange] - public function getIterator() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Statement::getIterator() is deprecated, use Result::iterateNumeric(), iterateAssociative() ' . - 'or iterateColumn() instead.' - ); - - return $this->stmt; - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead. - */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Statement::fetch() is deprecated, use Result::fetchNumeric(), fetchAssociative() or fetchOne() instead.' - ); - - return $this->stmt->fetch(...func_get_args()); - } - - /** - * {@inheritdoc} - * - * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. - */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Statement::fetchAll() is deprecated, use Result::fetchAllNumeric(), fetchAllAssociative() or ' . - 'fetchFirstColumn() instead.' - ); - - if ($ctorArgs !== null) { - return $this->stmt->fetchAll($fetchMode, $fetchArgument, $ctorArgs); - } - - if ($fetchArgument !== null) { - return $this->stmt->fetchAll($fetchMode, $fetchArgument); - } - - return $this->stmt->fetchAll($fetchMode); - } - - /** - * {@inheritDoc} - * - * @deprecated Use fetchOne() instead. - */ - public function fetchColumn($columnIndex = 0) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4019', - 'Statement::fetchColumn() is deprecated, use Result::fetchOne() instead.' - ); - - return $this->stmt->fetchColumn($columnIndex); - } - - /** - * {@inheritdoc} - * - * @deprecated Use Result::fetchNumeric() instead - * - * @throws Exception - */ - public function fetchNumeric() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4554', - 'Statement::%s() is deprecated, use Result::%s() instead.', - __FUNCTION__, - __FUNCTION__ - ); - - try { - if ($this->stmt instanceof Result) { - return $this->stmt->fetchNumeric(); - } - - return $this->stmt->fetch(FetchMode::NUMERIC); - } catch (Exception $e) { - $this->conn->handleDriverException($e); - } - } - - /** - * {@inheritdoc} - * - * @deprecated Use Result::fetchAssociative() instead - * - * @throws Exception - */ - public function fetchAssociative() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4554', - 'Statement::%s() is deprecated, use Result::%s() instead.', - __FUNCTION__, - __FUNCTION__ - ); - - try { - if ($this->stmt instanceof Result) { - return $this->stmt->fetchAssociative(); - } - - return $this->stmt->fetch(FetchMode::ASSOCIATIVE); - } catch (Exception $e) { - $this->conn->handleDriverException($e); - } - } - - /** - * {@inheritDoc} - * - * @deprecated Use Result::fetchOne() instead - * - * @throws Exception - */ - public function fetchOne() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4554', - 'Statement::%s() is deprecated, use Result::%s() instead.', - __FUNCTION__, - __FUNCTION__ - ); - - try { - if ($this->stmt instanceof Result) { - return $this->stmt->fetchOne(); - } - - return $this->stmt->fetch(FetchMode::COLUMN); - } catch (Exception $e) { - $this->conn->handleDriverException($e); - } - } - - /** - * {@inheritdoc} - * - * @deprecated Use Result::fetchAllNumeric() instead - * - * @throws Exception - */ - public function fetchAllNumeric(): array - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4554', - 'Statement::%s() is deprecated, use Result::%s() instead.', - __FUNCTION__, - __FUNCTION__ - ); - - try { - if ($this->stmt instanceof Result) { - return $this->stmt->fetchAllNumeric(); - } - - return $this->stmt->fetchAll(FetchMode::NUMERIC); - } catch (Exception $e) { - $this->conn->handleDriverException($e); - } - } - - /** - * {@inheritdoc} - * - * @deprecated Use Result::fetchAllAssociative() instead - * - * @throws Exception - */ - public function fetchAllAssociative(): array - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4554', - 'Statement::%s() is deprecated, use Result::%s() instead.', - __FUNCTION__, - __FUNCTION__ - ); - - try { - if ($this->stmt instanceof Result) { - return $this->stmt->fetchAllAssociative(); - } - - return $this->stmt->fetchAll(FetchMode::ASSOCIATIVE); - } catch (Exception $e) { - $this->conn->handleDriverException($e); - } - } - - /** - * Returns an associative array with the keys mapped to the first column and the values mapped to the second column. - * - * The result must contain at least two columns. - * - * @deprecated Use Result::fetchAllKeyValue() instead - * - * @return array - * - * @throws Exception - */ - public function fetchAllKeyValue(): array - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4554', - 'Statement::%s() is deprecated, use Result::%s() instead.', - __FUNCTION__, - __FUNCTION__ - ); - - $this->ensureHasKeyValue(); - - $data = []; - - foreach ($this->fetchAllNumeric() as [$key, $value]) { - $data[$key] = $value; - } - - return $data; - } - - /** - * Returns an associative array with the keys mapped to the first column and the values being - * an associative array representing the rest of the columns and their values. - * - * @deprecated Use Result::fetchAllAssociativeIndexed() instead - * - * @return array> - * - * @throws Exception - */ - public function fetchAllAssociativeIndexed(): array - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4554', - 'Statement::%s() is deprecated, use Result::%s() instead.', - __FUNCTION__, - __FUNCTION__ - ); - - $data = []; - - foreach ($this->fetchAll(FetchMode::ASSOCIATIVE) as $row) { - $data[array_shift($row)] = $row; - } - - return $data; - } - - /** - * {@inheritdoc} - * - * @deprecated Use Result::fetchFirstColumn() instead - * - * @throws Exception - */ - public function fetchFirstColumn(): array - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4554', - 'Statement::%s() is deprecated, use Result::%s() instead.', - __FUNCTION__, - __FUNCTION__ - ); - - try { - if ($this->stmt instanceof Result) { - return $this->stmt->fetchFirstColumn(); - } - - return $this->stmt->fetchAll(FetchMode::COLUMN); - } catch (Exception $e) { - $this->conn->handleDriverException($e); - } - } - - /** - * {@inheritDoc} - * - * @deprecated Use Result::iterateNumeric() instead - * - * @return Traversable> - * - * @throws Exception - */ - public function iterateNumeric(): Traversable - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4554', - 'Statement::%s() is deprecated, use Result::%s() instead.', - __FUNCTION__, - __FUNCTION__ - ); - - try { - if ($this->stmt instanceof Result) { - while (($row = $this->stmt->fetchNumeric()) !== false) { - yield $row; - } - } else { - while (($row = $this->stmt->fetch(FetchMode::NUMERIC)) !== false) { - yield $row; - } - } - } catch (Exception $e) { - $this->conn->handleDriverException($e); - } - } - - /** - * {@inheritDoc} - * - * @deprecated Use Result::iterateAssociative() instead - * - * @return Traversable> - * - * @throws Exception - */ - public function iterateAssociative(): Traversable - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4554', - 'Statement::%s() is deprecated, use Result::%s() instead.', - __FUNCTION__, - __FUNCTION__ - ); - - try { - if ($this->stmt instanceof Result) { - while (($row = $this->stmt->fetchAssociative()) !== false) { - yield $row; - } - } else { - while (($row = $this->stmt->fetch(FetchMode::ASSOCIATIVE)) !== false) { - yield $row; - } - } - } catch (Exception $e) { - $this->conn->handleDriverException($e); - } - } - - /** - * Returns an iterator over the result set with the keys mapped to the first column - * and the values mapped to the second column. - * - * The result must contain at least two columns. - * - * @deprecated Use Result::iterateKeyValue() instead - * - * @return Traversable - * - * @throws Exception - */ - public function iterateKeyValue(): Traversable - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4554', - 'Statement::%s() is deprecated, use Result::%s() instead.', - __FUNCTION__, - __FUNCTION__ - ); - - $this->ensureHasKeyValue(); - - foreach ($this->iterateNumeric() as [$key, $value]) { - yield $key => $value; - } - } - - /** - * Returns an iterator over the result set with the keys mapped to the first column and the values being - * an associative array representing the rest of the columns and their values. - * - * @deprecated Use Result::iterateAssociativeIndexed() instead - * - * @return Traversable> - * - * @throws Exception - */ - public function iterateAssociativeIndexed(): Traversable - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4554', - 'Statement::%s() is deprecated, use Result::%s() instead.', - __FUNCTION__, - __FUNCTION__ - ); - - while (($row = $this->stmt->fetch(FetchMode::ASSOCIATIVE)) !== false) { - yield array_shift($row) => $row; - } - } - - /** - * {@inheritDoc} - * - * @deprecated Use Result::iterateColumn() instead - * - * @return Traversable - * - * @throws Exception - */ - public function iterateColumn(): Traversable - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4554', - 'Statement::%s() is deprecated, use Result::%s() instead.', - __FUNCTION__, - __FUNCTION__ - ); - - try { - if ($this->stmt instanceof Result) { - while (($value = $this->stmt->fetchOne()) !== false) { - yield $value; - } - } else { - while (($value = $this->stmt->fetch(FetchMode::COLUMN)) !== false) { - yield $value; - } - } - } catch (Exception $e) { - $this->conn->handleDriverException($e); - } - } - - /** - * Returns the number of rows affected by the last execution of this statement. - * - * @return int The number of affected rows. - */ - public function rowCount() - { - return $this->stmt->rowCount(); - } - - public function free(): void - { - if ($this->stmt instanceof Result) { - $this->stmt->free(); - - return; - } - - $this->stmt->closeCursor(); - } - - /** - * Gets the wrapped driver statement. - * - * @return \Doctrine\DBAL\Driver\Statement - */ - public function getWrappedStatement() - { - return $this->stmt; - } - - private function ensureHasKeyValue(): void - { - $columnCount = $this->columnCount(); - - if ($columnCount < 2) { - throw NoKeyValue::fromColumnCount($columnCount); - } - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php deleted file mode 100644 index 26456bde0..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php +++ /dev/null @@ -1,143 +0,0 @@ -setName('dbal:import') - ->setDescription('Import SQL file(s) directly to Database.') - ->setDefinition([new InputArgument( - 'file', - InputArgument::REQUIRED | InputArgument::IS_ARRAY, - 'File path(s) of SQL to be executed.' - ), - ]) - ->setHelp(<<getHelper('db')->getConnection(); - - $fileNames = $input->getArgument('file'); - - if ($fileNames === null) { - return 0; - } - - foreach ((array) $fileNames as $fileName) { - $filePath = realpath($fileName); - - // Phar compatibility. - if ($filePath === false) { - $filePath = $fileName; - } - - if (! file_exists($filePath)) { - throw new InvalidArgumentException( - sprintf("SQL file '%s' does not exist.", $filePath) - ); - } - - if (! is_readable($filePath)) { - throw new InvalidArgumentException( - sprintf("SQL file '%s' does not have read permissions.", $filePath) - ); - } - - $output->write(sprintf("Processing file '%s'... ", $filePath)); - $sql = @file_get_contents($filePath); - - if ($sql === false) { - $message = sprintf("Unable to read SQL file '%s'", $filePath); - $error = error_get_last(); - - if ($error !== null) { - $message .= ': ' . $error['message']; - } - - throw new RuntimeException($message); - } - - if ($conn instanceof PDOConnection) { - // PDO Drivers - try { - $lines = 0; - - $stmt = $conn->prepare($sql); - assert($stmt instanceof PDOStatement); - - $stmt->execute(); - - do { - // Required due to "MySQL has gone away!" issue - $stmt->fetch(); - $stmt->closeCursor(); - - $lines++; - } while ($stmt->nextRowset()); - - $output->write(sprintf('%d statements executed!', $lines) . PHP_EOL); - } catch (PDOException $e) { - $output->write('error!' . PHP_EOL); - - throw new RuntimeException($e->getMessage(), $e->getCode(), $e); - } - } else { - // Non-PDO Drivers (ie. OCI8 driver) - $stmt = $conn->prepare($sql); - $rs = $stmt->execute(); - - if (! $rs) { - $error = $stmt->errorInfo(); - - $output->write('error!' . PHP_EOL); - - throw new RuntimeException($error[2], $error[0]); - } - - $output->writeln('OK!' . PHP_EOL); - - $stmt->closeCursor(); - } - } - - return 0; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php deleted file mode 100644 index 4553a0a70..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php +++ /dev/null @@ -1,242 +0,0 @@ -> */ - private $keywordListClasses = [ - 'mysql' => MySQLKeywords::class, - 'mysql57' => MySQL57Keywords::class, - 'mysql80' => MySQL80Keywords::class, - 'sqlserver' => SQLServerKeywords::class, - 'sqlserver2005' => SQLServer2005Keywords::class, - 'sqlserver2008' => SQLServer2008Keywords::class, - 'sqlserver2012' => SQLServer2012Keywords::class, - 'sqlite' => SQLiteKeywords::class, - 'pgsql' => PostgreSQLKeywords::class, - 'pgsql91' => PostgreSQL91Keywords::class, - 'pgsql92' => PostgreSQL92Keywords::class, - 'oracle' => OracleKeywords::class, - 'db2' => DB2Keywords::class, - 'sqlanywhere' => SQLAnywhereKeywords::class, - 'sqlanywhere11' => SQLAnywhere11Keywords::class, - 'sqlanywhere12' => SQLAnywhere12Keywords::class, - 'sqlanywhere16' => SQLAnywhere16Keywords::class, - ]; - - /** @var ConnectionProvider|null */ - private $connectionProvider; - - public function __construct(?ConnectionProvider $connectionProvider = null) - { - parent::__construct(); - $this->connectionProvider = $connectionProvider; - if ($connectionProvider !== null) { - return; - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3956', - 'Not passing a connection provider as the first constructor argument is deprecated' - ); - } - - /** - * If you want to add or replace a keywords list use this command. - * - * @param string $name - * @param class-string $class - * - * @return void - */ - public function setKeywordListClass($name, $class) - { - $this->keywordListClasses[$name] = $class; - } - - /** @return void */ - protected function configure() - { - $this - ->setName('dbal:reserved-words') - ->setDescription('Checks if the current database contains identifiers that are reserved.') - ->setDefinition([ - new InputOption('connection', null, InputOption::VALUE_REQUIRED, 'The named database connection'), - new InputOption( - 'list', - 'l', - InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, - 'Keyword-List name.' - ), - ]) - ->setHelp(<<%command.full_name% - -If you want to check against specific dialects you can -pass them to the command: - - %command.full_name% -l mysql -l pgsql - -The following keyword lists are currently shipped with Doctrine: - - * mysql - * mysql57 - * mysql80 - * pgsql - * pgsql92 - * sqlite - * oracle - * sqlserver - * sqlserver2005 - * sqlserver2008 - * sqlserver2012 - * sqlanywhere - * sqlanywhere11 - * sqlanywhere12 - * sqlanywhere16 - * db2 (Not checked by default) -EOT - ); - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $conn = $this->getConnection($input); - - $keywordLists = $input->getOption('list'); - - if (is_string($keywordLists)) { - $keywordLists = [$keywordLists]; - } elseif (! is_array($keywordLists)) { - $keywordLists = []; - } - - if (! $keywordLists) { - $keywordLists = [ - 'mysql', - 'mysql57', - 'mysql80', - 'pgsql', - 'pgsql92', - 'sqlite', - 'oracle', - 'sqlserver', - 'sqlserver2005', - 'sqlserver2008', - 'sqlserver2012', - 'sqlanywhere', - 'sqlanywhere11', - 'sqlanywhere12', - 'sqlanywhere16', - ]; - } - - $keywords = []; - foreach ($keywordLists as $keywordList) { - if (! isset($this->keywordListClasses[$keywordList])) { - throw new InvalidArgumentException( - "There exists no keyword list with name '" . $keywordList . "'. " . - 'Known lists: ' . implode(', ', array_keys($this->keywordListClasses)) - ); - } - - $class = $this->keywordListClasses[$keywordList]; - $keywords[] = new $class(); - } - - $output->write( - 'Checking keyword violations for ' . implode(', ', $keywordLists) . '...', - true - ); - - $schema = $conn->getSchemaManager()->createSchema(); - $visitor = new ReservedKeywordsValidator($keywords); - $schema->visit($visitor); - - $violations = $visitor->getViolations(); - if (count($violations) !== 0) { - $output->write( - 'There are ' . count($violations) . ' reserved keyword violations' - . ' in your database schema:', - true - ); - - foreach ($violations as $violation) { - $output->write(' - ' . $violation, true); - } - - return 1; - } - - $output->write('No reserved keywords violations have been found!', true); - - return 0; - } - - private function getConnection(InputInterface $input): Connection - { - $connectionName = $input->getOption('connection'); - assert(is_string($connectionName) || $connectionName === null); - - if ($this->connectionProvider === null) { - if ($connectionName !== null) { - throw new Exception('Specifying a connection is only supported when a ConnectionProvider is used.'); - } - - return $this->getHelper('db')->getConnection(); - } - - if ($connectionName !== null) { - return $this->connectionProvider->getConnection($connectionName); - } - - return $this->connectionProvider->getDefaultConnection(); - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php deleted file mode 100644 index 7fc14c340..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php +++ /dev/null @@ -1,118 +0,0 @@ - new ConnectionHelper($connection), - ]); - } - - /** - * Runs console with the given connection provider or helperset (deprecated). - * - * @param ConnectionProvider|HelperSet $helperSetOrConnectionProvider - * @param Command[] $commands - * - * @return void - */ - public static function run($helperSetOrConnectionProvider, $commands = []) - { - $cli = new Application('Doctrine Command Line Interface', Version::VERSION); - - $cli->setCatchExceptions(true); - - $connectionProvider = null; - if ($helperSetOrConnectionProvider instanceof HelperSet) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3956', - 'Passing an instance of "%s" as the first argument is deprecated. Pass an instance of "%s" instead.', - HelperSet::class, - ConnectionProvider::class - ); - $connectionProvider = null; - $cli->setHelperSet($helperSetOrConnectionProvider); - } elseif ($helperSetOrConnectionProvider instanceof ConnectionProvider) { - $connectionProvider = $helperSetOrConnectionProvider; - } else { - throw new TypeError(sprintf( - 'First argument must be an instance of "%s" or "%s"', - HelperSet::class, - ConnectionProvider::class - )); - } - - self::addCommands($cli, $connectionProvider); - - $cli->addCommands($commands); - $cli->run(); - } - - /** - * @return void - */ - public static function addCommands(Application $cli, ?ConnectionProvider $connectionProvider = null) - { - $cli->addCommands([ - new RunSqlCommand($connectionProvider), - new ImportCommand(), - new ReservedWordsCommand($connectionProvider), - ]); - } - - /** - * Prints the instructions to create a configuration file - * - * @return void - */ - public static function printCliConfigTemplate() - { - echo <<<'HELP' -You are missing a "cli-config.php" or "config/cli-config.php" file in your -project, which is required to get the Doctrine-DBAL Console working. You can use the -following sample as a template: - -_connection = $connection; - } - - /** - * Retrieves the Doctrine database Connection. - * - * @return Connection - */ - public function getConnection() - { - return $this->_connection; - } - - /** - * {@inheritdoc} - */ - public function getName() - { - return 'connection'; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php deleted file mode 100644 index be76b7c8a..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php +++ /dev/null @@ -1,35 +0,0 @@ -getDecimalTypeDeclarationSQL($column); - } - - /** - * {@inheritdoc} - */ - public function convertToPHPValue($value, AbstractPlatform $platform) - { - return $value; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php deleted file mode 100644 index 400e0ef38..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php +++ /dev/null @@ -1,47 +0,0 @@ -getVarcharTypeDeclarationSQL($column); - } - - /** - * {@inheritdoc} - */ - public function getDefaultLength(AbstractPlatform $platform) - { - return $platform->getVarcharDefaultLength(); - } - - /** - * {@inheritdoc} - */ - public function getName() - { - return Types::STRING; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php deleted file mode 100644 index 64e59fec0..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php +++ /dev/null @@ -1,396 +0,0 @@ - ArrayType::class, - Types::ASCII_STRING => AsciiStringType::class, - Types::BIGINT => BigIntType::class, - Types::BINARY => BinaryType::class, - Types::BLOB => BlobType::class, - Types::BOOLEAN => BooleanType::class, - Types::DATE_MUTABLE => DateType::class, - Types::DATE_IMMUTABLE => DateImmutableType::class, - Types::DATEINTERVAL => DateIntervalType::class, - Types::DATETIME_MUTABLE => DateTimeType::class, - Types::DATETIME_IMMUTABLE => DateTimeImmutableType::class, - Types::DATETIMETZ_MUTABLE => DateTimeTzType::class, - Types::DATETIMETZ_IMMUTABLE => DateTimeTzImmutableType::class, - Types::DECIMAL => DecimalType::class, - Types::FLOAT => FloatType::class, - Types::GUID => GuidType::class, - Types::INTEGER => IntegerType::class, - Types::JSON => JsonType::class, - Types::JSON_ARRAY => JsonArrayType::class, - Types::OBJECT => ObjectType::class, - Types::SIMPLE_ARRAY => SimpleArrayType::class, - Types::SMALLINT => SmallIntType::class, - Types::STRING => StringType::class, - Types::TEXT => TextType::class, - Types::TIME_MUTABLE => TimeType::class, - Types::TIME_IMMUTABLE => TimeImmutableType::class, - ]; - - /** @var TypeRegistry|null */ - private static $typeRegistry; - - /** - * @internal Do not instantiate directly - use {@see Type::addType()} method instead. - */ - final public function __construct() - { - } - - /** - * Converts a value from its PHP representation to its database representation - * of this type. - * - * @param mixed $value The value to convert. - * @param AbstractPlatform $platform The currently used database platform. - * - * @return mixed The database representation of the value. - */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) - { - return $value; - } - - /** - * Converts a value from its database representation to its PHP representation - * of this type. - * - * @param mixed $value The value to convert. - * @param AbstractPlatform $platform The currently used database platform. - * - * @return mixed The PHP representation of the value. - */ - public function convertToPHPValue($value, AbstractPlatform $platform) - { - return $value; - } - - /** - * Gets the default length of this type. - * - * @deprecated Rely on information provided by the platform instead. - * - * @return int|null - */ - public function getDefaultLength(AbstractPlatform $platform) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3255', - 'Type::getDefaultLength() is deprecated, use AbstractPlatform directly.' - ); - - return null; - } - - /** - * Gets the SQL declaration snippet for a column of this type. - * - * @param mixed[] $column The column definition - * @param AbstractPlatform $platform The currently used database platform. - * - * @return string - */ - abstract public function getSQLDeclaration(array $column, AbstractPlatform $platform); - - /** - * Gets the name of this type. - * - * @return string - * - * @todo Needed? - */ - abstract public function getName(); - - final public static function getTypeRegistry(): TypeRegistry - { - if (self::$typeRegistry === null) { - self::$typeRegistry = self::createTypeRegistry(); - } - - return self::$typeRegistry; - } - - private static function createTypeRegistry(): TypeRegistry - { - $instances = []; - - foreach (self::BUILTIN_TYPES_MAP as $name => $class) { - $instances[$name] = new $class(); - } - - return new TypeRegistry($instances); - } - - /** - * Factory method to create type instances. - * Type instances are implemented as flyweights. - * - * @param string $name The name of the type (as returned by getName()). - * - * @return Type - * - * @throws Exception - */ - public static function getType($name) - { - return self::getTypeRegistry()->get($name); - } - - /** - * Adds a custom type to the type map. - * - * @param string $name The name of the type. This should correspond to what getName() returns. - * @param class-string $className The class name of the custom type. - * - * @return void - * - * @throws Exception - */ - public static function addType($name, $className) - { - self::getTypeRegistry()->register($name, new $className()); - } - - /** - * Checks if exists support for a type. - * - * @param string $name The name of the type. - * - * @return bool TRUE if type is supported; FALSE otherwise. - */ - public static function hasType($name) - { - return self::getTypeRegistry()->has($name); - } - - /** - * Overrides an already defined type to use a different implementation. - * - * @param string $name - * @param class-string $className - * - * @return void - * - * @throws Exception - */ - public static function overrideType($name, $className) - { - self::getTypeRegistry()->override($name, new $className()); - } - - /** - * Gets the (preferred) binding type for values of this type that - * can be used when binding parameters to prepared statements. - * - * This method should return one of the {@link ParameterType} constants. - * - * @return int - */ - public function getBindingType() - { - return ParameterType::STRING; - } - - /** - * Gets the types array map which holds all registered types and the corresponding - * type class - * - * @return string[] - */ - public static function getTypesMap() - { - return array_map( - static function (Type $type): string { - return get_class($type); - }, - self::getTypeRegistry()->getMap() - ); - } - - /** - * @deprecated Relying on string representation is discouraged and will be removed in DBAL 3.0. - * - * @return string - */ - public function __toString() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3258', - 'Type::__toString() is deprecated, use Type::getName() or get_class($type) instead.' - ); - - $type = static::class; - $position = strrpos($type, '\\'); - - if ($position !== false) { - $type = substr($type, $position); - } - - return str_replace('Type', '', $type); - } - - /** - * Does working with this column require SQL conversion functions? - * - * This is a metadata function that is required for example in the ORM. - * Usage of {@link convertToDatabaseValueSQL} and - * {@link convertToPHPValueSQL} works for any type and mostly - * does nothing. This method can additionally be used for optimization purposes. - * - * @return bool - */ - public function canRequireSQLConversion() - { - return false; - } - - /** - * Modifies the SQL expression (identifier, parameter) to convert to a database value. - * - * @param string $sqlExpr - * - * @return string - */ - public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) - { - return $sqlExpr; - } - - /** - * Modifies the SQL expression (identifier, parameter) to convert to a PHP value. - * - * @param string $sqlExpr - * @param AbstractPlatform $platform - * - * @return string - */ - public function convertToPHPValueSQL($sqlExpr, $platform) - { - return $sqlExpr; - } - - /** - * Gets an array of database types that map to this Doctrine type. - * - * @return string[] - */ - public function getMappedDatabaseTypes(AbstractPlatform $platform) - { - return []; - } - - /** - * If this Doctrine Type maps to an already mapped database type, - * reverse schema engineering can't tell them apart. You need to mark - * one of those types as commented, which will have Doctrine use an SQL - * comment to typehint the actual Doctrine Type. - * - * @return bool - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - return false; - } -} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php b/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php deleted file mode 100644 index c0df834f8..000000000 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php +++ /dev/null @@ -1,35 +0,0 @@ -data = $data; + if (count($data) === 0) { + return; + } + + $this->columnCount = count($data[0]); + } + + /** + * {@inheritdoc} + */ + public function fetchNumeric() + { + $row = $this->fetch(); + + if ($row === false) { + return false; + } + + return array_values($row); + } + + /** + * {@inheritdoc} + */ + public function fetchAssociative() + { + return $this->fetch(); + } + + /** + * {@inheritdoc} + */ + public function fetchOne() + { + $row = $this->fetch(); + + if ($row === false) { + return false; + } + + return reset($row); + } + + /** + * {@inheritdoc} + */ + public function fetchAllNumeric(): array + { + return FetchUtils::fetchAllNumeric($this); + } + + /** + * {@inheritdoc} + */ + public function fetchAllAssociative(): array + { + return FetchUtils::fetchAllAssociative($this); + } + + /** + * {@inheritdoc} + */ + public function fetchFirstColumn(): array + { + return FetchUtils::fetchFirstColumn($this); + } + + public function rowCount(): int + { + return count($this->data); + } + + public function columnCount(): int + { + return $this->columnCount; + } + + public function free(): void + { + $this->data = []; + } + + /** + * @return mixed|false + */ + private function fetch() + { + if (! isset($this->data[$this->num])) { + return false; + } + + return $this->data[$this->num++]; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/CacheException.php b/app/vendor/doctrine/dbal/src/Cache/CacheException.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/CacheException.php rename to app/vendor/doctrine/dbal/src/Cache/CacheException.php diff --git a/app/vendor/doctrine/dbal/src/Cache/CachingResult.php b/app/vendor/doctrine/dbal/src/Cache/CachingResult.php new file mode 100644 index 000000000..c060673c5 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Cache/CachingResult.php @@ -0,0 +1,184 @@ +>|null */ + private $data; + + /** + * @param string $cacheKey + * @param string $realKey + * @param int $lifetime + */ + public function __construct(Result $result, Cache $cache, $cacheKey, $realKey, $lifetime) + { + $this->result = $result; + $this->cache = $cache; + $this->cacheKey = $cacheKey; + $this->realKey = $realKey; + $this->lifetime = $lifetime; + } + + /** + * {@inheritdoc} + */ + public function fetchNumeric() + { + $row = $this->fetch(); + + if ($row === false) { + return false; + } + + return array_values($row); + } + + /** + * {@inheritdoc} + */ + public function fetchAssociative() + { + return $this->fetch(); + } + + /** + * {@inheritdoc} + */ + public function fetchOne() + { + return FetchUtils::fetchOne($this); + } + + /** + * {@inheritdoc} + */ + public function fetchAllNumeric(): array + { + return array_map('array_values', $this->fetchAllAssociative()); + } + + /** + * {@inheritdoc} + */ + public function fetchAllAssociative(): array + { + $data = $this->result->fetchAllAssociative(); + + $this->store($data); + + return $data; + } + + /** + * {@inheritdoc} + */ + public function fetchFirstColumn(): array + { + return FetchUtils::fetchFirstColumn($this); + } + + public function rowCount(): int + { + return $this->result->rowCount(); + } + + public function columnCount(): int + { + return $this->result->columnCount(); + } + + public function free(): void + { + $this->data = null; + } + + /** + * @return array|false + * + * @throws Exception + */ + private function fetch() + { + if ($this->data === null) { + $this->data = []; + } + + $row = $this->result->fetchAssociative(); + + if ($row !== false) { + $this->data[] = $row; + + return $row; + } + + $this->saveToCache(); + + return false; + } + + /** + * @param array> $data + */ + private function store(array $data): void + { + $this->data = $data; + + $this->saveToCache(); + } + + private function saveToCache(): void + { + if ($this->data === null) { + return; + } + + $data = $this->cache->fetch($this->cacheKey); + + if ($data === false) { + $data = []; + } + + $data[$this->realKey] = $this->data; + + $this->cache->save($this->cacheKey, $data, $this->lifetime); + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php b/app/vendor/doctrine/dbal/src/Cache/QueryCacheProfile.php similarity index 97% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php rename to app/vendor/doctrine/dbal/src/Cache/QueryCacheProfile.php index 3a57358fa..44f5868cf 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php +++ b/app/vendor/doctrine/dbal/src/Cache/QueryCacheProfile.php @@ -20,7 +20,7 @@ class QueryCacheProfile private $resultCacheDriver; /** @var int */ - private $lifetime = 0; + private $lifetime; /** @var string|null */ private $cacheKey; @@ -70,7 +70,7 @@ public function getCacheKey() * Generates the real cache key from query, params, types and connection parameters. * * @param string $sql - * @param array|array $params + * @param list|array $params * @param array|array $types * @param array $connectionParams * diff --git a/app/vendor/doctrine/dbal/src/ColumnCase.php b/app/vendor/doctrine/dbal/src/ColumnCase.php new file mode 100644 index 000000000..cb0dd409b --- /dev/null +++ b/app/vendor/doctrine/dbal/src/ColumnCase.php @@ -0,0 +1,28 @@ +sqlLogger = $logger; + } + + /** + * Gets the SQL logger that is used. + */ + public function getSQLLogger(): ?SQLLogger + { + return $this->sqlLogger; + } + + /** + * Gets the cache driver implementation that is used for query result caching. + */ + public function getResultCacheImpl(): ?Cache + { + return $this->resultCacheImpl; + } + + /** + * Sets the cache driver implementation that is used for query result caching. + */ + public function setResultCacheImpl(Cache $cacheImpl): void + { + $this->resultCacheImpl = $cacheImpl; + } + + /** + * Sets the callable to use to filter schema assets. + */ + public function setSchemaAssetsFilter(?callable $callable = null): void + { + $this->schemaAssetsFilter = $callable; + } + + /** + * Returns the callable to use to filter schema assets. + */ + public function getSchemaAssetsFilter(): ?callable + { + return $this->schemaAssetsFilter; + } + + /** + * Sets the default auto-commit mode for connections. + * + * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual + * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either + * the method commit or the method rollback. By default, new connections are in auto-commit mode. + * + * @see getAutoCommit + * + * @param bool $autoCommit True to enable auto-commit mode; false to disable it + */ + public function setAutoCommit(bool $autoCommit): void + { + $this->autoCommit = $autoCommit; + } + + /** + * Returns the default auto-commit mode for connections. + * + * @see setAutoCommit + * + * @return bool True if auto-commit mode is enabled by default for connections, false otherwise. + */ + public function getAutoCommit(): bool + { + return $this->autoCommit; + } + + /** + * @param Middleware[] $middlewares + * + * @return $this + */ + public function setMiddlewares(array $middlewares): self + { + $this->middlewares = $middlewares; + + return $this; + } + + /** + * @return Middleware[] + */ + public function getMiddlewares(): array + { + return $this->middlewares; + } +} diff --git a/app/vendor/doctrine/dbal/src/Connection.php b/app/vendor/doctrine/dbal/src/Connection.php new file mode 100644 index 000000000..466484234 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Connection.php @@ -0,0 +1,1821 @@ + + * @phpstan-var array + * @psalm-var Params + */ + private $params; + + /** + * The database platform object used by the connection or NULL before it's initialized. + * + * @var AbstractPlatform|null + */ + private $platform; + + /** @var ExceptionConverter|null */ + private $exceptionConverter; + + /** @var Parser|null */ + private $parser; + + /** + * The schema manager. + * + * @deprecated Use {@link createSchemaManager()} instead. + * + * @var AbstractSchemaManager|null + */ + protected $_schemaManager; + + /** + * The used DBAL driver. + * + * @var Driver + */ + protected $_driver; + + /** + * Flag that indicates whether the current transaction is marked for rollback only. + * + * @var bool + */ + private $isRollbackOnly = false; + + /** + * Initializes a new instance of the Connection class. + * + * @internal The connection can be only instantiated by the driver manager. + * + * @param array $params The connection parameters. + * @param Driver $driver The driver to use. + * @param Configuration|null $config The configuration, optional. + * @param EventManager|null $eventManager The event manager, optional. + * @psalm-param Params $params + * @phpstan-param array $params + * + * @throws Exception + */ + public function __construct( + array $params, + Driver $driver, + ?Configuration $config = null, + ?EventManager $eventManager = null + ) { + $this->_driver = $driver; + $this->params = $params; + + if (isset($params['platform'])) { + if (! $params['platform'] instanceof Platforms\AbstractPlatform) { + throw Exception::invalidPlatformType($params['platform']); + } + + $this->platform = $params['platform']; + } + + // Create default config and event manager if none given + if ($config === null) { + $config = new Configuration(); + } + + if ($eventManager === null) { + $eventManager = new EventManager(); + } + + $this->_config = $config; + $this->_eventManager = $eventManager; + + $this->_expr = $this->createExpressionBuilder(); + + $this->autoCommit = $config->getAutoCommit(); + } + + /** + * Gets the parameters used during instantiation. + * + * @internal + * + * @return array + * @psalm-return Params + * @phpstan-return array + */ + public function getParams() + { + return $this->params; + } + + /** + * Gets the name of the currently selected database. + * + * @return string|null The name of the database or NULL if a database is not selected. + * The platforms which don't support the concept of a database (e.g. embedded databases) + * must always return a string as an indicator of an implicitly selected database. + * + * @throws Exception + */ + public function getDatabase() + { + $platform = $this->getDatabasePlatform(); + $query = $platform->getDummySelectSQL($platform->getCurrentDatabaseExpression()); + $database = $this->fetchOne($query); + + assert(is_string($database) || $database === null); + + return $database; + } + + /** + * Gets the DBAL driver instance. + * + * @return Driver + */ + public function getDriver() + { + return $this->_driver; + } + + /** + * Gets the Configuration used by the Connection. + * + * @return Configuration + */ + public function getConfiguration() + { + return $this->_config; + } + + /** + * Gets the EventManager used by the Connection. + * + * @return EventManager + */ + public function getEventManager() + { + return $this->_eventManager; + } + + /** + * Gets the DatabasePlatform for the connection. + * + * @return AbstractPlatform + * + * @throws Exception + */ + public function getDatabasePlatform() + { + if ($this->platform === null) { + $this->platform = $this->detectDatabasePlatform(); + $this->platform->setEventManager($this->_eventManager); + } + + return $this->platform; + } + + /** + * Creates an expression builder for the connection. + */ + public function createExpressionBuilder(): ExpressionBuilder + { + return new ExpressionBuilder($this); + } + + /** + * Gets the ExpressionBuilder for the connection. + * + * @deprecated Use {@link createExpressionBuilder()} instead. + * + * @return ExpressionBuilder + */ + public function getExpressionBuilder() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4515', + 'Connection::getExpressionBuilder() is deprecated,' + . ' use Connection::createExpressionBuilder() instead.' + ); + + return $this->_expr; + } + + /** + * Establishes the connection with the database. + * + * @return bool TRUE if the connection was successfully established, FALSE if + * the connection is already open. + * + * @throws Exception + */ + public function connect() + { + if ($this->_conn !== null) { + return false; + } + + try { + $this->_conn = $this->_driver->connect($this->params); + } catch (Driver\Exception $e) { + throw $this->convertException($e); + } + + if ($this->autoCommit === false) { + $this->beginTransaction(); + } + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new Event\ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * Detects and sets the database platform. + * + * Evaluates custom platform class and version in order to set the correct platform. + * + * @throws Exception If an invalid platform was specified for this connection. + */ + private function detectDatabasePlatform(): AbstractPlatform + { + $version = $this->getDatabasePlatformVersion(); + + if ($version !== null) { + assert($this->_driver instanceof VersionAwarePlatformDriver); + + return $this->_driver->createDatabasePlatformForVersion($version); + } + + return $this->_driver->getDatabasePlatform(); + } + + /** + * Returns the version of the related platform if applicable. + * + * Returns null if either the driver is not capable to create version + * specific platform instances, no explicit server version was specified + * or the underlying driver connection cannot determine the platform + * version without having to query it (performance reasons). + * + * @return string|null + * + * @throws Throwable + */ + private function getDatabasePlatformVersion() + { + // Driver does not support version specific platforms. + if (! $this->_driver instanceof VersionAwarePlatformDriver) { + return null; + } + + // Explicit platform version requested (supersedes auto-detection). + if (isset($this->params['serverVersion'])) { + return $this->params['serverVersion']; + } + + // If not connected, we need to connect now to determine the platform version. + if ($this->_conn === null) { + try { + $this->connect(); + } catch (Exception $originalException) { + if (! isset($this->params['dbname'])) { + throw $originalException; + } + + // The database to connect to might not yet exist. + // Retry detection without database name connection parameter. + $params = $this->params; + + unset($this->params['dbname']); + + try { + $this->connect(); + } catch (Exception $fallbackException) { + // Either the platform does not support database-less connections + // or something else went wrong. + throw $originalException; + } finally { + $this->params = $params; + } + + $serverVersion = $this->getServerVersion(); + + // Close "temporary" connection to allow connecting to the real database again. + $this->close(); + + return $serverVersion; + } + } + + return $this->getServerVersion(); + } + + /** + * Returns the database server version if the underlying driver supports it. + * + * @return string|null + * + * @throws Exception + */ + private function getServerVersion() + { + $connection = $this->getWrappedConnection(); + + // Automatic platform version detection. + if ($connection instanceof ServerInfoAwareConnection) { + try { + return $connection->getServerVersion(); + } catch (Driver\Exception $e) { + throw $this->convertException($e); + } + } + + // Unable to detect platform version. + return null; + } + + /** + * Returns the current auto-commit mode for this connection. + * + * @see setAutoCommit + * + * @return bool True if auto-commit mode is currently enabled for this connection, false otherwise. + */ + public function isAutoCommit() + { + return $this->autoCommit === true; + } + + /** + * Sets auto-commit mode for this connection. + * + * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual + * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either + * the method commit or the method rollback. By default, new connections are in auto-commit mode. + * + * NOTE: If this method is called during a transaction and the auto-commit mode is changed, the transaction is + * committed. If this method is called and the auto-commit mode is not changed, the call is a no-op. + * + * @see isAutoCommit + * + * @param bool $autoCommit True to enable auto-commit mode; false to disable it. + * + * @return void + */ + public function setAutoCommit($autoCommit) + { + $autoCommit = (bool) $autoCommit; + + // Mode not changed, no-op. + if ($autoCommit === $this->autoCommit) { + return; + } + + $this->autoCommit = $autoCommit; + + // Commit all currently active transactions if any when switching auto-commit mode. + if ($this->_conn === null || $this->transactionNestingLevel === 0) { + return; + } + + $this->commitAll(); + } + + /** + * Prepares and executes an SQL query and returns the first row of the result + * as an associative array. + * + * @param string $query SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @return array|false False is returned if no rows are found. + * + * @throws Exception + */ + public function fetchAssociative(string $query, array $params = [], array $types = []) + { + try { + return $this->executeQuery($query, $params, $types)->fetchAssociative(); + } catch (Driver\Exception $e) { + throw $this->convertExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares and executes an SQL query and returns the first row of the result + * as a numerically indexed array. + * + * @param string $query SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @return list< mixed>|false False is returned if no rows are found. + * + * @throws Exception + */ + public function fetchNumeric(string $query, array $params = [], array $types = []) + { + try { + return $this->executeQuery($query, $params, $types)->fetchNumeric(); + } catch (Driver\Exception $e) { + throw $this->convertExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares and executes an SQL query and returns the value of a single column + * of the first row of the result. + * + * @param string $query SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @return mixed|false False is returned if no rows are found. + * + * @throws Exception + */ + public function fetchOne(string $query, array $params = [], array $types = []) + { + try { + return $this->executeQuery($query, $params, $types)->fetchOne(); + } catch (Driver\Exception $e) { + throw $this->convertExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Whether an actual connection to the database is established. + * + * @return bool + */ + public function isConnected() + { + return $this->_conn !== null; + } + + /** + * Checks whether a transaction is currently active. + * + * @return bool TRUE if a transaction is currently active, FALSE otherwise. + */ + public function isTransactionActive() + { + return $this->transactionNestingLevel > 0; + } + + /** + * Adds condition based on the criteria to the query components + * + * @param array $criteria Map of key columns to their values + * @param string[] $columns Column names + * @param mixed[] $values Column values + * @param string[] $conditions Key conditions + * + * @throws Exception + */ + private function addCriteriaCondition( + array $criteria, + array &$columns, + array &$values, + array &$conditions + ): void { + $platform = $this->getDatabasePlatform(); + + foreach ($criteria as $columnName => $value) { + if ($value === null) { + $conditions[] = $platform->getIsNullExpression($columnName); + continue; + } + + $columns[] = $columnName; + $values[] = $value; + $conditions[] = $columnName . ' = ?'; + } + } + + /** + * Executes an SQL DELETE statement on a table. + * + * Table expression and columns are not escaped and are not safe for user-input. + * + * @param string $table Table name + * @param array $criteria Deletion criteria + * @param array|array $types Parameter types + * + * @return int The number of affected rows. + * + * @throws Exception + */ + public function delete($table, array $criteria, array $types = []) + { + if (count($criteria) === 0) { + throw InvalidArgumentException::fromEmptyCriteria(); + } + + $columns = $values = $conditions = []; + + $this->addCriteriaCondition($criteria, $columns, $values, $conditions); + + return $this->executeStatement( + 'DELETE FROM ' . $table . ' WHERE ' . implode(' AND ', $conditions), + $values, + is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types + ); + } + + /** + * Closes the connection. + * + * @return void + */ + public function close() + { + $this->_conn = null; + $this->transactionNestingLevel = 0; + } + + /** + * Sets the transaction isolation level. + * + * @param int $level The level to set. + * + * @return int + * + * @throws Exception + */ + public function setTransactionIsolation($level) + { + $this->transactionIsolationLevel = $level; + + return $this->executeStatement($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level)); + } + + /** + * Gets the currently active transaction isolation level. + * + * @return int The current transaction isolation level. + * + * @throws Exception + */ + public function getTransactionIsolation() + { + if ($this->transactionIsolationLevel === null) { + $this->transactionIsolationLevel = $this->getDatabasePlatform()->getDefaultTransactionIsolationLevel(); + } + + return $this->transactionIsolationLevel; + } + + /** + * Executes an SQL UPDATE statement on a table. + * + * Table expression and columns are not escaped and are not safe for user-input. + * + * @param string $table Table name + * @param array $data Column-value pairs + * @param array $criteria Update criteria + * @param array|array $types Parameter types + * + * @return int The number of affected rows. + * + * @throws Exception + */ + public function update($table, array $data, array $criteria, array $types = []) + { + $columns = $values = $conditions = $set = []; + + foreach ($data as $columnName => $value) { + $columns[] = $columnName; + $values[] = $value; + $set[] = $columnName . ' = ?'; + } + + $this->addCriteriaCondition($criteria, $columns, $values, $conditions); + + if (is_string(key($types))) { + $types = $this->extractTypeValues($columns, $types); + } + + $sql = 'UPDATE ' . $table . ' SET ' . implode(', ', $set) + . ' WHERE ' . implode(' AND ', $conditions); + + return $this->executeStatement($sql, $values, $types); + } + + /** + * Inserts a table row with specified data. + * + * Table expression and columns are not escaped and are not safe for user-input. + * + * @param string $table Table name + * @param array $data Column-value pairs + * @param array|array $types Parameter types + * + * @return int The number of affected rows. + * + * @throws Exception + */ + public function insert($table, array $data, array $types = []) + { + if (count($data) === 0) { + return $this->executeStatement('INSERT INTO ' . $table . ' () VALUES ()'); + } + + $columns = []; + $values = []; + $set = []; + + foreach ($data as $columnName => $value) { + $columns[] = $columnName; + $values[] = $value; + $set[] = '?'; + } + + return $this->executeStatement( + 'INSERT INTO ' . $table . ' (' . implode(', ', $columns) . ')' . + ' VALUES (' . implode(', ', $set) . ')', + $values, + is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types + ); + } + + /** + * Extract ordered type list from an ordered column list and type map. + * + * @param array $columnList + * @param array|array $types + * + * @return array|array + */ + private function extractTypeValues(array $columnList, array $types) + { + $typeValues = []; + + foreach ($columnList as $columnName) { + $typeValues[] = $types[$columnName] ?? ParameterType::STRING; + } + + return $typeValues; + } + + /** + * Quotes a string so it can be safely used as a table or column name, even if + * it is a reserved name. + * + * Delimiting style depends on the underlying database platform that is being used. + * + * NOTE: Just because you CAN use quoted identifiers does not mean + * you SHOULD use them. In general, they end up causing way more + * problems than they solve. + * + * @param string $str The name to be quoted. + * + * @return string The quoted name. + */ + public function quoteIdentifier($str) + { + return $this->getDatabasePlatform()->quoteIdentifier($str); + } + + /** + * @param mixed $value + * @param int|string|Type|null $type + * + * @return mixed + */ + public function quote($value, $type = ParameterType::STRING) + { + $connection = $this->getWrappedConnection(); + + [$value, $bindingType] = $this->getBindingInfo($value, $type); + + return $connection->quote($value, $bindingType); + } + + /** + * Prepares and executes an SQL query and returns the result as an array of numeric arrays. + * + * @param string $query SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @return list> + * + * @throws Exception + */ + public function fetchAllNumeric(string $query, array $params = [], array $types = []): array + { + try { + return $this->executeQuery($query, $params, $types)->fetchAllNumeric(); + } catch (Driver\Exception $e) { + throw $this->convertExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares and executes an SQL query and returns the result as an array of associative arrays. + * + * @param string $query SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @return list> + * + * @throws Exception + */ + public function fetchAllAssociative(string $query, array $params = [], array $types = []): array + { + try { + return $this->executeQuery($query, $params, $types)->fetchAllAssociative(); + } catch (Driver\Exception $e) { + throw $this->convertExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares and executes an SQL query and returns the result as an associative array with the keys + * mapped to the first column and the values mapped to the second column. + * + * @param string $query SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @return array + * + * @throws Exception + */ + public function fetchAllKeyValue(string $query, array $params = [], array $types = []): array + { + return $this->executeQuery($query, $params, $types)->fetchAllKeyValue(); + } + + /** + * Prepares and executes an SQL query and returns the result as an associative array with the keys mapped + * to the first column and the values being an associative array representing the rest of the columns + * and their values. + * + * @param string $query SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @return array> + * + * @throws Exception + */ + public function fetchAllAssociativeIndexed(string $query, array $params = [], array $types = []): array + { + return $this->executeQuery($query, $params, $types)->fetchAllAssociativeIndexed(); + } + + /** + * Prepares and executes an SQL query and returns the result as an array of the first column values. + * + * @param string $query SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @return list + * + * @throws Exception + */ + public function fetchFirstColumn(string $query, array $params = [], array $types = []): array + { + try { + return $this->executeQuery($query, $params, $types)->fetchFirstColumn(); + } catch (Driver\Exception $e) { + throw $this->convertExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares and executes an SQL query and returns the result as an iterator over rows represented as numeric arrays. + * + * @param string $query SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @return Traversable> + * + * @throws Exception + */ + public function iterateNumeric(string $query, array $params = [], array $types = []): Traversable + { + try { + $result = $this->executeQuery($query, $params, $types); + + while (($row = $result->fetchNumeric()) !== false) { + yield $row; + } + } catch (Driver\Exception $e) { + throw $this->convertExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares and executes an SQL query and returns the result as an iterator over rows represented + * as associative arrays. + * + * @param string $query SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @return Traversable> + * + * @throws Exception + */ + public function iterateAssociative(string $query, array $params = [], array $types = []): Traversable + { + try { + $result = $this->executeQuery($query, $params, $types); + + while (($row = $result->fetchAssociative()) !== false) { + yield $row; + } + } catch (Driver\Exception $e) { + throw $this->convertExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares and executes an SQL query and returns the result as an iterator with the keys + * mapped to the first column and the values mapped to the second column. + * + * @param string $query SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @return Traversable + * + * @throws Exception + */ + public function iterateKeyValue(string $query, array $params = [], array $types = []): Traversable + { + return $this->executeQuery($query, $params, $types)->iterateKeyValue(); + } + + /** + * Prepares and executes an SQL query and returns the result as an iterator with the keys mapped + * to the first column and the values being an associative array representing the rest of the columns + * and their values. + * + * @param string $query SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @return Traversable> + * + * @throws Exception + */ + public function iterateAssociativeIndexed(string $query, array $params = [], array $types = []): Traversable + { + return $this->executeQuery($query, $params, $types)->iterateAssociativeIndexed(); + } + + /** + * Prepares and executes an SQL query and returns the result as an iterator over the first column values. + * + * @param string $query SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @return Traversable + * + * @throws Exception + */ + public function iterateColumn(string $query, array $params = [], array $types = []): Traversable + { + try { + $result = $this->executeQuery($query, $params, $types); + + while (($value = $result->fetchOne()) !== false) { + yield $value; + } + } catch (Driver\Exception $e) { + throw $this->convertExceptionDuringQuery($e, $query, $params, $types); + } + } + + /** + * Prepares an SQL statement. + * + * @param string $sql The SQL statement to prepare. + * + * @throws Exception + */ + public function prepare(string $sql): Statement + { + return new Statement($sql, $this); + } + + /** + * Executes an, optionally parametrized, SQL query. + * + * If the query is parametrized, a prepared statement is used. + * If an SQLLogger is configured, the execution is logged. + * + * @param string $sql SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @throws Exception + */ + public function executeQuery( + string $sql, + array $params = [], + $types = [], + ?QueryCacheProfile $qcp = null + ): Result { + if ($qcp !== null) { + return $this->executeCacheQuery($sql, $params, $types, $qcp); + } + + $connection = $this->getWrappedConnection(); + + $logger = $this->_config->getSQLLogger(); + if ($logger !== null) { + $logger->startQuery($sql, $params, $types); + } + + try { + if (count($params) > 0) { + if ($this->needsArrayParameterConversion($params, $types)) { + [$sql, $params, $types] = $this->expandArrayParameters($sql, $params, $types); + } + + $stmt = $connection->prepare($sql); + if (count($types) > 0) { + $this->_bindTypedValues($stmt, $params, $types); + $result = $stmt->execute(); + } else { + $result = $stmt->execute($params); + } + } else { + $result = $connection->query($sql); + } + + return new Result($result, $this); + } catch (Driver\Exception $e) { + throw $this->convertExceptionDuringQuery($e, $sql, $params, $types); + } finally { + if ($logger !== null) { + $logger->stopQuery(); + } + } + } + + /** + * Executes a caching query. + * + * @param string $sql SQL query + * @param list|array $params Query parameters + * @param array|array $types Parameter types + * + * @throws CacheException + * @throws Exception + */ + public function executeCacheQuery($sql, $params, $types, QueryCacheProfile $qcp): Result + { + $resultCache = $qcp->getResultCacheDriver() ?? $this->_config->getResultCacheImpl(); + + if ($resultCache === null) { + throw CacheException::noResultDriverConfigured(); + } + + $connectionParams = $this->params; + unset($connectionParams['platform']); + + [$cacheKey, $realKey] = $qcp->generateCacheKeys($sql, $params, $types, $connectionParams); + + // fetch the row pointers entry + $data = $resultCache->fetch($cacheKey); + + if ($data !== false) { + // is the real key part of this row pointers map or is the cache only pointing to other cache keys? + if (isset($data[$realKey])) { + $result = new ArrayResult($data[$realKey]); + } elseif (array_key_exists($realKey, $data)) { + $result = new ArrayResult([]); + } + } + + if (! isset($result)) { + $result = new CachingResult( + $this->executeQuery($sql, $params, $types), + $resultCache, + $cacheKey, + $realKey, + $qcp->getLifetime() + ); + } + + return new Result($result, $this); + } + + /** + * Executes an SQL statement with the given parameters and returns the number of affected rows. + * + * Could be used for: + * - DML statements: INSERT, UPDATE, DELETE, etc. + * - DDL statements: CREATE, DROP, ALTER, etc. + * - DCL statements: GRANT, REVOKE, etc. + * - Session control statements: ALTER SESSION, SET, DECLARE, etc. + * - Other statements that don't yield a row set. + * + * This method supports PDO binding types as well as DBAL mapping types. + * + * @param string $sql SQL statement + * @param list|array $params Statement parameters + * @param array|array $types Parameter types + * + * @return int The number of affected rows. + * + * @throws Exception + */ + public function executeStatement($sql, array $params = [], array $types = []) + { + $connection = $this->getWrappedConnection(); + + $logger = $this->_config->getSQLLogger(); + if ($logger !== null) { + $logger->startQuery($sql, $params, $types); + } + + try { + if (count($params) > 0) { + if ($this->needsArrayParameterConversion($params, $types)) { + [$sql, $params, $types] = $this->expandArrayParameters($sql, $params, $types); + } + + $stmt = $connection->prepare($sql); + + if (count($types) > 0) { + $this->_bindTypedValues($stmt, $params, $types); + + $result = $stmt->execute(); + } else { + $result = $stmt->execute($params); + } + + return $result->rowCount(); + } + + return $connection->exec($sql); + } catch (Driver\Exception $e) { + throw $this->convertExceptionDuringQuery($e, $sql, $params, $types); + } finally { + if ($logger !== null) { + $logger->stopQuery(); + } + } + } + + /** + * Returns the current transaction nesting level. + * + * @return int The nesting level. A value of 0 means there's no active transaction. + */ + public function getTransactionNestingLevel() + { + return $this->transactionNestingLevel; + } + + /** + * Returns the ID of the last inserted row, or the last value from a sequence object, + * depending on the underlying driver. + * + * Note: This method may not return a meaningful or consistent result across different drivers, + * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY + * columns or sequences. + * + * @param string|null $name Name of the sequence object from which the ID should be returned. + * + * @return string A string representation of the last inserted ID. + * + * @throws Exception + */ + public function lastInsertId($name = null) + { + try { + return $this->getWrappedConnection()->lastInsertId($name); + } catch (Driver\Exception $e) { + throw $this->convertException($e); + } + } + + /** + * Executes a function in a transaction. + * + * The function gets passed this Connection instance as an (optional) parameter. + * + * If an exception occurs during execution of the function or transaction commit, + * the transaction is rolled back and the exception re-thrown. + * + * @param Closure $func The function to execute transactionally. + * + * @return mixed The value returned by $func + * + * @throws Throwable + */ + public function transactional(Closure $func) + { + $this->beginTransaction(); + try { + $res = $func($this); + $this->commit(); + + return $res; + } catch (Throwable $e) { + $this->rollBack(); + + throw $e; + } + } + + /** + * Sets if nested transactions should use savepoints. + * + * @param bool $nestTransactionsWithSavepoints + * + * @return void + * + * @throws Exception + */ + public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints) + { + if ($this->transactionNestingLevel > 0) { + throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction(); + } + + if (! $this->getDatabasePlatform()->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->nestTransactionsWithSavepoints = (bool) $nestTransactionsWithSavepoints; + } + + /** + * Gets if nested transactions should use savepoints. + * + * @return bool + */ + public function getNestTransactionsWithSavepoints() + { + return $this->nestTransactionsWithSavepoints; + } + + /** + * Returns the savepoint name to use for nested transactions are false if they are not supported + * "savepointFormat" parameter is not set + * + * @return mixed A string with the savepoint name or false. + */ + protected function _getNestedTransactionSavePointName() + { + return 'DOCTRINE2_SAVEPOINT_' . $this->transactionNestingLevel; + } + + /** + * @return bool + * + * @throws Exception + */ + public function beginTransaction() + { + $connection = $this->getWrappedConnection(); + + ++$this->transactionNestingLevel; + + $logger = $this->_config->getSQLLogger(); + + if ($this->transactionNestingLevel === 1) { + if ($logger !== null) { + $logger->startQuery('"START TRANSACTION"'); + } + + $connection->beginTransaction(); + + if ($logger !== null) { + $logger->stopQuery(); + } + } elseif ($this->nestTransactionsWithSavepoints) { + if ($logger !== null) { + $logger->startQuery('"SAVEPOINT"'); + } + + $this->createSavepoint($this->_getNestedTransactionSavePointName()); + if ($logger !== null) { + $logger->stopQuery(); + } + } + + return true; + } + + /** + * @return bool + * + * @throws Exception + */ + public function commit() + { + if ($this->transactionNestingLevel === 0) { + throw ConnectionException::noActiveTransaction(); + } + + if ($this->isRollbackOnly) { + throw ConnectionException::commitFailedRollbackOnly(); + } + + $result = true; + + $connection = $this->getWrappedConnection(); + + $logger = $this->_config->getSQLLogger(); + + if ($this->transactionNestingLevel === 1) { + if ($logger !== null) { + $logger->startQuery('"COMMIT"'); + } + + $result = $connection->commit(); + + if ($logger !== null) { + $logger->stopQuery(); + } + } elseif ($this->nestTransactionsWithSavepoints) { + if ($logger !== null) { + $logger->startQuery('"RELEASE SAVEPOINT"'); + } + + $this->releaseSavepoint($this->_getNestedTransactionSavePointName()); + if ($logger !== null) { + $logger->stopQuery(); + } + } + + --$this->transactionNestingLevel; + + if ($this->autoCommit !== false || $this->transactionNestingLevel !== 0) { + return $result; + } + + $this->beginTransaction(); + + return $result; + } + + /** + * Commits all current nesting transactions. + * + * @throws Exception + */ + private function commitAll(): void + { + while ($this->transactionNestingLevel !== 0) { + if ($this->autoCommit === false && $this->transactionNestingLevel === 1) { + // When in no auto-commit mode, the last nesting commit immediately starts a new transaction. + // Therefore we need to do the final commit here and then leave to avoid an infinite loop. + $this->commit(); + + return; + } + + $this->commit(); + } + } + + /** + * Cancels any database changes done during the current transaction. + * + * @return bool + * + * @throws Exception + */ + public function rollBack() + { + if ($this->transactionNestingLevel === 0) { + throw ConnectionException::noActiveTransaction(); + } + + $connection = $this->getWrappedConnection(); + + $logger = $this->_config->getSQLLogger(); + + if ($this->transactionNestingLevel === 1) { + if ($logger !== null) { + $logger->startQuery('"ROLLBACK"'); + } + + $this->transactionNestingLevel = 0; + $connection->rollBack(); + $this->isRollbackOnly = false; + if ($logger !== null) { + $logger->stopQuery(); + } + + if ($this->autoCommit === false) { + $this->beginTransaction(); + } + } elseif ($this->nestTransactionsWithSavepoints) { + if ($logger !== null) { + $logger->startQuery('"ROLLBACK TO SAVEPOINT"'); + } + + $this->rollbackSavepoint($this->_getNestedTransactionSavePointName()); + --$this->transactionNestingLevel; + if ($logger !== null) { + $logger->stopQuery(); + } + } else { + $this->isRollbackOnly = true; + --$this->transactionNestingLevel; + } + + return true; + } + + /** + * Creates a new savepoint. + * + * @param string $savepoint The name of the savepoint to create. + * + * @return void + * + * @throws Exception + */ + public function createSavepoint($savepoint) + { + $platform = $this->getDatabasePlatform(); + + if (! $platform->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->executeStatement($platform->createSavePoint($savepoint)); + } + + /** + * Releases the given savepoint. + * + * @param string $savepoint The name of the savepoint to release. + * + * @return void + * + * @throws Exception + */ + public function releaseSavepoint($savepoint) + { + $platform = $this->getDatabasePlatform(); + + if (! $platform->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + if (! $platform->supportsReleaseSavepoints()) { + return; + } + + $this->executeStatement($platform->releaseSavePoint($savepoint)); + } + + /** + * Rolls back to the given savepoint. + * + * @param string $savepoint The name of the savepoint to rollback to. + * + * @return void + * + * @throws Exception + */ + public function rollbackSavepoint($savepoint) + { + $platform = $this->getDatabasePlatform(); + + if (! $platform->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->executeStatement($platform->rollbackSavePoint($savepoint)); + } + + /** + * Gets the wrapped driver connection. + * + * @return DriverConnection + * + * @throws Exception + */ + public function getWrappedConnection() + { + $this->connect(); + + assert($this->_conn !== null); + + return $this->_conn; + } + + /** + * Creates a SchemaManager that can be used to inspect or change the + * database schema through the connection. + * + * @throws Exception + */ + public function createSchemaManager(): AbstractSchemaManager + { + return $this->_driver->getSchemaManager( + $this, + $this->getDatabasePlatform() + ); + } + + /** + * Gets the SchemaManager that can be used to inspect or change the + * database schema through the connection. + * + * @deprecated Use {@link createSchemaManager()} instead. + * + * @return AbstractSchemaManager + * + * @throws Exception + */ + public function getSchemaManager() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4515', + 'Connection::getSchemaManager() is deprecated, use Connection::createSchemaManager() instead.' + ); + + if ($this->_schemaManager === null) { + $this->_schemaManager = $this->createSchemaManager(); + } + + return $this->_schemaManager; + } + + /** + * Marks the current transaction so that the only possible + * outcome for the transaction to be rolled back. + * + * @return void + * + * @throws ConnectionException If no transaction is active. + */ + public function setRollbackOnly() + { + if ($this->transactionNestingLevel === 0) { + throw ConnectionException::noActiveTransaction(); + } + + $this->isRollbackOnly = true; + } + + /** + * Checks whether the current transaction is marked for rollback only. + * + * @return bool + * + * @throws ConnectionException If no transaction is active. + */ + public function isRollbackOnly() + { + if ($this->transactionNestingLevel === 0) { + throw ConnectionException::noActiveTransaction(); + } + + return $this->isRollbackOnly; + } + + /** + * Converts a given value to its database representation according to the conversion + * rules of a specific DBAL mapping type. + * + * @param mixed $value The value to convert. + * @param string $type The name of the DBAL mapping type. + * + * @return mixed The converted value. + * + * @throws Exception + */ + public function convertToDatabaseValue($value, $type) + { + return Type::getType($type)->convertToDatabaseValue($value, $this->getDatabasePlatform()); + } + + /** + * Converts a given value to its PHP representation according to the conversion + * rules of a specific DBAL mapping type. + * + * @param mixed $value The value to convert. + * @param string $type The name of the DBAL mapping type. + * + * @return mixed The converted type. + * + * @throws Exception + */ + public function convertToPHPValue($value, $type) + { + return Type::getType($type)->convertToPHPValue($value, $this->getDatabasePlatform()); + } + + /** + * Binds a set of parameters, some or all of which are typed with a PDO binding type + * or DBAL mapping type, to a given statement. + * + * @param DriverStatement $stmt Prepared statement + * @param list|array $params Statement parameters + * @param array|array $types Parameter types + * + * @throws Exception + */ + private function _bindTypedValues(DriverStatement $stmt, array $params, array $types): void + { + // Check whether parameters are positional or named. Mixing is not allowed. + if (is_int(key($params))) { + $bindIndex = 1; + + foreach ($params as $key => $value) { + if (isset($types[$key])) { + $type = $types[$key]; + [$value, $bindingType] = $this->getBindingInfo($value, $type); + $stmt->bindValue($bindIndex, $value, $bindingType); + } else { + $stmt->bindValue($bindIndex, $value); + } + + ++$bindIndex; + } + } else { + // Named parameters + foreach ($params as $name => $value) { + if (isset($types[$name])) { + $type = $types[$name]; + [$value, $bindingType] = $this->getBindingInfo($value, $type); + $stmt->bindValue($name, $value, $bindingType); + } else { + $stmt->bindValue($name, $value); + } + } + } + } + + /** + * Gets the binding type of a given type. + * + * @param mixed $value The value to bind. + * @param int|string|Type|null $type The type to bind (PDO or DBAL). + * + * @return mixed[] [0] => the (escaped) value, [1] => the binding type. + * + * @throws Exception + */ + private function getBindingInfo($value, $type) + { + if (is_string($type)) { + $type = Type::getType($type); + } + + if ($type instanceof Type) { + $value = $type->convertToDatabaseValue($value, $this->getDatabasePlatform()); + $bindingType = $type->getBindingType(); + } else { + $bindingType = $type; + } + + return [$value, $bindingType]; + } + + /** + * Creates a new instance of a SQL query builder. + * + * @return QueryBuilder + */ + public function createQueryBuilder() + { + return new Query\QueryBuilder($this); + } + + /** + * @internal + * + * @param list|array $params + * @param array|array $types + */ + final public function convertExceptionDuringQuery( + Driver\Exception $e, + string $sql, + array $params = [], + array $types = [] + ): DriverException { + return $this->handleDriverException($e, new Query($sql, $params, $types)); + } + + /** + * @internal + */ + final public function convertException(Driver\Exception $e): DriverException + { + return $this->handleDriverException($e, null); + } + + /** + * @param array|array $params + * @param array|array $types + * + * @return array{string, list, array} + */ + private function expandArrayParameters(string $sql, array $params, array $types): array + { + if ($this->parser === null) { + $this->parser = $this->getDatabasePlatform()->createSQLParser(); + } + + $visitor = new ExpandArrayParameters($params, $types); + + $this->parser->parse($sql, $visitor); + + return [ + $visitor->getSQL(), + $visitor->getParameters(), + $visitor->getTypes(), + ]; + } + + /** + * @param array|array $params + * @param array|array $types + */ + private function needsArrayParameterConversion(array $params, array $types): bool + { + if (is_string(key($params))) { + return true; + } + + foreach ($types as $type) { + if ($type === self::PARAM_INT_ARRAY || $type === self::PARAM_STR_ARRAY) { + return true; + } + } + + return false; + } + + private function handleDriverException( + Driver\Exception $driverException, + ?Query $query + ): DriverException { + if ($this->exceptionConverter === null) { + $this->exceptionConverter = $this->_driver->getExceptionConverter(); + } + + $exception = $this->exceptionConverter->convert($driverException, $query); + + if ($exception instanceof ConnectionLost) { + $this->close(); + } + + return $exception; + } + + /** + * BC layer for a wide-spread use-case of old DBAL APIs + * + * @deprecated This API is deprecated and will be removed after 2022 + * + * @param array $params The query parameters + * @param array $types The parameter types + */ + public function executeUpdate(string $sql, array $params = [], array $types = []): int + { + return $this->executeStatement($sql, $params, $types); + } + + /** + * BC layer for a wide-spread use-case of old DBAL APIs + * + * @deprecated This API is deprecated and will be removed after 2022 + */ + public function query(string $sql): Result + { + return $this->executeQuery($sql); + } + + /** + * BC layer for a wide-spread use-case of old DBAL APIs + * + * @deprecated This API is deprecated and will be removed after 2022 + */ + public function exec(string $sql): int + { + return $this->executeStatement($sql); + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/ConnectionException.php b/app/vendor/doctrine/dbal/src/ConnectionException.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/ConnectionException.php rename to app/vendor/doctrine/dbal/src/ConnectionException.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connections/PrimaryReadReplicaConnection.php b/app/vendor/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php similarity index 81% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connections/PrimaryReadReplicaConnection.php rename to app/vendor/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php index e2606e7d2..9d38f904b 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connections/PrimaryReadReplicaConnection.php +++ b/app/vendor/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php @@ -7,15 +7,16 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver\Connection as DriverConnection; +use Doctrine\DBAL\Driver\Exception as DriverException; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Event\ConnectionEventArgs; use Doctrine\DBAL\Events; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Statement; use InvalidArgumentException; use function array_rand; -use function assert; use function count; -use function func_get_args; /** * Primary-Replica Connection @@ -27,9 +28,8 @@ * * 1. Replica if primary was never picked before and ONLY if 'getWrappedConnection' * or 'executeQuery' is used. - * 2. Primary picked when 'exec', 'executeUpdate', 'executeStatement', 'insert', 'delete', 'update', 'createSavepoint', - * 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit', 'query' or - * 'prepare' is called. + * 2. Primary picked when 'executeStatement', 'insert', 'delete', 'update', 'createSavepoint', + * 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit' or 'prepare' is called. * 3. If Primary was picked once during the lifetime of the connection it will always get picked afterwards. * 4. One replica connection is randomly picked ONCE during a request. * @@ -99,6 +99,7 @@ class PrimaryReadReplicaConnection extends Connection * @psalm-param Params $params * @phpstan-param array $params * + * @throws Exception * @throws InvalidArgumentException */ public function __construct( @@ -156,7 +157,7 @@ public function connect($connectionName = null) protected function performConnect(?string $connectionName = null): bool { $requestedConnectionChange = ($connectionName !== null); - $connectionName = $connectionName ?: 'replica'; + $connectionName = $connectionName ?? 'replica'; if ($connectionName !== 'replica' && $connectionName !== 'primary') { throw new InvalidArgumentException('Invalid option to connect(), only primary or replica allowed.'); @@ -233,19 +234,20 @@ public function ensureConnectedToReplica(): bool * @param string $connectionName * * @return DriverConnection + * + * @throws Exception */ protected function connectTo($connectionName) { $params = $this->getParams(); - $driverOptions = $params['driverOptions'] ?? []; - $connectionParams = $this->chooseConnectionConfiguration($connectionName, $params); - $user = $connectionParams['user'] ?? null; - $password = $connectionParams['password'] ?? null; - - return $this->_driver->connect($connectionParams, $user, $password, $driverOptions); + try { + return $this->_driver->connect($connectionParams); + } catch (DriverException $e) { + throw $this->convertException($e); + } } /** @@ -269,18 +271,6 @@ protected function chooseConnectionConfiguration($connectionName, $params) return $config; } - /** - * {@inheritDoc} - * - * @deprecated Use {@link executeStatement()} instead. - */ - public function executeUpdate($sql, array $params = [], array $types = []) - { - $this->ensureConnectedToPrimary(); - - return parent::executeUpdate($sql, $params, $types); - } - /** * {@inheritDoc} */ @@ -321,16 +311,6 @@ public function rollBack() return parent::rollBack(); } - /** - * {@inheritDoc} - */ - public function delete($table, array $criteria, array $types = []) - { - $this->ensureConnectedToPrimary(); - - return parent::delete($table, $criteria, $types); - } - /** * {@inheritDoc} */ @@ -344,36 +324,6 @@ public function close() $this->connections = ['primary' => null, 'replica' => null]; } - /** - * {@inheritDoc} - */ - public function update($table, array $data, array $criteria, array $types = []) - { - $this->ensureConnectedToPrimary(); - - return parent::update($table, $data, $criteria, $types); - } - - /** - * {@inheritDoc} - */ - public function insert($table, array $data, array $types = []) - { - $this->ensureConnectedToPrimary(); - - return parent::insert($table, $data, $types); - } - - /** - * {@inheritDoc} - */ - public function exec($sql) - { - $this->ensureConnectedToPrimary(); - - return parent::exec($sql); - } - /** * {@inheritDoc} */ @@ -404,36 +354,7 @@ public function rollbackSavepoint($savepoint) parent::rollbackSavepoint($savepoint); } - /** - * {@inheritDoc} - */ - public function query() - { - $this->ensureConnectedToPrimary(); - assert($this->_conn instanceof DriverConnection); - - $args = func_get_args(); - - $logger = $this->getConfiguration()->getSQLLogger(); - if ($logger) { - $logger->startQuery($args[0]); - } - - $statement = $this->_conn->query(...$args); - - $statement->setFetchMode($this->defaultFetchMode); - - if ($logger) { - $logger->stopQuery(); - } - - return $statement; - } - - /** - * {@inheritDoc} - */ - public function prepare($sql) + public function prepare(string $sql): Statement { $this->ensureConnectedToPrimary(); diff --git a/app/vendor/doctrine/dbal/src/Driver.php b/app/vendor/doctrine/dbal/src/Driver.php new file mode 100644 index 000000000..50a6005f9 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver.php @@ -0,0 +1,48 @@ +getCode()) { + case 1213: + return new DeadlockException($exception, $query); + + case 1205: + return new LockWaitTimeoutException($exception, $query); + + case 1050: + return new TableExistsException($exception, $query); + + case 1051: + case 1146: + return new TableNotFoundException($exception, $query); + + case 1216: + case 1217: + case 1451: + case 1452: + case 1701: + return new ForeignKeyConstraintViolationException($exception, $query); + + case 1062: + case 1557: + case 1569: + case 1586: + return new UniqueConstraintViolationException($exception, $query); + + case 1054: + case 1166: + case 1611: + return new InvalidFieldNameException($exception, $query); + + case 1052: + case 1060: + case 1110: + return new NonUniqueFieldNameException($exception, $query); + + case 1064: + case 1149: + case 1287: + case 1341: + case 1342: + case 1343: + case 1344: + case 1382: + case 1479: + case 1541: + case 1554: + case 1626: + return new SyntaxErrorException($exception, $query); + + case 1044: + case 1045: + case 1046: + case 1049: + case 1095: + case 1142: + case 1143: + case 1227: + case 1370: + case 1429: + case 2002: + case 2005: + return new ConnectionException($exception, $query); + + case 2006: + return new ConnectionLost($exception, $query); + + case 1048: + case 1121: + case 1138: + case 1171: + case 1252: + case 1263: + case 1364: + case 1566: + return new NotNullConstraintViolationException($exception, $query); + } + + return new DriverException($exception, $query); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php b/app/vendor/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php new file mode 100644 index 000000000..f25e58d6c --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php @@ -0,0 +1,68 @@ +getCode()) { + case 1: + case 2299: + case 38911: + return new UniqueConstraintViolationException($exception, $query); + + case 904: + return new InvalidFieldNameException($exception, $query); + + case 918: + case 960: + return new NonUniqueFieldNameException($exception, $query); + + case 923: + return new SyntaxErrorException($exception, $query); + + case 942: + return new TableNotFoundException($exception, $query); + + case 955: + return new TableExistsException($exception, $query); + + case 1017: + case 12545: + return new ConnectionException($exception, $query); + + case 1400: + return new NotNullConstraintViolationException($exception, $query); + + case 2266: + case 2291: + case 2292: + return new ForeignKeyConstraintViolationException($exception, $query); + } + + return new DriverException($exception, $query); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php b/app/vendor/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php new file mode 100644 index 000000000..b9530d0b2 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php @@ -0,0 +1,85 @@ +getSQLState()) { + case '40001': + case '40P01': + return new DeadlockException($exception, $query); + + case '0A000': + // Foreign key constraint violations during a TRUNCATE operation + // are considered "feature not supported" in PostgreSQL. + if (strpos($exception->getMessage(), 'truncate') !== false) { + return new ForeignKeyConstraintViolationException($exception, $query); + } + + break; + + case '23502': + return new NotNullConstraintViolationException($exception, $query); + + case '23503': + return new ForeignKeyConstraintViolationException($exception, $query); + + case '23505': + return new UniqueConstraintViolationException($exception, $query); + + case '42601': + return new SyntaxErrorException($exception, $query); + + case '42702': + return new NonUniqueFieldNameException($exception, $query); + + case '42703': + return new InvalidFieldNameException($exception, $query); + + case '42P01': + return new TableNotFoundException($exception, $query); + + case '42P07': + return new TableExistsException($exception, $query); + + case '08006': + return new ConnectionException($exception, $query); + } + + // Prior to fixing https://bugs.php.net/bug.php?id=64705 (PHP 7.3.22 and PHP 7.4.10), + // in some cases (mainly connection errors) the PDO exception wouldn't provide a SQLSTATE via its code. + // We have to match against the SQLSTATE in the error message in these cases. + if ($exception->getCode() === 7 && strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) { + return new ConnectionException($exception, $query); + } + + return new DriverException($exception, $query); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php b/app/vendor/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php new file mode 100644 index 000000000..3fc3ff081 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php @@ -0,0 +1,21 @@ +getMessage(), 'database is locked') !== false) { + return new LockWaitTimeoutException($exception, $query); + } + + if ( + strpos($exception->getMessage(), 'must be unique') !== false || + strpos($exception->getMessage(), 'is not unique') !== false || + strpos($exception->getMessage(), 'are not unique') !== false || + strpos($exception->getMessage(), 'UNIQUE constraint failed') !== false + ) { + return new UniqueConstraintViolationException($exception, $query); + } + + if ( + strpos($exception->getMessage(), 'may not be NULL') !== false || + strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false + ) { + return new NotNullConstraintViolationException($exception, $query); + } + + if (strpos($exception->getMessage(), 'no such table:') !== false) { + return new TableNotFoundException($exception, $query); + } + + if (strpos($exception->getMessage(), 'already exists') !== false) { + return new TableExistsException($exception, $query); + } + + if (strpos($exception->getMessage(), 'has no column named') !== false) { + return new InvalidFieldNameException($exception, $query); + } + + if (strpos($exception->getMessage(), 'ambiguous column name') !== false) { + return new NonUniqueFieldNameException($exception, $query); + } + + if (strpos($exception->getMessage(), 'syntax error') !== false) { + return new SyntaxErrorException($exception, $query); + } + + if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) { + return new ReadOnlyException($exception, $query); + } + + if (strpos($exception->getMessage(), 'unable to open database file') !== false) { + return new ConnectionException($exception, $query); + } + + return new DriverException($exception, $query); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/AbstractDB2Driver.php b/app/vendor/doctrine/dbal/src/Driver/AbstractDB2Driver.php new file mode 100644 index 000000000..d8c707bb0 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/AbstractDB2Driver.php @@ -0,0 +1,38 @@ +sqlState = $sqlState; + } + + /** + * {@inheritdoc} + */ + public function getSQLState() + { + return $this->sqlState; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/AbstractMySQLDriver.php b/app/vendor/doctrine/dbal/src/Driver/AbstractMySQLDriver.php new file mode 100644 index 000000000..31949b9c1 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/AbstractMySQLDriver.php @@ -0,0 +1,136 @@ +getMariaDbMysqlVersionNumber($version), '10.2.7', '>=')) { + return new MariaDb1027Platform(); + } + + if (! $mariadb) { + $oracleMysqlVersion = $this->getOracleMysqlVersionNumber($version); + if (version_compare($oracleMysqlVersion, '8', '>=')) { + return new MySQL80Platform(); + } + + if (version_compare($oracleMysqlVersion, '5.7.9', '>=')) { + return new MySQL57Platform(); + } + } + + return $this->getDatabasePlatform(); + } + + /** + * Get a normalized 'version number' from the server string + * returned by Oracle MySQL servers. + * + * @param string $versionString Version string returned by the driver, i.e. '5.7.10' + * + * @throws Exception + */ + private function getOracleMysqlVersionNumber(string $versionString): string + { + if ( + preg_match( + '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', + $versionString, + $versionParts + ) === 0 + ) { + throw Exception::invalidPlatformVersionSpecified( + $versionString, + '..' + ); + } + + $majorVersion = $versionParts['major']; + $minorVersion = $versionParts['minor'] ?? 0; + $patchVersion = $versionParts['patch'] ?? null; + + if ($majorVersion === '5' && $minorVersion === '7' && $patchVersion === null) { + $patchVersion = '9'; + } + + return $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + } + + /** + * Detect MariaDB server version, including hack for some mariadb distributions + * that starts with the prefix '5.5.5-' + * + * @param string $versionString Version string as returned by mariadb server, i.e. '5.5.5-Mariadb-10.0.8-xenial' + * + * @throws Exception + */ + private function getMariaDbMysqlVersionNumber(string $versionString): string + { + if ( + preg_match( + '/^(?:5\.5\.5-)?(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', + $versionString, + $versionParts + ) === 0 + ) { + throw Exception::invalidPlatformVersionSpecified( + $versionString, + '^(?:5\.5\.5-)?(mariadb-)?..' + ); + } + + return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch']; + } + + /** + * {@inheritdoc} + * + * @return MySQLPlatform + */ + public function getDatabasePlatform() + { + return new MySQLPlatform(); + } + + /** + * {@inheritdoc} + * + * @return MySQLSchemaManager + */ + public function getSchemaManager(Connection $conn, AbstractPlatform $platform) + { + return new MySQLSchemaManager($conn, $platform); + } + + public function getExceptionConverter(): ExceptionConverter + { + return new MySQL\ExceptionConverter(); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/AbstractOracleDriver.php b/app/vendor/doctrine/dbal/src/Driver/AbstractOracleDriver.php new file mode 100644 index 000000000..80e4a02c8 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/AbstractOracleDriver.php @@ -0,0 +1,51 @@ +\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $version, $versionParts) === 0) { + throw Exception::invalidPlatformVersionSpecified( + $version, + '..' + ); + } + + $majorVersion = $versionParts['major']; + $minorVersion = $versionParts['minor'] ?? 0; + $patchVersion = $versionParts['patch'] ?? 0; + $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + + if (version_compare($version, '10.0', '>=')) { + return new PostgreSQL100Platform(); + } + + return new PostgreSQL94Platform(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new PostgreSQL94Platform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(Connection $conn, AbstractPlatform $platform) + { + return new PostgreSQLSchemaManager($conn, $platform); + } + + public function getExceptionConverter(): ExceptionConverter + { + return new PostgreSQL\ExceptionConverter(); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php b/app/vendor/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php new file mode 100644 index 000000000..b3d0928bb --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php @@ -0,0 +1,38 @@ +> + * @return list> * * @throws Exception */ @@ -42,7 +42,7 @@ public static function fetchAllNumeric(Result $result): array } /** - * @return array> + * @return list> * * @throws Exception */ @@ -58,7 +58,7 @@ public static function fetchAllAssociative(Result $result): array } /** - * @return array + * @return list * * @throws Exception */ diff --git a/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Connection.php b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Connection.php new file mode 100644 index 000000000..c8e36da3e --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Connection.php @@ -0,0 +1,165 @@ + $driverOptions + * + * @throws Exception + */ + public function __construct( + string $database, + bool $persistent, + string $username, + string $password, + array $driverOptions = [] + ) { + if ($persistent) { + $conn = db2_pconnect($database, $username, $password, $driverOptions); + } else { + $conn = db2_connect($database, $username, $password, $driverOptions); + } + + if ($conn === false) { + throw ConnectionFailed::new(); + } + + $this->conn = $conn; + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + $serverInfo = db2_server_info($this->conn); + assert($serverInfo instanceof stdClass); + + return $serverInfo->DBMS_VER; + } + + public function prepare(string $sql): DriverStatement + { + $stmt = @db2_prepare($this->conn, $sql); + + if ($stmt === false) { + throw PrepareFailed::new(error_get_last()); + } + + return new Statement($stmt); + } + + public function query(string $sql): ResultInterface + { + return $this->prepare($sql)->execute(); + } + + /** + * {@inheritdoc} + */ + public function quote($value, $type = ParameterType::STRING) + { + $value = db2_escape_string($value); + + if ($type === ParameterType::INTEGER) { + return $value; + } + + return "'" . $value . "'"; + } + + public function exec(string $sql): int + { + $stmt = @db2_exec($this->conn, $sql); + + if ($stmt === false) { + throw ConnectionError::new($this->conn); + } + + return db2_num_rows($stmt); + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + return db2_last_insert_id($this->conn); + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + $result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_OFF); + assert(is_bool($result)); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + if (! db2_commit($this->conn)) { + throw ConnectionError::new($this->conn); + } + + $result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON); + assert(is_bool($result)); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function rollBack() + { + if (! db2_rollback($this->conn)) { + throw ConnectionError::new($this->conn); + } + + $result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON); + assert(is_bool($result)); + + return $result; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DataSourceName.php b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/DataSourceName.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DataSourceName.php rename to app/vendor/doctrine/dbal/src/Driver/IBMDB2/DataSourceName.php diff --git a/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Driver.php b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Driver.php new file mode 100644 index 000000000..c08165520 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Driver.php @@ -0,0 +1,24 @@ +toString(), + isset($params['persistent']) && $params['persistent'] === true, + $params['user'] ?? '', + $params['password'] ?? '', + $params['driverOptions'] ?? [] + ); + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php similarity index 81% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php rename to app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php index 61244c1cc..86821688c 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php +++ b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php @@ -4,14 +4,14 @@ namespace Doctrine\DBAL\Driver\IBMDB2\Exception; -use Doctrine\DBAL\Driver\IBMDB2\DB2Exception; +use Doctrine\DBAL\Driver\AbstractException; /** * @internal * * @psalm-immutable */ -final class CannotCopyStreamToStream extends DB2Exception +final class CannotCopyStreamToStream extends AbstractException { /** * @psalm-param array{message: string}|null $error diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php similarity index 80% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php rename to app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php index d5481ed9e..587ae65f4 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php +++ b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php @@ -4,14 +4,14 @@ namespace Doctrine\DBAL\Driver\IBMDB2\Exception; -use Doctrine\DBAL\Driver\IBMDB2\DB2Exception; +use Doctrine\DBAL\Driver\AbstractException; /** * @internal * * @psalm-immutable */ -final class CannotCreateTemporaryFile extends DB2Exception +final class CannotCreateTemporaryFile extends AbstractException { /** * @psalm-param array{message: string}|null $error diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/CannotWriteToTemporaryFile.php b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotWriteToTemporaryFile.php similarity index 80% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/CannotWriteToTemporaryFile.php rename to app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotWriteToTemporaryFile.php index 33d0b86cc..f2287b300 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/CannotWriteToTemporaryFile.php +++ b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotWriteToTemporaryFile.php @@ -4,14 +4,14 @@ namespace Doctrine\DBAL\Driver\IBMDB2\Exception; -use Doctrine\DBAL\Driver\IBMDB2\DB2Exception; +use Doctrine\DBAL\Driver\AbstractException; /** * @internal * * @psalm-immutable */ -final class CannotWriteToTemporaryFile extends DB2Exception +final class CannotWriteToTemporaryFile extends AbstractException { /** * @psalm-param array{message: string}|null $error diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/ConnectionError.php b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionError.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/ConnectionError.php rename to app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionError.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/ConnectionFailed.php b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionFailed.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/ConnectionFailed.php rename to app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/ConnectionFailed.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/PrepareFailed.php b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/PrepareFailed.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/PrepareFailed.php rename to app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/PrepareFailed.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/StatementError.php b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/StatementError.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/Exception/StatementError.php rename to app/vendor/doctrine/dbal/src/Driver/IBMDB2/Exception/StatementError.php diff --git a/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Result.php b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Result.php new file mode 100644 index 000000000..d8e9fc599 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Result.php @@ -0,0 +1,113 @@ +statement = $statement; + } + + /** + * {@inheritDoc} + */ + public function fetchNumeric() + { + $row = @db2_fetch_array($this->statement); + + if ($row === false && db2_stmt_error($this->statement) !== '02000') { + throw StatementError::new($this->statement); + } + + return $row; + } + + /** + * {@inheritDoc} + */ + public function fetchAssociative() + { + $row = @db2_fetch_assoc($this->statement); + + if ($row === false && db2_stmt_error($this->statement) !== '02000') { + throw StatementError::new($this->statement); + } + + return $row; + } + + /** + * {@inheritDoc} + */ + public function fetchOne() + { + return FetchUtils::fetchOne($this); + } + + /** + * {@inheritDoc} + */ + public function fetchAllNumeric(): array + { + return FetchUtils::fetchAllNumeric($this); + } + + /** + * {@inheritDoc} + */ + public function fetchAllAssociative(): array + { + return FetchUtils::fetchAllAssociative($this); + } + + /** + * {@inheritDoc} + */ + public function fetchFirstColumn(): array + { + return FetchUtils::fetchFirstColumn($this); + } + + public function rowCount(): int + { + return @db2_num_rows($this->statement); + } + + public function columnCount(): int + { + $count = db2_num_fields($this->statement); + + if ($count !== false) { + return $count; + } + + return 0; + } + + public function free(): void + { + db2_free_result($this->statement); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Statement.php b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Statement.php new file mode 100644 index 000000000..c655ea74a --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/IBMDB2/Statement.php @@ -0,0 +1,198 @@ +stmt = $stmt; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = ParameterType::STRING) + { + assert(is_int($param)); + + return $this->bindParam($param, $value, $type); + } + + /** + * {@inheritdoc} + */ + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + { + assert(is_int($param)); + + switch ($type) { + case ParameterType::INTEGER: + $this->bind($param, $variable, DB2_PARAM_IN, DB2_LONG); + break; + + case ParameterType::LARGE_OBJECT: + if (isset($this->lobs[$param])) { + [, $handle] = $this->lobs[$param]; + fclose($handle); + } + + $handle = $this->createTemporaryFile(); + $path = stream_get_meta_data($handle)['uri']; + + $this->bind($param, $path, DB2_PARAM_FILE, DB2_BINARY); + + $this->lobs[$param] = [&$variable, $handle]; + break; + + default: + $this->bind($param, $variable, DB2_PARAM_IN, DB2_CHAR); + break; + } + + return true; + } + + /** + * @param int $position Parameter position + * @param mixed $variable + * + * @throws Exception + */ + private function bind($position, &$variable, int $parameterType, int $dataType): void + { + $this->bindParam[$position] =& $variable; + + if (! db2_bind_param($this->stmt, $position, 'variable', $parameterType, $dataType)) { + throw StatementError::new($this->stmt); + } + } + + /** + * {@inheritdoc} + */ + public function execute($params = null): ResultInterface + { + if ($params === null) { + ksort($this->bindParam); + + $params = []; + + foreach ($this->bindParam as $value) { + $params[] = $value; + } + } + + foreach ($this->lobs as [$source, $target]) { + if (is_resource($source)) { + $this->copyStreamToStream($source, $target); + + continue; + } + + $this->writeStringToStream($source, $target); + } + + $result = db2_execute($this->stmt, $params); + + foreach ($this->lobs as [, $handle]) { + fclose($handle); + } + + $this->lobs = []; + + if ($result === false) { + throw StatementError::new($this->stmt); + } + + return new Result($this->stmt); + } + + /** + * @return resource + * + * @throws Exception + */ + private function createTemporaryFile() + { + $handle = @tmpfile(); + + if ($handle === false) { + throw CannotCreateTemporaryFile::new(error_get_last()); + } + + return $handle; + } + + /** + * @param resource $source + * @param resource $target + * + * @throws Exception + */ + private function copyStreamToStream($source, $target): void + { + if (@stream_copy_to_stream($source, $target) === false) { + throw CannotCopyStreamToStream::new(error_get_last()); + } + } + + /** + * @param resource $target + * + * @throws Exception + */ + private function writeStringToStream(string $string, $target): void + { + if (@fwrite($target, $string) === false) { + throw CannotWriteToTemporaryFile::new(error_get_last()); + } + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/Middleware.php b/app/vendor/doctrine/dbal/src/Driver/Middleware.php new file mode 100644 index 000000000..4629d9a85 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/Middleware.php @@ -0,0 +1,12 @@ + $preInitializers + * @param iterable $postInitializers + * + * @throws Exception + */ + public function __construct( + ?string $host = null, + ?string $username = null, + ?string $password = null, + ?string $database = null, + ?int $port = null, + ?string $socket = null, + ?int $flags = null, + iterable $preInitializers = [], + iterable $postInitializers = [] + ) { + $connection = mysqli_init(); + assert($connection !== false); + + foreach ($preInitializers as $initializer) { + $initializer->initialize($connection); + } + + if (! @$connection->real_connect($host, $username, $password, $database, $port, $socket, $flags)) { + throw ConnectionFailed::new($connection); + } + + foreach ($postInitializers as $initializer) { + $initializer->initialize($connection); + } + + $this->conn = $connection; + } + + /** + * Retrieves mysqli native resource handle. + * + * Could be used if part of your application is not using DBAL. + * + * @return mysqli + */ + public function getWrappedResourceHandle() + { + return $this->conn; + } + + /** + * {@inheritdoc} + * + * The server version detection includes a special case for MariaDB + * to support '5.5.5-' prefixed versions introduced in Maria 10+ + * + * @link https://jira.mariadb.org/browse/MDEV-4088 + */ + public function getServerVersion() + { + $serverInfos = $this->conn->get_server_info(); + if (stripos($serverInfos, 'mariadb') !== false) { + return $serverInfos; + } + + $majorVersion = floor($this->conn->server_version / 10000); + $minorVersion = floor(($this->conn->server_version - $majorVersion * 10000) / 100); + $patchVersion = floor($this->conn->server_version - $majorVersion * 10000 - $minorVersion * 100); + + return $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + } + + public function prepare(string $sql): DriverStatement + { + return new Statement($this->conn, $sql); + } + + public function query(string $sql): ResultInterface + { + return $this->prepare($sql)->execute(); + } + + /** + * {@inheritdoc} + */ + public function quote($value, $type = ParameterType::STRING) + { + return "'" . $this->conn->escape_string($value) . "'"; + } + + public function exec(string $sql): int + { + if ($this->conn->query($sql) === false) { + throw ConnectionError::new($this->conn); + } + + return $this->conn->affected_rows; + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + return $this->conn->insert_id; + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + $this->conn->query('START TRANSACTION'); + + return true; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->conn->commit(); + } + + /** + * {@inheritdoc} + */ + public function rollBack() + { + return $this->conn->rollback(); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/Mysqli/Driver.php b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Driver.php new file mode 100644 index 000000000..28897ed3d --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Driver.php @@ -0,0 +1,119 @@ +withOptions($preInitializers, $driverOptions); + } + + $preInitializers = $this->withSecure($preInitializers, $params); + $postInitializers = $this->withCharset($postInitializers, $params); + + return new Connection( + $host, + $params['user'] ?? null, + $params['password'] ?? null, + $params['dbname'] ?? null, + $params['port'] ?? null, + $params['unix_socket'] ?? null, + $flags, + $preInitializers, + $postInitializers + ); + } + + /** + * @param list $initializers + * @param array $options + * + * @return list + */ + private function withOptions(array $initializers, array $options): array + { + if (count($options) !== 0) { + $initializers[] = new Options($options); + } + + return $initializers; + } + + /** + * @param list $initializers + * @param array $params + * + * @return list + */ + private function withSecure(array $initializers, array $params): array + { + if ( + isset($params['ssl_key']) || + isset($params['ssl_cert']) || + isset($params['ssl_ca']) || + isset($params['ssl_capath']) || + isset($params['ssl_cipher']) + ) { + $initializers[] = new Secure( + $params['ssl_key'] ?? '', + $params['ssl_cert'] ?? '', + $params['ssl_ca'] ?? '', + $params['ssl_capath'] ?? '', + $params['ssl_cipher'] ?? '' + ); + } + + return $initializers; + } + + /** + * @param list $initializers + * @param array $params + * + * @return list + */ + private function withCharset(array $initializers, array $params): array + { + if (isset($params['charset'])) { + $initializers[] = new Charset($params['charset']); + } + + return $initializers; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionError.php b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionError.php new file mode 100644 index 000000000..25c33f065 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionError.php @@ -0,0 +1,21 @@ +error, $connection->sqlstate, $connection->errno); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php new file mode 100644 index 000000000..16cab72e5 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php @@ -0,0 +1,21 @@ +connect_error, 'HY000', $connection->connect_errno); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/FailedReadingStreamOffset.php b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/FailedReadingStreamOffset.php new file mode 100644 index 000000000..6f26dbec8 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/FailedReadingStreamOffset.php @@ -0,0 +1,22 @@ +error), + $connection->sqlstate, + $connection->errno + ); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php new file mode 100644 index 000000000..962175679 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php @@ -0,0 +1,27 @@ +charset = $charset; + } + + public function initialize(mysqli $connection): void + { + if ($connection->set_charset($this->charset)) { + return; + } + + throw InvalidCharset::fromCharset($connection, $this->charset); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php new file mode 100644 index 000000000..bcf2fc2d1 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php @@ -0,0 +1,34 @@ + */ + private $options; + + /** + * @param array $options + */ + public function __construct(array $options) + { + $this->options = $options; + } + + public function initialize(mysqli $connection): void + { + foreach ($this->options as $option => $value) { + if (! mysqli_options($connection, $option, $value)) { + throw InvalidOption::fromOption($option, $value); + } + } + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php new file mode 100644 index 000000000..9d6db4e0e --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php @@ -0,0 +1,40 @@ +key = $key; + $this->cert = $cert; + $this->ca = $ca; + $this->capath = $capath; + $this->cipher = $cipher; + } + + public function initialize(mysqli $connection): void + { + $connection->ssl_set($this->key, $this->cert, $this->ca, $this->capath, $this->cipher); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/Mysqli/Result.php b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Result.php new file mode 100644 index 000000000..11e00f0b2 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Result.php @@ -0,0 +1,187 @@ + + */ + private $columnNames = []; + + /** @var mixed[] */ + private $boundValues = []; + + /** + * @internal The result can be only instantiated by its driver connection or statement. + * + * @throws Exception + */ + public function __construct(mysqli_stmt $statement) + { + $this->statement = $statement; + + $meta = $statement->result_metadata(); + + if ($meta === false) { + return; + } + + $this->hasColumns = true; + + $fields = $meta->fetch_fields(); + assert(is_array($fields)); + + $this->columnNames = array_map(static function (stdClass $field): string { + return $field->name; + }, $fields); + + $meta->free(); + + // Store result of every execution which has it. Otherwise it will be impossible + // to execute a new statement in case if the previous one has non-fetched rows + // @link http://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html + $this->statement->store_result(); + + // Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql, + // it will have to allocate as much memory as it may be needed for the given column type + // (e.g. for a LONGBLOB column it's 4 gigabytes) + // @link https://bugs.php.net/bug.php?id=51386#1270673122 + // + // Make sure that the values are bound after each execution. Otherwise, if free() has been + // previously called on the result, the values are unbound making the statement unusable. + // + // It's also important that row values are bound after _each_ call to store_result(). Otherwise, + // if mysqli is compiled with libmysql, subsequently fetched string values will get truncated + // to the length of the ones fetched during the previous execution. + $this->boundValues = array_fill(0, count($this->columnNames), null); + + $refs = []; + foreach ($this->boundValues as &$value) { + $refs[] =& $value; + } + + if (! $this->statement->bind_result(...$refs)) { + throw StatementError::new($this->statement); + } + } + + /** + * {@inheritdoc} + */ + public function fetchNumeric() + { + $ret = $this->statement->fetch(); + + if ($ret === false) { + throw StatementError::new($this->statement); + } + + if ($ret === null) { + return false; + } + + $values = []; + + foreach ($this->boundValues as $v) { + $values[] = $v; + } + + return $values; + } + + /** + * {@inheritDoc} + */ + public function fetchAssociative() + { + $values = $this->fetchNumeric(); + + if ($values === false) { + return false; + } + + return array_combine($this->columnNames, $values); + } + + /** + * {@inheritdoc} + */ + public function fetchOne() + { + return FetchUtils::fetchOne($this); + } + + /** + * {@inheritdoc} + */ + public function fetchAllNumeric(): array + { + return FetchUtils::fetchAllNumeric($this); + } + + /** + * {@inheritdoc} + */ + public function fetchAllAssociative(): array + { + return FetchUtils::fetchAllAssociative($this); + } + + /** + * {@inheritdoc} + */ + public function fetchFirstColumn(): array + { + return FetchUtils::fetchFirstColumn($this); + } + + public function rowCount(): int + { + if ($this->hasColumns) { + return $this->statement->num_rows; + } + + return $this->statement->affected_rows; + } + + public function columnCount(): int + { + return $this->statement->field_count; + } + + public function free(): void + { + $this->statement->free_result(); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/Mysqli/Statement.php b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Statement.php new file mode 100644 index 000000000..9f802e112 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/Mysqli/Statement.php @@ -0,0 +1,227 @@ + 's', + ParameterType::STRING => 's', + ParameterType::BINARY => 's', + ParameterType::BOOLEAN => 'i', + ParameterType::NULL => 's', + ParameterType::INTEGER => 'i', + ParameterType::LARGE_OBJECT => 'b', + ]; + + /** @var mysqli */ + protected $_conn; + + /** @var mysqli_stmt */ + protected $_stmt; + + /** @var mixed[]|null */ + protected $_bindedValues; + + /** @var string */ + protected $types; + + /** + * Contains ref values for bindValue(). + * + * @var mixed[] + */ + protected $_values = []; + + /** + * @internal The statement can be only instantiated by its driver connection. + * + * @param string $prepareString + * + * @throws Exception + */ + public function __construct(mysqli $conn, $prepareString) + { + $this->_conn = $conn; + + $stmt = $conn->prepare($prepareString); + + if ($stmt === false) { + throw ConnectionError::new($this->_conn); + } + + $this->_stmt = $stmt; + + $paramCount = $this->_stmt->param_count; + if (0 >= $paramCount) { + return; + } + + $this->types = str_repeat('s', $paramCount); + $this->_bindedValues = array_fill(1, $paramCount, null); + } + + /** + * {@inheritdoc} + */ + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + { + assert(is_int($param)); + + if (! isset(self::$_paramTypeMap[$type])) { + throw UnknownParameterType::new($type); + } + + $this->_bindedValues[$param] =& $variable; + $this->types[$param - 1] = self::$_paramTypeMap[$type]; + + return true; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = ParameterType::STRING) + { + assert(is_int($param)); + + if (! isset(self::$_paramTypeMap[$type])) { + throw UnknownParameterType::new($type); + } + + $this->_values[$param] = $value; + $this->_bindedValues[$param] =& $this->_values[$param]; + $this->types[$param - 1] = self::$_paramTypeMap[$type]; + + return true; + } + + /** + * {@inheritdoc} + */ + public function execute($params = null): ResultInterface + { + if ($this->_bindedValues !== null) { + if ($params !== null) { + if (! $this->bindUntypedValues($params)) { + throw StatementError::new($this->_stmt); + } + } else { + $this->bindTypedParameters(); + } + } + + if (! $this->_stmt->execute()) { + throw StatementError::new($this->_stmt); + } + + return new Result($this->_stmt); + } + + /** + * Binds parameters with known types previously bound to the statement + * + * @throws Exception + */ + private function bindTypedParameters(): void + { + $streams = $values = []; + $types = $this->types; + + foreach ($this->_bindedValues as $parameter => $value) { + assert(is_int($parameter)); + + if (! isset($types[$parameter - 1])) { + $types[$parameter - 1] = static::$_paramTypeMap[ParameterType::STRING]; + } + + if ($types[$parameter - 1] === static::$_paramTypeMap[ParameterType::LARGE_OBJECT]) { + if (is_resource($value)) { + if (get_resource_type($value) !== 'stream') { + throw NonStreamResourceUsedAsLargeObject::new($parameter); + } + + $streams[$parameter] = $value; + $values[$parameter] = null; + continue; + } + + $types[$parameter - 1] = static::$_paramTypeMap[ParameterType::STRING]; + } + + $values[$parameter] = $value; + } + + if (! $this->_stmt->bind_param($types, ...$values)) { + throw StatementError::new($this->_stmt); + } + + $this->sendLongData($streams); + } + + /** + * Handle $this->_longData after regular query parameters have been bound + * + * @param array $streams + * + * @throws Exception + */ + private function sendLongData(array $streams): void + { + foreach ($streams as $paramNr => $stream) { + while (! feof($stream)) { + $chunk = fread($stream, 8192); + + if ($chunk === false) { + throw FailedReadingStreamOffset::new($paramNr); + } + + if (! $this->_stmt->send_long_data($paramNr - 1, $chunk)) { + throw StatementError::new($this->_stmt); + } + } + } + } + + /** + * Binds a array of values to bound parameters. + * + * @param mixed[] $values + * + * @return bool + */ + private function bindUntypedValues(array $values) + { + $params = []; + $types = str_repeat('s', count($values)); + + foreach ($values as &$v) { + $params[] =& $v; + } + + return $this->_stmt->bind_param($types, ...$params); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/OCI8/Connection.php b/app/vendor/doctrine/dbal/src/Driver/OCI8/Connection.php new file mode 100644 index 000000000..1bc5c62ce --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/OCI8/Connection.php @@ -0,0 +1,174 @@ +dbh = $dbh; + $this->executionMode = new ExecutionMode(); + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + $version = oci_server_version($this->dbh); + + if ($version === false) { + throw Error::new($this->dbh); + } + + assert(preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', $version, $matches) === 1); + + return $matches[1]; + } + + public function prepare(string $sql): DriverStatement + { + return new Statement($this->dbh, $sql, $this->executionMode); + } + + public function query(string $sql): ResultInterface + { + return $this->prepare($sql)->execute(); + } + + /** + * {@inheritdoc} + */ + public function quote($value, $type = ParameterType::STRING) + { + if (is_int($value) || is_float($value)) { + return $value; + } + + $value = str_replace("'", "''", $value); + + return "'" . addcslashes($value, "\000\n\r\\\032") . "'"; + } + + public function exec(string $sql): int + { + return $this->prepare($sql)->execute()->rowCount(); + } + + /** + * {@inheritdoc} + * + * @param string|null $name + * + * @return int|false + */ + public function lastInsertId($name = null) + { + if ($name === null) { + return false; + } + + $result = $this->query('SELECT ' . $name . '.CURRVAL FROM DUAL')->fetchOne(); + + if ($result === false) { + throw SequenceDoesNotExist::new(); + } + + return (int) $result; + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + $this->executionMode->disableAutoCommit(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + if (! oci_commit($this->dbh)) { + throw Error::new($this->dbh); + } + + $this->executionMode->enableAutoCommit(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function rollBack() + { + if (! oci_rollback($this->dbh)) { + throw Error::new($this->dbh); + } + + $this->executionMode->enableAutoCommit(); + + return true; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/OCI8/ConvertPositionalToNamedPlaceholders.php b/app/vendor/doctrine/dbal/src/Driver/OCI8/ConvertPositionalToNamedPlaceholders.php new file mode 100644 index 000000000..483879d47 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/OCI8/ConvertPositionalToNamedPlaceholders.php @@ -0,0 +1,58 @@ +). + * + * Oracle does not support positional parameters, hence this method converts all + * positional parameters into artificially named parameters. + * + * @internal This class is not covered by the backward compatibility promise + */ +final class ConvertPositionalToNamedPlaceholders implements Visitor +{ + /** @var list */ + private $buffer = []; + + /** @var array */ + private $parameterMap = []; + + public function acceptOther(string $sql): void + { + $this->buffer[] = $sql; + } + + public function acceptPositionalParameter(string $sql): void + { + $position = count($this->parameterMap) + 1; + $param = ':param' . $position; + + $this->parameterMap[$position] = $param; + + $this->buffer[] = $param; + } + + public function acceptNamedParameter(string $sql): void + { + $this->buffer[] = $sql; + } + + public function getSQL(): string + { + return implode('', $this->buffer); + } + + /** + * @return array + */ + public function getParameterMap(): array + { + return $this->parameterMap; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/OCI8/Driver.php b/app/vendor/doctrine/dbal/src/Driver/OCI8/Driver.php new file mode 100644 index 000000000..351ffb87a --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/OCI8/Driver.php @@ -0,0 +1,42 @@ +_constructDsn($params), + $params['charset'] ?? '', + $params['sessionMode'] ?? OCI_NO_AUTO_COMMIT, + $params['persistent'] ?? false + ); + } + + /** + * Constructs the Oracle DSN. + * + * @param mixed[] $params + * + * @return string The DSN. + */ + protected function _constructDsn(array $params) + { + return $this->getEasyConnectString($params); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/OCI8/Exception/ConnectionFailed.php b/app/vendor/doctrine/dbal/src/Driver/OCI8/Exception/ConnectionFailed.php new file mode 100644 index 000000000..cefe9a3ad --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/OCI8/Exception/ConnectionFailed.php @@ -0,0 +1,26 @@ +isAutoCommitEnabled = true; + } + + public function disableAutoCommit(): void + { + $this->isAutoCommitEnabled = false; + } + + public function isAutoCommitEnabled(): bool + { + return $this->isAutoCommitEnabled; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/OCI8/Result.php b/app/vendor/doctrine/dbal/src/Driver/OCI8/Result.php new file mode 100644 index 000000000..8f77da759 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/OCI8/Result.php @@ -0,0 +1,147 @@ +statement = $statement; + } + + /** + * {@inheritDoc} + */ + public function fetchNumeric() + { + return $this->fetch(OCI_NUM); + } + + /** + * {@inheritDoc} + */ + public function fetchAssociative() + { + return $this->fetch(OCI_ASSOC); + } + + /** + * {@inheritDoc} + */ + public function fetchOne() + { + return FetchUtils::fetchOne($this); + } + + /** + * {@inheritDoc} + */ + public function fetchAllNumeric(): array + { + return $this->fetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_ROW); + } + + /** + * {@inheritDoc} + */ + public function fetchAllAssociative(): array + { + return $this->fetchAll(OCI_ASSOC, OCI_FETCHSTATEMENT_BY_ROW); + } + + /** + * {@inheritDoc} + */ + public function fetchFirstColumn(): array + { + return $this->fetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_COLUMN)[0]; + } + + public function rowCount(): int + { + $count = oci_num_rows($this->statement); + + if ($count !== false) { + return $count; + } + + return 0; + } + + public function columnCount(): int + { + $count = oci_num_fields($this->statement); + + if ($count !== false) { + return $count; + } + + return 0; + } + + public function free(): void + { + oci_cancel($this->statement); + } + + /** + * @return mixed|false + * + * @throws Exception + */ + private function fetch(int $mode) + { + $result = oci_fetch_array($this->statement, $mode | OCI_RETURN_NULLS | OCI_RETURN_LOBS); + + if ($result === false && oci_error($this->statement) !== false) { + throw Error::new($this->statement); + } + + return $result; + } + + /** + * @return array + */ + private function fetchAll(int $mode, int $fetchStructure): array + { + oci_fetch_all( + $this->statement, + $result, + 0, + -1, + $mode | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS + ); + + return $result; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/OCI8/Statement.php b/app/vendor/doctrine/dbal/src/Driver/OCI8/Statement.php new file mode 100644 index 000000000..1f3afda20 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/OCI8/Statement.php @@ -0,0 +1,165 @@ +parse($query, $visitor); + + $stmt = oci_parse($dbh, $visitor->getSQL()); + assert(is_resource($stmt)); + + $this->_sth = $stmt; + $this->_dbh = $dbh; + $this->_paramMap = $visitor->getParameterMap(); + $this->executionMode = $executionMode; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = ParameterType::STRING) + { + return $this->bindParam($param, $value, $type, null); + } + + /** + * {@inheritdoc} + */ + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + { + if (is_int($param)) { + if (! isset($this->_paramMap[$param])) { + throw UnknownParameterIndex::new($param); + } + + $param = $this->_paramMap[$param]; + } + + if ($type === ParameterType::LARGE_OBJECT) { + $lob = oci_new_descriptor($this->_dbh, OCI_D_LOB); + + assert($lob !== false); + + $lob->writeTemporary($variable, OCI_TEMP_BLOB); + + $variable =& $lob; + } + + $this->boundValues[$param] =& $variable; + + return oci_bind_by_name( + $this->_sth, + $param, + $variable, + $length ?? -1, + $this->convertParameterType($type) + ); + } + + /** + * Converts DBAL parameter type to oci8 parameter type + */ + private function convertParameterType(int $type): int + { + switch ($type) { + case ParameterType::BINARY: + return OCI_B_BIN; + + case ParameterType::LARGE_OBJECT: + return OCI_B_BLOB; + + default: + return SQLT_CHR; + } + } + + /** + * {@inheritdoc} + */ + public function execute($params = null): ResultInterface + { + if ($params !== null) { + foreach ($params as $key => $val) { + if (is_int($key)) { + $this->bindValue($key + 1, $val); + } else { + $this->bindValue($key, $val); + } + } + } + + if ($this->executionMode->isAutoCommitEnabled()) { + $mode = OCI_COMMIT_ON_SUCCESS; + } else { + $mode = OCI_NO_AUTO_COMMIT; + } + + $ret = @oci_execute($this->_sth, $mode); + if (! $ret) { + throw Error::new($this->_sth); + } + + return new Result($this->_sth); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/PDO/Connection.php b/app/vendor/doctrine/dbal/src/Driver/PDO/Connection.php new file mode 100644 index 000000000..169016937 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/PDO/Connection.php @@ -0,0 +1,151 @@ +connection = new PDO($dsn, (string) $user, (string) $password, (array) $options); + $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + } + + public function exec(string $sql): int + { + try { + $result = $this->connection->exec($sql); + + assert($result !== false); + + return $result; + } catch (PDOException $exception) { + throw Exception::new($exception); + } + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + return $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION); + } + + /** + * {@inheritDoc} + * + * @return Statement + */ + public function prepare(string $sql): StatementInterface + { + try { + $stmt = $this->connection->prepare($sql); + assert($stmt instanceof PDOStatement); + + return $this->createStatement($stmt); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + } + + public function query(string $sql): ResultInterface + { + try { + $stmt = $this->connection->query($sql); + assert($stmt instanceof PDOStatement); + + return new Result($stmt); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + } + + /** + * {@inheritdoc} + */ + public function quote($value, $type = ParameterType::STRING) + { + return $this->connection->quote($value, $type); + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + try { + if ($name === null) { + return $this->connection->lastInsertId(); + } + + return $this->connection->lastInsertId($name); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + } + + /** + * Creates a wrapped statement + */ + protected function createStatement(PDOStatement $stmt): Statement + { + return new Statement($stmt); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + return $this->connection->beginTransaction(); + } + + /** + * {@inheritDoc} + */ + public function commit() + { + return $this->connection->commit(); + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + return $this->connection->rollBack(); + } + + public function getWrappedConnection(): PDO + { + return $this->connection; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/PDO/Exception.php b/app/vendor/doctrine/dbal/src/Driver/PDO/Exception.php new file mode 100644 index 000000000..49f55951d --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/PDO/Exception.php @@ -0,0 +1,28 @@ +errorInfo !== null) { + [$sqlState, $code] = $exception->errorInfo; + } else { + $code = $exception->getCode(); + $sqlState = null; + } + + return new self($exception->getMessage(), $sqlState, $code, $exception); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php b/app/vendor/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php new file mode 100644 index 000000000..50c05a40f --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php @@ -0,0 +1,64 @@ +constructPdoDsn($params), + $params['user'] ?? '', + $params['password'] ?? '', + $driverOptions + ); + } + + /** + * Constructs the MySQL PDO DSN. + * + * @param mixed[] $params + * + * @return string The DSN. + */ + protected function constructPdoDsn(array $params) + { + $dsn = 'mysql:'; + if (isset($params['host']) && $params['host'] !== '') { + $dsn .= 'host=' . $params['host'] . ';'; + } + + if (isset($params['port'])) { + $dsn .= 'port=' . $params['port'] . ';'; + } + + if (isset($params['dbname'])) { + $dsn .= 'dbname=' . $params['dbname'] . ';'; + } + + if (isset($params['unix_socket'])) { + $dsn .= 'unix_socket=' . $params['unix_socket'] . ';'; + } + + if (isset($params['charset'])) { + $dsn .= 'charset=' . $params['charset'] . ';'; + } + + return $dsn; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/PDO/OCI/Driver.php b/app/vendor/doctrine/dbal/src/Driver/PDO/OCI/Driver.php new file mode 100644 index 000000000..a2c1f4652 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/PDO/OCI/Driver.php @@ -0,0 +1,49 @@ +constructPdoDsn($params), + $params['user'] ?? '', + $params['password'] ?? '', + $driverOptions + ); + } + + /** + * Constructs the Oracle PDO DSN. + * + * @param mixed[] $params + * + * @return string The DSN. + */ + private function constructPdoDsn(array $params) + { + $dsn = 'oci:dbname=' . $this->getEasyConnectString($params); + + if (isset($params['charset'])) { + $dsn .= ';charset=' . $params['charset']; + } + + return $dsn; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php b/app/vendor/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php new file mode 100644 index 000000000..e9620bde7 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php @@ -0,0 +1,109 @@ +_constructPdoDsn($params), + $params['user'] ?? '', + $params['password'] ?? '', + $driverOptions, + ); + + if ( + defined('PDO::PGSQL_ATTR_DISABLE_PREPARES') + && (! isset($driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES]) + || $driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES] === true + ) + ) { + $connection->getWrappedConnection()->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true); + } + + /* defining client_encoding via SET NAMES to avoid inconsistent DSN support + * - the 'client_encoding' connection param only works with postgres >= 9.1 + * - passing client_encoding via the 'options' param breaks pgbouncer support + */ + if (isset($params['charset'])) { + $connection->exec('SET NAMES \'' . $params['charset'] . '\''); + } + + return $connection; + } + + /** + * Constructs the Postgres PDO DSN. + * + * @param mixed[] $params + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'pgsql:'; + + if (isset($params['host']) && $params['host'] !== '') { + $dsn .= 'host=' . $params['host'] . ';'; + } + + if (isset($params['port']) && $params['port'] !== '') { + $dsn .= 'port=' . $params['port'] . ';'; + } + + if (isset($params['dbname'])) { + $dsn .= 'dbname=' . $params['dbname'] . ';'; + } elseif (isset($params['default_dbname'])) { + $dsn .= 'dbname=' . $params['default_dbname'] . ';'; + } else { + // Used for temporary connections to allow operations like dropping the database currently connected to. + // Connecting without an explicit database does not work, therefore "postgres" database is used + // as it is mostly present in every server setup. + $dsn .= 'dbname=postgres;'; + } + + if (isset($params['sslmode'])) { + $dsn .= 'sslmode=' . $params['sslmode'] . ';'; + } + + if (isset($params['sslrootcert'])) { + $dsn .= 'sslrootcert=' . $params['sslrootcert'] . ';'; + } + + if (isset($params['sslcert'])) { + $dsn .= 'sslcert=' . $params['sslcert'] . ';'; + } + + if (isset($params['sslkey'])) { + $dsn .= 'sslkey=' . $params['sslkey'] . ';'; + } + + if (isset($params['sslcrl'])) { + $dsn .= 'sslcrl=' . $params['sslcrl'] . ';'; + } + + if (isset($params['application_name'])) { + $dsn .= 'application_name=' . $params['application_name'] . ';'; + } + + return $dsn; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/PDO/Result.php b/app/vendor/doctrine/dbal/src/Driver/PDO/Result.php new file mode 100644 index 000000000..888716d81 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/PDO/Result.php @@ -0,0 +1,130 @@ +statement = $statement; + } + + /** + * {@inheritDoc} + */ + public function fetchNumeric() + { + return $this->fetch(PDO::FETCH_NUM); + } + + /** + * {@inheritDoc} + */ + public function fetchAssociative() + { + return $this->fetch(PDO::FETCH_ASSOC); + } + + /** + * {@inheritDoc} + */ + public function fetchOne() + { + return $this->fetch(PDO::FETCH_COLUMN); + } + + /** + * {@inheritDoc} + */ + public function fetchAllNumeric(): array + { + return $this->fetchAll(PDO::FETCH_NUM); + } + + /** + * {@inheritDoc} + */ + public function fetchAllAssociative(): array + { + return $this->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * {@inheritDoc} + */ + public function fetchFirstColumn(): array + { + return $this->fetchAll(PDO::FETCH_COLUMN); + } + + public function rowCount(): int + { + try { + return $this->statement->rowCount(); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + } + + public function columnCount(): int + { + try { + return $this->statement->columnCount(); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + } + + public function free(): void + { + $this->statement->closeCursor(); + } + + /** + * @return mixed|false + * + * @throws Exception + */ + private function fetch(int $mode) + { + try { + return $this->statement->fetch($mode); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + } + + /** + * @return list + * + * @throws Exception + */ + private function fetchAll(int $mode): array + { + try { + $data = $this->statement->fetchAll($mode); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + + assert(is_array($data)); + + return $data; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php b/app/vendor/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php new file mode 100644 index 000000000..a64f4245a --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php @@ -0,0 +1,97 @@ +connection = $connection; + } + + public function prepare(string $sql): StatementInterface + { + return new Statement( + $this->connection->prepare($sql) + ); + } + + public function query(string $sql): Result + { + return $this->connection->query($sql); + } + + /** + * {@inheritDoc} + */ + public function quote($value, $type = ParameterType::STRING) + { + return $this->connection->quote($value, $type); + } + + public function exec(string $sql): int + { + return $this->connection->exec($sql); + } + + /** + * {@inheritDoc} + */ + public function lastInsertId($name = null) + { + if ($name === null) { + return $this->connection->lastInsertId($name); + } + + return $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?') + ->execute([$name]) + ->fetchOne(); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + return $this->connection->beginTransaction(); + } + + /** + * {@inheritDoc} + */ + public function commit() + { + return $this->connection->commit(); + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + return $this->connection->rollBack(); + } + + /** + * {@inheritDoc} + */ + public function getServerVersion() + { + return $this->connection->getServerVersion(); + } + + public function getWrappedConnection(): PDO + { + return $this->connection->getWrappedConnection(); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/PDO/SQLSrv/Driver.php b/app/vendor/doctrine/dbal/src/Driver/PDO/SQLSrv/Driver.php new file mode 100644 index 000000000..c48425049 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/PDO/SQLSrv/Driver.php @@ -0,0 +1,99 @@ + $value) { + if (is_int($option)) { + $pdoOptions[$option] = $value; + } else { + $dsnOptions[$option] = $value; + } + } + } + + if (! empty($params['persistent'])) { + $pdoOptions[PDO::ATTR_PERSISTENT] = true; + } + + return new Connection( + new PDOConnection( + $this->_constructPdoDsn($params, $dsnOptions), + $params['user'] ?? '', + $params['password'] ?? '', + $pdoOptions + ) + ); + } + + /** + * Constructs the Sqlsrv PDO DSN. + * + * @param mixed[] $params + * @param string[] $connectionOptions + * + * @return string The DSN. + * + * @throws Exception + */ + private function _constructPdoDsn(array $params, array $connectionOptions) + { + $dsn = 'sqlsrv:server='; + + if (isset($params['host'])) { + $dsn .= $params['host']; + + if (isset($params['port'])) { + $dsn .= ',' . $params['port']; + } + } elseif (isset($params['port'])) { + throw PortWithoutHost::new(); + } + + if (isset($params['dbname'])) { + $connectionOptions['Database'] = $params['dbname']; + } + + if (isset($params['MultipleActiveResultSets'])) { + $connectionOptions['MultipleActiveResultSets'] = $params['MultipleActiveResultSets'] ? 'true' : 'false'; + } + + return $dsn . $this->getConnectionOptionsDsn($connectionOptions); + } + + /** + * Converts a connection options array to the DSN + * + * @param string[] $connectionOptions + */ + private function getConnectionOptionsDsn(array $connectionOptions): string + { + $connectionOptionsDsn = ''; + + foreach ($connectionOptions as $paramName => $paramValue) { + $connectionOptionsDsn .= sprintf(';%s=%s', $paramName, $paramValue); + } + + return $connectionOptionsDsn; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php b/app/vendor/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php new file mode 100644 index 000000000..d2da1625c --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/PDO/SQLSrv/Statement.php @@ -0,0 +1,80 @@ +statement = $statement; + } + + /** + * {@inheritdoc} + * + * @param string|int $param + * @param mixed $variable + * @param int $type + * @param int|null $length + * @param mixed $driverOptions The usage of the argument is deprecated. + */ + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null) + { + if (func_num_args() > 4) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4533', + 'The $driverOptions argument of Statement::bindParam() is deprecated.' + ); + } + + switch ($type) { + case ParameterType::LARGE_OBJECT: + case ParameterType::BINARY: + if ($driverOptions === null) { + $driverOptions = PDO::SQLSRV_ENCODING_BINARY; + } + + break; + + case ParameterType::ASCII: + $type = ParameterType::STRING; + $length = 0; + $driverOptions = PDO::SQLSRV_ENCODING_SYSTEM; + break; + } + + return $this->statement->bindParam($param, $variable, $type, $length, $driverOptions); + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = ParameterType::STRING) + { + return $this->bindParam($param, $value, $type); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null): Result + { + return $this->statement->execute($params); + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php b/app/vendor/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php new file mode 100644 index 000000000..d85ba28fa --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php @@ -0,0 +1,71 @@ + ['callback' => [SqlitePlatform::class, 'udfSqrt'], 'numArgs' => 1], + 'mod' => ['callback' => [SqlitePlatform::class, 'udfMod'], 'numArgs' => 2], + 'locate' => ['callback' => [SqlitePlatform::class, 'udfLocate'], 'numArgs' => -1], + ]; + + /** + * {@inheritdoc} + * + * @return Connection + */ + public function connect(array $params) + { + $driverOptions = $params['driverOptions'] ?? []; + + if (isset($driverOptions['userDefinedFunctions'])) { + $this->_userDefinedFunctions = array_merge( + $this->_userDefinedFunctions, + $driverOptions['userDefinedFunctions'] + ); + unset($driverOptions['userDefinedFunctions']); + } + + $connection = new Connection( + $this->_constructPdoDsn($params), + $params['user'] ?? '', + $params['password'] ?? '', + $driverOptions + ); + + $pdo = $connection->getWrappedConnection(); + + foreach ($this->_userDefinedFunctions as $fn => $data) { + $pdo->sqliteCreateFunction($fn, $data['callback'], $data['numArgs']); + } + + return $connection; + } + + /** + * Constructs the Sqlite PDO DSN. + * + * @param mixed[] $params + * + * @return string The DSN. + */ + protected function _constructPdoDsn(array $params) + { + $dsn = 'sqlite:'; + if (isset($params['path'])) { + $dsn .= $params['path']; + } elseif (isset($params['memory'])) { + $dsn .= ':memory:'; + } + + return $dsn; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/PDO/Statement.php b/app/vendor/doctrine/dbal/src/Driver/PDO/Statement.php new file mode 100644 index 000000000..1461239e6 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/PDO/Statement.php @@ -0,0 +1,115 @@ + PDO::PARAM_NULL, + ParameterType::INTEGER => PDO::PARAM_INT, + ParameterType::STRING => PDO::PARAM_STR, + ParameterType::ASCII => PDO::PARAM_STR, + ParameterType::BINARY => PDO::PARAM_LOB, + ParameterType::LARGE_OBJECT => PDO::PARAM_LOB, + ParameterType::BOOLEAN => PDO::PARAM_BOOL, + ]; + + /** @var PDOStatement */ + private $stmt; + + /** + * @internal The statement can be only instantiated by its driver connection. + */ + public function __construct(PDOStatement $stmt) + { + $this->stmt = $stmt; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = ParameterType::STRING) + { + $type = $this->convertParamType($type); + + try { + return $this->stmt->bindValue($param, $value, $type); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + } + + /** + * {@inheritDoc} + * + * @param mixed $param + * @param mixed $variable + * @param int $type + * @param int|null $length + * @param mixed $driverOptions The usage of the argument is deprecated. + * + * @return bool + */ + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null) + { + if (func_num_args() > 4) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4533', + 'The $driverOptions argument of Statement::bindParam() is deprecated.' + ); + } + + $type = $this->convertParamType($type); + + try { + return $this->stmt->bindParam($param, $variable, $type, ...array_slice(func_get_args(), 3)); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + } + + /** + * {@inheritdoc} + */ + public function execute($params = null): ResultInterface + { + try { + $this->stmt->execute($params); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + + return new Result($this->stmt); + } + + /** + * Converts DBAL parameter type to PDO parameter type + * + * @param int $type Parameter type + * + * @throws ExceptionInterface + */ + private function convertParamType(int $type): int + { + if (! isset(self::PARAM_TYPE_MAP[$type])) { + throw UnknownParameterType::new($type); + } + + return self::PARAM_TYPE_MAP[$type]; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/Result.php b/app/vendor/doctrine/dbal/src/Driver/Result.php new file mode 100644 index 000000000..7843a9589 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/Result.php @@ -0,0 +1,93 @@ +|false + * + * @throws Exception + */ + public function fetchNumeric(); + + /** + * Returns the next row of the result as an associative array or FALSE if there are no more rows. + * + * @return array|false + * + * @throws Exception + */ + public function fetchAssociative(); + + /** + * Returns the first value of the next row of the result or FALSE if there are no more rows. + * + * @return mixed|false + * + * @throws Exception + */ + public function fetchOne(); + + /** + * Returns an array containing all of the result rows represented as numeric arrays. + * + * @return list> + * + * @throws Exception + */ + public function fetchAllNumeric(): array; + + /** + * Returns an array containing all of the result rows represented as associative arrays. + * + * @return list> + * + * @throws Exception + */ + public function fetchAllAssociative(): array; + + /** + * Returns an array containing the values of the first column of the result. + * + * @return list + * + * @throws Exception + */ + public function fetchFirstColumn(): array; + + /** + * Returns the number of rows affected by the DELETE, INSERT, or UPDATE statement that produced the result. + * + * If the statement executed a SELECT query or a similar platform-specific SQL (e.g. DESCRIBE, SHOW, etc.), + * some database drivers may return the number of rows returned by that query. However, this behaviour + * is not guaranteed for all drivers and should not be relied on in portable applications. + * + * @return int The number of rows. + * + * @throws Exception + */ + public function rowCount(): int; + + /** + * Returns the number of columns in the result + * + * @return int The number of columns in the result. If the columns cannot be counted, + * this method must return 0. + * + * @throws Exception + */ + public function columnCount(): int; + + /** + * Discards the non-fetched portion of the result, enabling the originating statement to be executed again. + */ + public function free(): void; +} diff --git a/app/vendor/doctrine/dbal/src/Driver/SQLSrv/Connection.php b/app/vendor/doctrine/dbal/src/Driver/SQLSrv/Connection.php new file mode 100644 index 000000000..e37ea9006 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/SQLSrv/Connection.php @@ -0,0 +1,156 @@ +conn = $conn; + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + $serverInfo = sqlsrv_server_info($this->conn); + + return $serverInfo['SQLServerVersion']; + } + + public function prepare(string $sql): DriverStatement + { + return new Statement($this->conn, $sql); + } + + public function query(string $sql): ResultInterface + { + return $this->prepare($sql)->execute(); + } + + /** + * {@inheritDoc} + */ + public function quote($value, $type = ParameterType::STRING) + { + if (is_int($value)) { + return $value; + } + + if (is_float($value)) { + return sprintf('%F', $value); + } + + return "'" . str_replace("'", "''", $value) . "'"; + } + + public function exec(string $sql): int + { + $stmt = sqlsrv_query($this->conn, $sql); + + if ($stmt === false) { + throw Error::new(); + } + + $rowsAffected = sqlsrv_rows_affected($stmt); + + if ($rowsAffected === false) { + throw Error::new(); + } + + return $rowsAffected; + } + + /** + * {@inheritDoc} + */ + public function lastInsertId($name = null) + { + if ($name !== null) { + $result = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?') + ->execute([$name]); + } else { + $result = $this->query('SELECT @@IDENTITY'); + } + + return $result->fetchOne(); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + if (! sqlsrv_begin_transaction($this->conn)) { + throw Error::new(); + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function commit() + { + if (! sqlsrv_commit($this->conn)) { + throw Error::new(); + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + if (! sqlsrv_rollback($this->conn)) { + throw Error::new(); + } + + return true; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/SQLSrv/Driver.php b/app/vendor/doctrine/dbal/src/Driver/SQLSrv/Driver.php new file mode 100644 index 000000000..efa9ccfe9 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/SQLSrv/Driver.php @@ -0,0 +1,56 @@ +statement = $stmt; + } + + /** + * {@inheritDoc} + */ + public function fetchNumeric() + { + return $this->fetch(SQLSRV_FETCH_NUMERIC); + } + + /** + * {@inheritDoc} + */ + public function fetchAssociative() + { + return $this->fetch(SQLSRV_FETCH_ASSOC); + } + + /** + * {@inheritDoc} + */ + public function fetchOne() + { + return FetchUtils::fetchOne($this); + } + + /** + * {@inheritDoc} + */ + public function fetchAllNumeric(): array + { + return FetchUtils::fetchAllNumeric($this); + } + + /** + * {@inheritDoc} + */ + public function fetchAllAssociative(): array + { + return FetchUtils::fetchAllAssociative($this); + } + + /** + * {@inheritDoc} + */ + public function fetchFirstColumn(): array + { + return FetchUtils::fetchFirstColumn($this); + } + + public function rowCount(): int + { + $count = sqlsrv_rows_affected($this->statement); + + if ($count !== false) { + return $count; + } + + return 0; + } + + public function columnCount(): int + { + $count = sqlsrv_num_fields($this->statement); + + if ($count !== false) { + return $count; + } + + return 0; + } + + public function free(): void + { + // emulate it by fetching and discarding rows, similarly to what PDO does in this case + // @link http://php.net/manual/en/pdostatement.closecursor.php + // @link https://github.com/php/php-src/blob/php-7.0.11/ext/pdo/pdo_stmt.c#L2075 + // deliberately do not consider multiple result sets, since doctrine/dbal doesn't support them + while (sqlsrv_fetch($this->statement)) { + } + } + + /** + * @return mixed|false + */ + private function fetch(int $fetchType) + { + return sqlsrv_fetch_array($this->statement, $fetchType) ?? false; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/SQLSrv/Statement.php b/app/vendor/doctrine/dbal/src/Driver/SQLSrv/Statement.php new file mode 100644 index 000000000..8267bc0de --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/SQLSrv/Statement.php @@ -0,0 +1,191 @@ +conn = $conn; + $this->sql = $sql; + + if (stripos($sql, 'INSERT INTO ') !== 0) { + return; + } + + $this->sql .= self::LAST_INSERT_ID_SQL; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = ParameterType::STRING) + { + assert(is_int($param)); + + $this->variables[$param] = $value; + $this->types[$param] = $type; + + return true; + } + + /** + * {@inheritdoc} + */ + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + { + assert(is_int($param)); + + $this->variables[$param] =& $variable; + $this->types[$param] = $type; + + // unset the statement resource if it exists as the new one will need to be bound to the new variable + $this->stmt = null; + + return true; + } + + /** + * {@inheritdoc} + */ + public function execute($params = null): ResultInterface + { + if ($params !== null) { + foreach ($params as $key => $val) { + if (is_int($key)) { + $this->bindValue($key + 1, $val); + } else { + $this->bindValue($key, $val); + } + } + } + + if ($this->stmt === null) { + $this->stmt = $this->prepare(); + } + + if (! sqlsrv_execute($this->stmt)) { + throw Error::new(); + } + + return new Result($this->stmt); + } + + /** + * Prepares SQL Server statement resource + * + * @return resource + * + * @throws Exception + */ + private function prepare() + { + $params = []; + + foreach ($this->variables as $column => &$variable) { + switch ($this->types[$column]) { + case ParameterType::LARGE_OBJECT: + $params[$column - 1] = [ + &$variable, + SQLSRV_PARAM_IN, + SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY), + SQLSRV_SQLTYPE_VARBINARY('max'), + ]; + break; + + case ParameterType::BINARY: + $params[$column - 1] = [ + &$variable, + SQLSRV_PARAM_IN, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY), + ]; + break; + + case ParameterType::ASCII: + $params[$column - 1] = [ + &$variable, + SQLSRV_PARAM_IN, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), + ]; + break; + + default: + $params[$column - 1] =& $variable; + break; + } + } + + $stmt = sqlsrv_prepare($this->conn, $this->sql, $params); + + if ($stmt === false) { + throw Error::new(); + } + + return $stmt; + } +} diff --git a/app/vendor/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php b/app/vendor/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php new file mode 100644 index 000000000..622f98c6c --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php @@ -0,0 +1,18 @@ +execute() is called. + * + * As mentioned above, the named parameters are not natively supported by the mysqli driver, use executeQuery(), + * fetchAll(), fetchArray(), fetchColumn(), fetchAssoc() methods to have the named parameter emulated by doctrine. + * + * Most parameters are input parameters, that is, parameters that are + * used in a read-only fashion to build up the query. Some drivers support the invocation + * of stored procedures that return data as output parameters, and some also as input/output + * parameters that both send in data and are updated to receive it. + * + * @param string|int $param Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement using + * question mark placeholders, this will be the 1-indexed position of the parameter. + * @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter. + * @param int $type Explicit data type for the parameter using the {@link ParameterType} + * constants. + * @param int|null $length You must specify maxlength when using an OUT bind + * so that PHP allocates enough memory to hold the returned value. + * + * @return bool TRUE on success or FALSE on failure. + * + * @throws Exception + */ + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null); + + /** + * Executes a prepared statement + * + * If the prepared statement included parameter markers, you must either: + * call {@link bindParam()} to bind PHP variables to the parameter markers: + * bound variables pass their value as input and receive the output value, + * if any, of their associated parameter markers or pass an array of input-only + * parameter values. + * + * @param mixed[]|null $params A numeric array of values with as many elements as there are + * bound parameters in the SQL statement being executed. + * + * @throws Exception + */ + public function execute($params = null): Result; +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php b/app/vendor/doctrine/dbal/src/DriverManager.php similarity index 87% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php rename to app/vendor/doctrine/dbal/src/DriverManager.php index 2ef49c498..58a0cc579 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php +++ b/app/vendor/doctrine/dbal/src/DriverManager.php @@ -3,12 +3,10 @@ namespace Doctrine\DBAL; use Doctrine\Common\EventManager; -use Doctrine\DBAL\Driver\DrizzlePDOMySql; use Doctrine\DBAL\Driver\IBMDB2; use Doctrine\DBAL\Driver\Mysqli; use Doctrine\DBAL\Driver\OCI8; use Doctrine\DBAL\Driver\PDO; -use Doctrine\DBAL\Driver\SQLAnywhere; use Doctrine\DBAL\Driver\SQLSrv; use function array_keys; @@ -85,8 +83,6 @@ final class DriverManager 'ibm_db2' => IBMDB2\Driver::class, 'pdo_sqlsrv' => PDO\SQLSrv\Driver::class, 'mysqli' => Mysqli\Driver::class, - 'drizzle_pdo_mysql' => DrizzlePDOMySql\Driver::class, - 'sqlanywhere' => SQLAnywhere\Driver::class, 'sqlsrv' => SQLSrv\Driver::class, ]; @@ -139,11 +135,6 @@ private function __construct() * Any additional driver-specific options for the driver. These are just passed * through to the driver. * - * pdo: - * You can pass an existing PDO instance through this parameter. The PDO - * instance will be wrapped in a Doctrine\DBAL\Connection. - * This feature is deprecated and no longer supported in 3.0.x version. - * * wrapperClass: * You may specify a custom wrapper class through the 'wrapperClass' * parameter but this class MUST inherit from Doctrine\DBAL\Connection. @@ -192,28 +183,16 @@ public static function getConnection( ?EventManager $eventManager = null ): Connection { // create default config and event manager, if not set - if (! $config) { + if ($config === null) { $config = new Configuration(); } - if (! $eventManager) { + if ($eventManager === null) { $eventManager = new EventManager(); } $params = self::parseDatabaseUrl($params); - // @todo: deprecated, notice thrown by connection constructor - if (isset($params['master'])) { - $params['master'] = self::parseDatabaseUrl($params['master']); - } - - // @todo: deprecated, notice thrown by connection constructor - if (isset($params['slaves'])) { - foreach ($params['slaves'] as $key => $slaveParams) { - $params['slaves'][$key] = self::parseDatabaseUrl($slaveParams); - } - } - // URL support for PrimaryReplicaConnection if (isset($params['primary'])) { $params['primary'] = self::parseDatabaseUrl($params['primary']); @@ -225,29 +204,12 @@ public static function getConnection( } } - // URL support for PoolingShardConnection - if (isset($params['global'])) { - $params['global'] = self::parseDatabaseUrl($params['global']); - } - - if (isset($params['shards'])) { - foreach ($params['shards'] as $key => $shardParams) { - $params['shards'][$key] = self::parseDatabaseUrl($shardParams); - } - } - - // check for existing pdo object - if (isset($params['pdo']) && ! $params['pdo'] instanceof \PDO) { - throw Exception::invalidPdoInstance(); - } + $driver = self::createDriver($params); - if (isset($params['pdo'])) { - $params['pdo']->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - $params['driver'] = 'pdo_' . $params['pdo']->getAttribute(\PDO::ATTR_DRIVER_NAME); + foreach ($config->getMiddlewares() as $middleware) { + $driver = $middleware->wrap($driver); } - $driver = self::createDriver($params); - $wrapperClass = Connection::class; if (isset($params['wrapperClass'])) { if (! is_subclass_of($params['wrapperClass'], $wrapperClass)) { @@ -281,9 +243,9 @@ public static function getAvailableDrivers(): array private static function createDriver(array $params): Driver { if (isset($params['driverClass'])) { - $interfaces = class_implements($params['driverClass'], true); + $interfaces = class_implements($params['driverClass']); - if ($interfaces === false || ! in_array(Driver::class, $interfaces)) { + if ($interfaces === false || ! in_array(Driver::class, $interfaces, true)) { throw Exception::invalidDriverClass($params['driverClass']); } @@ -353,10 +315,6 @@ private static function parseDatabaseUrl(array $params): array $url[$param] = rawurldecode($value); } - // If we have a connection URL, we have to unset the default PDO instance connection parameter (if any) - // as we cannot merge connection details from the URL into the PDO instance (URL takes precedence). - unset($params['pdo']); - $params = self::parseDatabaseUrlScheme($url['scheme'] ?? null, $params); if (isset($url['host'])) { @@ -490,7 +448,7 @@ private static function parseSqliteDatabaseUrlPath(array $url, array $params): a * * @throws Exception If parsing failed or resolution is not possible. */ - private static function parseDatabaseUrlScheme($scheme, array $params): array + private static function parseDatabaseUrlScheme(?string $scheme, array $params): array { if ($scheme !== null) { // The requested driver from the URL scheme takes precedence diff --git a/app/vendor/doctrine/dbal/src/Event/ConnectionEventArgs.php b/app/vendor/doctrine/dbal/src/Event/ConnectionEventArgs.php new file mode 100644 index 000000000..eeddb91a5 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Event/ConnectionEventArgs.php @@ -0,0 +1,28 @@ +connection = $connection; + } + + /** + * @return Connection + */ + public function getConnection() + { + return $this->connection; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php b/app/vendor/doctrine/dbal/src/Event/Listeners/OracleSessionInit.php similarity index 94% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php rename to app/vendor/doctrine/dbal/src/Event/Listeners/OracleSessionInit.php index a907716f8..d6d1fd9d1 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php +++ b/app/vendor/doctrine/dbal/src/Event/Listeners/OracleSessionInit.php @@ -5,6 +5,7 @@ use Doctrine\Common\EventSubscriber; use Doctrine\DBAL\Event\ConnectionEventArgs; use Doctrine\DBAL\Events; +use Doctrine\DBAL\Exception; use function array_change_key_case; use function array_merge; @@ -44,10 +45,12 @@ public function __construct(array $oracleSessionVars = []) /** * @return void + * + * @throws Exception */ public function postConnect(ConnectionEventArgs $args) { - if (! count($this->_defaultSessionVars)) { + if (count($this->_defaultSessionVars) === 0) { return; } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php b/app/vendor/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php similarity index 85% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php rename to app/vendor/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php index ea63cab43..f7a4e9129 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php +++ b/app/vendor/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php @@ -5,6 +5,7 @@ use Doctrine\Common\EventSubscriber; use Doctrine\DBAL\Event\ConnectionEventArgs; use Doctrine\DBAL\Events; +use Doctrine\DBAL\Exception; /** * Session init listener for executing a single SQL statement right after a connection is opened. @@ -24,11 +25,12 @@ public function __construct($sql) /** * @return void + * + * @throws Exception */ public function postConnect(ConnectionEventArgs $args) { - $conn = $args->getConnection(); - $conn->exec($this->sql); + $args->getConnection()->executeStatement($this->sql); } /** diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php b/app/vendor/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php rename to app/vendor/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php b/app/vendor/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php rename to app/vendor/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php b/app/vendor/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php rename to app/vendor/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php b/app/vendor/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php rename to app/vendor/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php b/app/vendor/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php rename to app/vendor/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php diff --git a/app/vendor/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php b/app/vendor/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php new file mode 100644 index 000000000..58efa7b50 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php @@ -0,0 +1,97 @@ +tableColumn = $tableColumn; + $this->table = $table; + $this->database = $database; + $this->connection = $connection; + } + + /** + * Allows to clear the column which means the column will be excluded from + * tables column list. + * + * @return SchemaColumnDefinitionEventArgs + */ + public function setColumn(?Column $column = null) + { + $this->column = $column; + + return $this; + } + + /** + * @return Column|null + */ + public function getColumn() + { + return $this->column; + } + + /** + * @return mixed[] + */ + public function getTableColumn() + { + return $this->tableColumn; + } + + /** + * @return string + */ + public function getTable() + { + return $this->table; + } + + /** + * @return string + */ + public function getDatabase() + { + return $this->database; + } + + /** + * @return Connection + */ + public function getConnection() + { + return $this->connection; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php b/app/vendor/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php rename to app/vendor/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php b/app/vendor/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php rename to app/vendor/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php b/app/vendor/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php rename to app/vendor/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaEventArgs.php b/app/vendor/doctrine/dbal/src/Event/SchemaEventArgs.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaEventArgs.php rename to app/vendor/doctrine/dbal/src/Event/SchemaEventArgs.php diff --git a/app/vendor/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php b/app/vendor/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php new file mode 100644 index 000000000..82c17a26a --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php @@ -0,0 +1,83 @@ +tableIndex = $tableIndex; + $this->table = $table; + $this->connection = $connection; + } + + /** + * Allows to clear the index which means the index will be excluded from tables index list. + * + * @return SchemaIndexDefinitionEventArgs + */ + public function setIndex(?Index $index = null) + { + $this->index = $index; + + return $this; + } + + /** + * @return Index|null + */ + public function getIndex() + { + return $this->index; + } + + /** + * @return mixed[] + */ + public function getTableIndex() + { + return $this->tableIndex; + } + + /** + * @return string + */ + public function getTable() + { + return $this->table; + } + + /** + * @return Connection + */ + public function getConnection() + { + return $this->connection; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php b/app/vendor/doctrine/dbal/src/Events.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php rename to app/vendor/doctrine/dbal/src/Events.php diff --git a/app/vendor/doctrine/dbal/src/Exception.php b/app/vendor/doctrine/dbal/src/Exception.php new file mode 100644 index 000000000..56c4cc21c --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Exception.php @@ -0,0 +1,152 @@ +getMessage(); + } else { + $message = 'An exception occurred in the driver: ' . $driverException->getMessage(); + } + + parent::__construct($message, $driverException->getCode(), $driverException); + + $this->query = $query; + } + + /** + * {@inheritDoc} + */ + public function getSQLState() + { + $previous = $this->getPrevious(); + assert($previous instanceof TheDriverException); + + return $previous->getSQLState(); + } + + public function getQuery(): ?Query + { + return $this->query; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ForeignKeyConstraintViolationException.php b/app/vendor/doctrine/dbal/src/Exception/ForeignKeyConstraintViolationException.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ForeignKeyConstraintViolationException.php rename to app/vendor/doctrine/dbal/src/Exception/ForeignKeyConstraintViolationException.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidArgumentException.php b/app/vendor/doctrine/dbal/src/Exception/InvalidArgumentException.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidArgumentException.php rename to app/vendor/doctrine/dbal/src/Exception/InvalidArgumentException.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidFieldNameException.php b/app/vendor/doctrine/dbal/src/Exception/InvalidFieldNameException.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidFieldNameException.php rename to app/vendor/doctrine/dbal/src/Exception/InvalidFieldNameException.php diff --git a/app/vendor/doctrine/dbal/src/Exception/InvalidLockMode.php b/app/vendor/doctrine/dbal/src/Exception/InvalidLockMode.php new file mode 100644 index 000000000..9a4d6c77b --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Exception/InvalidLockMode.php @@ -0,0 +1,24 @@ +|array */ + private $originalParameters; + + /** @var array|array */ + private $originalTypes; + + /** @var int */ + private $originalParameterIndex = 0; + + /** @var list */ + private $convertedSQL = []; + + /** @var list */ + private $convertedParameteres = []; + + /** @var array */ + private $convertedTypes = []; + + /** + * @param array|array $parameters + * @param array|array $types + */ + public function __construct(array $parameters, array $types) + { + $this->originalParameters = $parameters; + $this->originalTypes = $types; + } + + public function acceptPositionalParameter(string $sql): void + { + $index = $this->originalParameterIndex; + + if (! array_key_exists($index, $this->originalParameters)) { + throw MissingPositionalParameter::new($index); + } + + $this->acceptParameter($index, $this->originalParameters[$index]); + + $this->originalParameterIndex++; + } + + public function acceptNamedParameter(string $sql): void + { + $name = substr($sql, 1); + + if (! array_key_exists($name, $this->originalParameters)) { + throw MissingNamedParameter::new($name); + } + + $this->acceptParameter($name, $this->originalParameters[$name]); + } + + public function acceptOther(string $sql): void + { + $this->convertedSQL[] = $sql; + } + + public function getSQL(): string + { + return implode('', $this->convertedSQL); + } + + /** + * @return list + */ + public function getParameters(): array + { + return $this->convertedParameteres; + } + + /** + * @param int|string $key + * @param mixed $value + */ + private function acceptParameter($key, $value): void + { + if (! isset($this->originalTypes[$key])) { + $this->convertedSQL[] = '?'; + $this->convertedParameteres[] = $value; + + return; + } + + $type = $this->originalTypes[$key]; + + if ($type !== Connection::PARAM_INT_ARRAY && $type !== Connection::PARAM_STR_ARRAY) { + $this->appendTypedParameter([$value], $type); + + return; + } + + if (count($value) === 0) { + $this->convertedSQL[] = 'NULL'; + + return; + } + + $this->appendTypedParameter($value, $type - Connection::ARRAY_PARAM_OFFSET); + } + + /** + * @return array + */ + public function getTypes(): array + { + return $this->convertedTypes; + } + + /** + * @param list $values + * @param Type|int|string|null $type + */ + private function appendTypedParameter(array $values, $type): void + { + $this->convertedSQL[] = implode(', ', array_fill(0, count($values), '?')); + + $index = count($this->convertedParameteres); + + foreach ($values as $value) { + $this->convertedParameteres[] = $value; + $this->convertedTypes[$index] = $type; + + $index++; + } + } +} diff --git a/app/vendor/doctrine/dbal/src/FetchMode.php b/app/vendor/doctrine/dbal/src/FetchMode.php new file mode 100644 index 000000000..79fa6a3f3 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/FetchMode.php @@ -0,0 +1,16 @@ +getDriver() instanceof Driver\PDOSqlite\Driver) { + if ($conn->getDriver() instanceof Driver\PDO\SQLite\Driver) { throw new Exception('Cannot use TableGenerator with SQLite.'); } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php b/app/vendor/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php rename to app/vendor/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/LockMode.php b/app/vendor/doctrine/dbal/src/LockMode.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/LockMode.php rename to app/vendor/doctrine/dbal/src/LockMode.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php b/app/vendor/doctrine/dbal/src/Logging/DebugStack.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php rename to app/vendor/doctrine/dbal/src/Logging/DebugStack.php diff --git a/app/vendor/doctrine/dbal/src/Logging/LoggerChain.php b/app/vendor/doctrine/dbal/src/Logging/LoggerChain.php new file mode 100644 index 000000000..9b44dc0e3 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Logging/LoggerChain.php @@ -0,0 +1,40 @@ + */ + private $loggers; + + /** + * @param iterable $loggers + */ + public function __construct(iterable $loggers = []) + { + $this->loggers = $loggers; + } + + /** + * {@inheritdoc} + */ + public function startQuery($sql, ?array $params = null, ?array $types = null) + { + foreach ($this->loggers as $logger) { + $logger->startQuery($sql, $params, $types); + } + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + foreach ($this->loggers as $logger) { + $logger->stopQuery(); + } + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php b/app/vendor/doctrine/dbal/src/Logging/SQLLogger.php similarity index 92% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php rename to app/vendor/doctrine/dbal/src/Logging/SQLLogger.php index 8328a71ba..a0bdf1bf6 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php +++ b/app/vendor/doctrine/dbal/src/Logging/SQLLogger.php @@ -13,7 +13,7 @@ interface SQLLogger * Logs a SQL statement somewhere. * * @param string $sql SQL statement - * @param array|array|null $params Statement parameters + * @param list|array|null $params Statement parameters * @param array|array|null $types Parameter types * * @return void diff --git a/app/vendor/doctrine/dbal/src/ParameterType.php b/app/vendor/doctrine/dbal/src/ParameterType.php new file mode 100644 index 000000000..77917e870 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/ParameterType.php @@ -0,0 +1,57 @@ +doctrineTypeComments)); - return in_array($doctrineType->getName(), $this->doctrineTypeComments); + return in_array($doctrineType->getName(), $this->doctrineTypeComments, true); } /** @@ -657,20 +601,6 @@ public function getRegexpExpression() throw Exception::notSupported(__METHOD__); } - /** - * Returns the global unique identifier expression. - * - * @deprecated Use application-generated UUIDs instead - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getGuidExpression() - { - throw Exception::notSupported(__METHOD__); - } - /** * Returns the SQL snippet to get the average value of a column. * @@ -830,7 +760,7 @@ public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = f $expression .= $char . ' '; } - if ($mode || $char !== false) { + if ($mode !== TrimMode::UNSPECIFIED || $char !== false) { $expression .= 'FROM '; } @@ -1358,6 +1288,11 @@ public function getBitOrComparisonExpression($value1, $value2) return '(' . $value1 . ' | ' . $value2 . ')'; } + /** + * Returns the SQL expression which represents the currently selected database. + */ + abstract public function getCurrentDatabaseExpression(): string; + /** * Returns the FOR UPDATE expression. * @@ -1372,15 +1307,21 @@ public function getForUpdateSQL() * Honors that some SQL vendors such as MsSql use table hints for locking instead of the * ANSI SQL FOR UPDATE specification. * - * @param string $fromClause The FROM clause to append the hint for the given lock mode to. - * @param int|null $lockMode One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will - * be appended to the FROM clause. - * - * @return string + * @param string $fromClause The FROM clause to append the hint for the given lock mode to + * @param int $lockMode One of the Doctrine\DBAL\LockMode::* constants */ - public function appendLockHint($fromClause, $lockMode) + public function appendLockHint(string $fromClause, int $lockMode): string { - return $fromClause; + switch ($lockMode) { + case LockMode::NONE: + case LockMode::OPTIMISTIC: + case LockMode::PESSIMISTIC_READ: + case LockMode::PESSIMISTIC_WRITE: + return $fromClause; + + default: + throw InvalidLockMode::fromLockMode($lockMode); + } } /** @@ -1420,6 +1361,14 @@ public function getDropDatabaseSQL($name) return 'DROP DATABASE ' . $name; } + /** + * Returns the SQL snippet to drop a schema. + */ + public function getDropSchemaSQL(string $schemaName): string + { + return 'DROP SCHEMA ' . $schemaName; + } + /** * Returns the SQL snippet to drop an existing table. * @@ -1575,12 +1524,26 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE if (($createFlags & self::CREATE_INDEXES) > 0) { foreach ($table->getIndexes() as $index) { - if ($index->isPrimary()) { - $options['primary'] = $index->getQuotedColumns($this); - $options['primary_index'] = $index; - } else { + if (! $index->isPrimary()) { $options['indexes'][$index->getQuotedName($this)] = $index; + + continue; } + + $options['primary'] = $index->getQuotedColumns($this); + $options['primary_index'] = $index; + } + + foreach ($table->getUniqueConstraints() as $uniqueConstraint) { + $options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint; + } + } + + if (($createFlags & self::CREATE_FOREIGNKEYS) > 0) { + $options['foreignKeys'] = []; + + foreach ($table->getForeignKeys() as $fkConstraint) { + $options['foreignKeys'][] = $fkConstraint; } } @@ -1593,6 +1556,7 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn) ) { $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs); $columnSql = array_merge($columnSql, $eventArgs->getSql()); @@ -1614,22 +1578,16 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE $columnData['length'] = 255; } - if (in_array($column->getName(), $options['primary'])) { + if (in_array($column->getName(), $options['primary'], true)) { $columnData['primary'] = true; } $columns[$name] = $columnData; } - if (($createFlags & self::CREATE_FOREIGNKEYS) > 0) { - $options['foreignKeys'] = []; - foreach ($table->getForeignKeys() as $fkConstraint) { - $options['foreignKeys'][] = $fkConstraint; - } - } - if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) { $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs); if ($eventArgs->isDefaultPrevented()) { @@ -1638,6 +1596,7 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE } $sql = $this->_getCreateTableSQL($tableName, $columns, $options); + if ($this->supportsCommentOnStatement()) { if ($table->hasOption('comment')) { $sql[] = $this->getCommentOnTableSQL($tableName, $table->getOption('comment')); @@ -1736,8 +1695,8 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] } $query = 'CREATE TABLE ' . $name . ' (' . $columnListSql; - $check = $this->getCheckDeclarationSQL($columns); + if (! empty($check)) { $query .= ', ' . $check; } @@ -1817,6 +1776,8 @@ public function getCreateConstraintSQL(Constraint $constraint, $table) 'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().' ); } + } elseif ($constraint instanceof UniqueConstraint) { + $query .= ' UNIQUE'; } elseif ($constraint instanceof ForeignKeyConstraint) { $query .= ' FOREIGN KEY'; @@ -2189,21 +2150,6 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) ]; } - /** - * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions. - * - * @deprecated - * - * @return string[] - */ - protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff) - { - return array_merge( - $this->getPreAlterTableIndexForeignKeySQL($diff), - $this->getPostAlterTableIndexForeignKeySQL($diff) - ); - } - /** * Gets declaration of a number of columns in bulk. * @@ -2276,6 +2222,8 @@ public function getColumnDeclarationListSQL(array $columns) * a string that defines the complete column * * @return string DBMS specific SQL code portion that should be used to declare the column. + * + * @throws Exception */ public function getColumnDeclarationSQL($name, array $column) { @@ -2284,19 +2232,18 @@ public function getColumnDeclarationSQL($name, array $column) } else { $default = $this->getDefaultValueDeclarationSQL($column); - $charset = isset($column['charset']) && $column['charset'] ? + $charset = ! empty($column['charset']) ? ' ' . $this->getColumnCharsetDeclarationSQL($column['charset']) : ''; - $collation = isset($column['collation']) && $column['collation'] ? + $collation = ! empty($column['collation']) ? ' ' . $this->getColumnCollationDeclarationSQL($column['collation']) : ''; - $notnull = isset($column['notnull']) && $column['notnull'] ? ' NOT NULL' : ''; + $notnull = ! empty($column['notnull']) ? ' NOT NULL' : ''; - $unique = isset($column['unique']) && $column['unique'] ? + $unique = ! empty($column['unique']) ? ' ' . $this->getUniqueFieldDeclarationSQL() : ''; - $check = isset($column['check']) && $column['check'] ? - ' ' . $column['check'] : ''; + $check = ! empty($column['check']) ? ' ' . $column['check'] : ''; $typeDecl = $column['type']->getSQLDeclaration($column, $this); $declaration = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation; @@ -2403,25 +2350,27 @@ public function getCheckDeclarationSQL(array $definition) * Obtains DBMS specific SQL code portion needed to set a unique * constraint declaration to be used in statements like CREATE TABLE. * - * @param string $name The name of the unique constraint. - * @param Index $index The index definition. + * @param string $name The name of the unique constraint. + * @param UniqueConstraint $constraint The unique constraint definition. * * @return string DBMS specific SQL code portion needed to set a constraint. * * @throws InvalidArgumentException */ - public function getUniqueConstraintDeclarationSQL($name, Index $index) + public function getUniqueConstraintDeclarationSQL($name, UniqueConstraint $constraint) { - $columns = $index->getColumns(); + $columns = $constraint->getQuotedColumns($this); $name = new Identifier($name); if (count($columns) === 0) { throw new InvalidArgumentException("Incomplete definition. 'columns' required."); } - return 'CONSTRAINT ' . $name->getQuotedName($this) . ' UNIQUE (' - . $this->getIndexFieldDeclarationListSQL($index) - . ')' . $this->getPartialIndexSQL($index); + $constraintFlags = array_merge(['UNIQUE'], array_map('strtoupper', $constraint->getFlags())); + $constraintName = $name->getQuotedName($this); + $columnListNames = $this->getColumnsFieldDeclarationListSQL($columns); + + return sprintf('CONSTRAINT %s %s (%s)', $constraintName, implode(' ', $constraintFlags), $columnListNames); } /** @@ -2444,9 +2393,8 @@ public function getIndexDeclarationSQL($name, Index $index) throw new InvalidArgumentException("Incomplete definition. 'columns' required."); } - return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' (' - . $this->getIndexFieldDeclarationListSQL($index) - . ')' . $this->getPartialIndexSQL($index); + return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) + . ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index); } /** @@ -2466,22 +2414,23 @@ public function getCustomTypeDeclarationSQL(array $column) /** * Obtains DBMS specific SQL code portion needed to set an index * declaration to be used in statements like CREATE TABLE. - * - * @param mixed[]|Index $columnsOrIndex array declaration is deprecated, prefer passing Index to this method */ - public function getIndexFieldDeclarationListSQL($columnsOrIndex): string + public function getIndexFieldDeclarationListSQL(Index $index): string { - if ($columnsOrIndex instanceof Index) { - return implode(', ', $columnsOrIndex->getQuotedColumns($this)); - } - - if (! is_array($columnsOrIndex)) { - throw new InvalidArgumentException('Fields argument should be an Index or array.'); - } + return implode(', ', $index->getQuotedColumns($this)); + } + /** + * Obtains DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param mixed[] $columns + */ + public function getColumnsFieldDeclarationListSQL(array $columns): string + { $ret = []; - foreach ($columnsOrIndex as $column => $definition) { + foreach ($columns as $column => $definition) { if (is_array($definition)) { $ret[] = $column; } else { @@ -2549,7 +2498,7 @@ public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { $query = ''; - if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) { + if ($foreignKey->hasOption('onUpdate')) { $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate')); } @@ -2596,7 +2545,7 @@ public function getForeignKeyReferentialActionSQL($action) public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) { $sql = ''; - if (strlen($foreignKey->getName())) { + if (strlen($foreignKey->getName()) > 0) { $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' '; } @@ -2660,25 +2609,6 @@ public function getColumnCollationDeclarationSQL($collation) return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : ''; } - /** - * Whether the platform prefers sequences for ID generation. - * Subclasses should override this method to return TRUE if they prefer sequences. - * - * @deprecated - * - * @return bool - */ - public function prefersSequences() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4229', - 'AbstractPlatform::prefersSequences() is deprecated without replacement and removed in DBAL 3.0' - ); - - return false; - } - /** * Whether the platform prefers identity columns (eg. autoincrement) for ID generation. * Subclasses should override this method to return TRUE if they prefer identity columns. @@ -2822,12 +2752,21 @@ public function getListDatabasesSQL() /** * Returns the SQL statement for retrieving the namespaces defined in the database. * + * @deprecated Use {@link AbstractSchemaManager::listSchemaNames()} instead. + * * @return string * * @throws Exception If not supported on this platform. */ public function getListNamespacesSQL() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'AbstractPlatform::getListNamespacesSQL() is deprecated,' + . ' use AbstractSchemaManager::listSchemaNames() instead.' + ); + throw Exception::notSupported(__METHOD__); } @@ -3136,7 +3075,7 @@ public function usesSequenceEmulatedIdentityColumns() /** * Returns the name of the sequence for a particular identity column in a particular table. * - * @see usesSequenceEmulatedIdentityColumns + * @see usesSequenceEmulatedIdentityColumns * * @param string $tableName The name of the table to return the sequence name for. * @param string $columnName The name of the identity column in the table to return the sequence name for. @@ -3238,24 +3177,6 @@ public function supportsForeignKeyConstraints() return true; } - /** - * Whether this platform supports onUpdate in foreign key constraints. - * - * @deprecated - * - * @return bool - */ - public function supportsForeignKeyOnUpdate() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4229', - 'AbstractPlatform::supportsForeignKeyOnUpdate() is deprecated without replacement and removed in DBAL 3.0' - ); - - return $this->supportsForeignKeyConstraints(); - } - /** * Whether the platform supports database schemas. * @@ -3353,18 +3274,6 @@ public function hasNativeJsonType() return false; } - /** - * @deprecated - * - * @return string - * - * @todo Remove in 3.0 - */ - public function getIdentityColumnNullInsertSQL() - { - return ''; - } - /** * Whether this platform supports views. * @@ -3434,20 +3343,14 @@ public function getTimeFormatString() * * @param string $query * @param int|null $limit - * @param int|null $offset + * @param int $offset * * @return string * * @throws Exception */ - final public function modifyLimitQuery($query, $limit, $offset = null) + final public function modifyLimitQuery($query, $limit, $offset = 0) { - if ($limit !== null) { - $limit = (int) $limit; - } - - $offset = (int) $offset; - if ($offset < 0) { throw new Exception(sprintf( 'Offset must be a positive integer or zero, %d given', @@ -3470,7 +3373,7 @@ final public function modifyLimitQuery($query, $limit, $offset = null) * * @param string $query * @param int|null $limit - * @param int|null $offset + * @param int $offset * * @return string */ @@ -3497,48 +3400,6 @@ public function supportsLimitOffset() return true; } - /** - * Gets the character casing of a column in an SQL result set of this platform. - * - * @deprecated - * - * @param string $column The column name for which to get the correct character casing. - * - * @return string The column name in the character casing used in SQL result sets. - */ - public function getSQLResultCasing($column) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4229', - 'AbstractPlatform::getSQLResultCasing is deprecated without replacement and removed in DBAL 3.' . - 'Use Portability\Connection with PORTABILITY_FIX_CASE to get portable result cases.' - ); - - return $column; - } - - /** - * Makes any fixes to a name of a schema element (table, sequence, ...) that are required - * by restrictions of the platform, like a maximum length. - * - * @deprecated - * - * @param string $schemaElementName - * - * @return string - */ - public function fixSchemaElementName($schemaElementName) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4132', - 'AbstractPlatform::fixSchemaElementName is deprecated with no replacement and removed in DBAL 3.0' - ); - - return $schemaElementName; - } - /** * Maximum length of any given database identifier, like tables or column names. * @@ -3638,25 +3499,37 @@ public function rollbackSavePoint($savepoint) final public function getReservedKeywordsList() { // Check for an existing instantiation of the keywords class. - if ($this->_keywords) { - return $this->_keywords; + if ($this->_keywords === null) { + // Store the instance so it doesn't need to be generated on every request. + $this->_keywords = $this->createReservedKeywordsList(); } + return $this->_keywords; + } + + /** + * Creates an instance of the reserved keyword list of this platform. + * + * This method will become @abstract in DBAL 4.0.0. + * + * @throws Exception + */ + protected function createReservedKeywordsList(): KeywordList + { $class = $this->getReservedKeywordsClass(); $keywords = new $class(); if (! $keywords instanceof KeywordList) { throw Exception::notSupported(__METHOD__); } - // Store the instance so it doesn't need to be generated on every request. - $this->_keywords = $keywords; - return $keywords; } /** * Returns the class name of the reserved keywords list. * + * @deprecated Implement {@link createReservedKeywordsList()} instead. + * * @return string * @psalm-return class-string * @@ -3664,6 +3537,13 @@ final public function getReservedKeywordsList() */ protected function getReservedKeywordsClass() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'AbstractPlatform::getReservedKeywordsClass() is deprecated,' + . ' use AbstractPlatform::createReservedKeywordsList() instead.' + ); + throw Exception::notSupported(__METHOD__); } @@ -3711,6 +3591,14 @@ final public function escapeStringForLike(string $inputString, string $escapeCha ); } + /** + * @internal + */ + public function createSQLParser(): Parser + { + return new Parser(false); + } + protected function getLikeWildcardCharacters(): string { return '%_'; diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php b/app/vendor/doctrine/dbal/src/Platforms/DB2Platform.php similarity index 94% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php rename to app/vendor/doctrine/dbal/src/Platforms/DB2Platform.php index 28f76ca4c..036310545 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php +++ b/app/vendor/doctrine/dbal/src/Platforms/DB2Platform.php @@ -9,6 +9,7 @@ use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Types; +use Doctrine\Deprecations\Deprecation; use function array_merge; use function count; @@ -19,7 +20,6 @@ use function implode; use function sprintf; use function strpos; -use function strtoupper; class DB2Platform extends AbstractPlatform { @@ -72,21 +72,21 @@ public function getBlobTypeDeclarationSQL(array $column) public function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'smallint' => 'smallint', - 'bigint' => 'bigint', - 'integer' => 'integer', - 'time' => 'time', - 'date' => 'date', - 'varchar' => 'string', - 'character' => 'string', - 'varbinary' => 'binary', - 'binary' => 'binary', - 'clob' => 'text', - 'blob' => 'blob', - 'decimal' => 'decimal', - 'double' => 'float', - 'real' => 'float', - 'timestamp' => 'datetime', + 'bigint' => 'bigint', + 'binary' => 'binary', + 'blob' => 'blob', + 'character' => 'string', + 'clob' => 'text', + 'date' => 'date', + 'decimal' => 'decimal', + 'double' => 'float', + 'integer' => 'integer', + 'real' => 'float', + 'smallint' => 'smallint', + 'time' => 'time', + 'timestamp' => 'datetime', + 'varbinary' => 'binary', + 'varchar' => 'string', ]; } @@ -109,8 +109,8 @@ public function isCommentedDoctrineType(Type $doctrineType) */ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) { - return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(254)') - : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(254)') + : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); } /** @@ -291,6 +291,7 @@ public function getListTableColumnsSQL($table, $database = null) c.colname, c.colno, c.typename, + c.codepage, c.nulls, c.length, c.scale, @@ -668,7 +669,7 @@ private function getAlterColumnClausesSQL(ColumnDiff $columnDiff) $alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this); - if ($column['columnDefinition']) { + if ($column['columnDefinition'] !== null) { return [$alterClause . ' ' . $column['columnDefinition']]; } @@ -692,7 +693,7 @@ private function getAlterColumnClausesSQL(ColumnDiff $columnDiff) if (isset($column['default'])) { $defaultClause = $this->getDefaultValueDeclarationSQL($column); - if ($defaultClause) { + if ($defaultClause !== '') { $clauses[] = $alterClause . ' SET' . $defaultClause; } } else { @@ -760,7 +761,7 @@ public function getDefaultValueDeclarationSQL($column) return ''; } - if (isset($column['version']) && $column['version']) { + if (! empty($column['version'])) { if ((string) $column['type'] !== 'DateTime') { $column['default'] = '1'; } @@ -796,7 +797,7 @@ public function getTemporaryTableName($tableName) /** * {@inheritDoc} */ - protected function doModifyLimitQuery($query, $limit, $offset = null) + protected function doModifyLimitQuery($query, $limit, $offset) { $where = []; @@ -844,32 +845,25 @@ public function getSubstringExpression($string, $start, $length = null) return 'SUBSTR(' . $string . ', ' . $start . ', ' . $length . ')'; } - /** - * {@inheritDoc} - */ - public function supportsIdentityColumns() + public function getCurrentDatabaseExpression(): string { - return true; + return 'CURRENT_USER'; } /** * {@inheritDoc} */ - public function prefersIdentityColumns() + public function supportsIdentityColumns() { return true; } /** * {@inheritDoc} - * - * DB2 returns all column names in SQL result sets in uppercase. - * - * @deprecated */ - public function getSQLResultCasing($column) + public function prefersIdentityColumns() { - return strtoupper($column); + return true; } /** @@ -904,9 +898,18 @@ public function supportsSavepoints() /** * {@inheritDoc} + * + * @deprecated Implement {@link createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'DB2Platform::getReservedKeywordsClass() is deprecated,' + . ' use DB2Platform::createReservedKeywordsList() instead.' + ); + return Keywords\DB2Keywords::class; } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DateIntervalUnit.php b/app/vendor/doctrine/dbal/src/Platforms/DateIntervalUnit.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DateIntervalUnit.php rename to app/vendor/doctrine/dbal/src/Platforms/DateIntervalUnit.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php b/app/vendor/doctrine/dbal/src/Platforms/Keywords/DB2Keywords.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php rename to app/vendor/doctrine/dbal/src/Platforms/Keywords/DB2Keywords.php index 8533f579d..db577d302 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php +++ b/app/vendor/doctrine/dbal/src/Platforms/Keywords/DB2Keywords.php @@ -27,24 +27,6 @@ protected function getKeywords() 'ALIAS', 'ALL', 'ALLOCATE', - 'DOCUMENT', - 'DOUBLE', - 'DROP', - 'DSSIZE', - 'DYNAMIC', - 'EACH', - 'LOCK', - 'LOCKMAX', - 'LOCKSIZE', - 'LONG', - 'LOOP', - 'MAINTAINED', - 'ROUND_CEILING', - 'ROUND_DOWN', - 'ROUND_FLOOR', - 'ROUND_HALF_DOWN', - 'ROUND_HALF_EVEN', - 'ROUND_HALF_UP', 'ALLOW', 'ALTER', 'AND', @@ -112,6 +94,38 @@ protected function getKeywords() 'DATABASE', 'DATAPARTITIONNAME', 'DATAPARTITIONNUM', + 'DATE', + 'DAY', + 'DAYS', + 'DB2GENERAL', + 'DB2GENRL', + 'DB2SQL', + 'DBINFO', + 'DBPARTITIONNAME', + 'DBPARTITIONNUM', + 'DEALLOCATE', + 'DECLARE', + 'DEFAULT', + 'DEFAULTS', + 'DEFINITION', + 'DELETE', + 'DENSE_RANK', + 'DENSERANK', + 'DESCRIBE', + 'DESCRIPTOR', + 'DETERMINISTIC', + 'DIAGNOSTICS', + 'DISABLE', + 'DISALLOW', + 'DISCONNECT', + 'DISTINCT', + 'DO', + 'DOCUMENT', + 'DOUBLE', + 'DROP', + 'DSSIZE', + 'DYNAMIC', + 'EACH', 'EDITPROC', 'ELSE', 'ELSEIF', @@ -179,6 +193,38 @@ protected function getKeywords() 'INSENSITIVE', 'INSERT', 'INTEGRITY', + 'INTERSECT', + 'INTO', + 'IS', + 'ISOBID', + 'ISOLATION', + 'ITERATE', + 'JAR', + 'JAVA', + 'JOIN', + 'KEEP', + 'KEY', + 'LABEL', + 'LANGUAGE', + 'LATERAL', + 'LC_CTYPE', + 'LEAVE', + 'LEFT', + 'LIKE', + 'LINKTYPE', + 'LOCAL', + 'LOCALDATE', + 'LOCALE', + 'LOCALTIME', + 'LOCALTIMESTAMP RIGHT', + 'LOCATOR', + 'LOCATORS', + 'LOCK', + 'LOCKMAX', + 'LOCKSIZE', + 'LONG', + 'LOOP', + 'MAINTAINED', 'MATERIALIZED', 'MAXVALUE', 'MICROSECOND', @@ -246,6 +292,37 @@ protected function getKeywords() 'PROCEDURE', 'PROGRAM', 'PSID', + 'PUBLIC', + 'QUERY', + 'QUERYNO', + 'RANGE', + 'RANK', + 'READ', + 'READS', + 'RECOVERY', + 'REFERENCES', + 'REFERENCING', + 'REFRESH', + 'RELEASE', + 'RENAME', + 'REPEAT', + 'RESET', + 'RESIGNAL', + 'RESTART', + 'RESTRICT', + 'RESULT', + 'RESULT_SET_LOCATOR WLM', + 'RETURN', + 'RETURNS', + 'REVOKE', + 'ROLE', + 'ROLLBACK', + 'ROUND_CEILING', + 'ROUND_DOWN', + 'ROUND_FLOOR', + 'ROUND_HALF_DOWN', + 'ROUND_HALF_EVEN', + 'ROUND_HALF_UP', 'ROUND_UP', 'ROUTINE', 'ROW', @@ -313,107 +390,30 @@ protected function getKeywords() 'UNIQUE', 'UNTIL', 'UPDATE', - 'DATE', - 'DAY', - 'DAYS', - 'DB2GENERAL', - 'DB2GENRL', - 'DB2SQL', - 'DBINFO', - 'DBPARTITIONNAME', - 'DBPARTITIONNUM', - 'DEALLOCATE', - 'DECLARE', - 'DEFAULT', - 'DEFAULTS', - 'DEFINITION', - 'DELETE', - 'DENSE_RANK', - 'DENSERANK', - 'DESCRIBE', - 'DESCRIPTOR', - 'DETERMINISTIC', - 'DIAGNOSTICS', - 'DISABLE', - 'DISALLOW', - 'DISCONNECT', - 'DISTINCT', - 'DO', - 'INTERSECT', - 'PUBLIC', 'USAGE', - 'INTO', - 'QUERY', 'USER', - 'IS', - 'QUERYNO', 'USING', - 'ISOBID', - 'RANGE', 'VALIDPROC', - 'ISOLATION', - 'RANK', 'VALUE', - 'ITERATE', - 'READ', 'VALUES', - 'JAR', - 'READS', 'VARIABLE', - 'JAVA', - 'RECOVERY', 'VARIANT', - 'JOIN', - 'REFERENCES', 'VCAT', - 'KEEP', - 'REFERENCING', 'VERSION', - 'KEY', - 'REFRESH', 'VIEW', - 'LABEL', - 'RELEASE', 'VOLATILE', - 'LANGUAGE', - 'RENAME', 'VOLUMES', - 'LATERAL', - 'REPEAT', 'WHEN', - 'LC_CTYPE', - 'RESET', 'WHENEVER', - 'LEAVE', - 'RESIGNAL', 'WHERE', - 'LEFT', - 'RESTART', 'WHILE', - 'LIKE', - 'RESTRICT', 'WITH', - 'LINKTYPE', - 'RESULT', 'WITHOUT', - 'LOCAL', - 'RESULT_SET_LOCATOR WLM', - 'LOCALDATE', - 'RETURN', 'WRITE', - 'LOCALE', - 'RETURNS', 'XMLELEMENT', - 'LOCALTIME', - 'REVOKE', 'XMLEXISTS', - 'LOCALTIMESTAMP RIGHT', 'XMLNAMESPACES', - 'LOCATOR', - 'ROLE', 'YEAR', - 'LOCATORS', - 'ROLLBACK', 'YEARS', ]; } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php b/app/vendor/doctrine/dbal/src/Platforms/Keywords/KeywordList.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php rename to app/vendor/doctrine/dbal/src/Platforms/Keywords/KeywordList.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MariaDb102Keywords.php b/app/vendor/doctrine/dbal/src/Platforms/Keywords/MariaDb102Keywords.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MariaDb102Keywords.php rename to app/vendor/doctrine/dbal/src/Platforms/Keywords/MariaDb102Keywords.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQL57Keywords.php b/app/vendor/doctrine/dbal/src/Platforms/Keywords/MySQL57Keywords.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQL57Keywords.php rename to app/vendor/doctrine/dbal/src/Platforms/Keywords/MySQL57Keywords.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQL80Keywords.php b/app/vendor/doctrine/dbal/src/Platforms/Keywords/MySQL80Keywords.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQL80Keywords.php rename to app/vendor/doctrine/dbal/src/Platforms/Keywords/MySQL80Keywords.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php b/app/vendor/doctrine/dbal/src/Platforms/Keywords/MySQLKeywords.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php rename to app/vendor/doctrine/dbal/src/Platforms/Keywords/MySQLKeywords.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php b/app/vendor/doctrine/dbal/src/Platforms/Keywords/OracleKeywords.php similarity index 98% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php rename to app/vendor/doctrine/dbal/src/Platforms/Keywords/OracleKeywords.php index 92ca81a07..a73a93d60 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php +++ b/app/vendor/doctrine/dbal/src/Platforms/Keywords/OracleKeywords.php @@ -22,118 +22,118 @@ protected function getKeywords() { return [ 'ACCESS', - 'ELSE', - 'MODIFY', - 'START', 'ADD', - 'EXCLUSIVE', - 'NOAUDIT', - 'SELECT', 'ALL', - 'EXISTS', - 'NOCOMPRESS', - 'SESSION', 'ALTER', - 'FILE', - 'NOT', - 'SET', 'AND', - 'FLOAT', - 'NOTFOUND ', - 'SHARE', 'ANY', - 'FOR', - 'NOWAIT', - 'SIZE', 'ARRAYLEN', - 'FROM', - 'NULL', - 'SMALLINT', 'AS', - 'GRANT', - 'NUMBER', - 'SQLBUF', 'ASC', - 'GROUP', - 'OF', - 'SUCCESSFUL', 'AUDIT', - 'HAVING', - 'OFFLINE ', - 'SYNONYM', 'BETWEEN', - 'IDENTIFIED', - 'ON', - 'SYSDATE', 'BY', - 'IMMEDIATE', - 'ONLINE', - 'TABLE', 'CHAR', - 'IN', - 'OPTION', - 'THEN', 'CHECK', - 'INCREMENT', - 'OR', - 'TO', 'CLUSTER', - 'INDEX', - 'ORDER', - 'TRIGGER', 'COLUMN', - 'INITIAL', - 'PCTFREE', - 'UID', 'COMMENT', - 'INSERT', - 'PRIOR', - 'UNION', 'COMPRESS', - 'INTEGER', - 'PRIVILEGES', - 'UNIQUE', 'CONNECT', - 'INTERSECT', - 'PUBLIC', - 'UPDATE', 'CREATE', - 'INTO', - 'RAW', - 'USER', 'CURRENT', - 'IS', - 'RENAME', - 'VALIDATE', 'DATE', - 'LEVEL', - 'RESOURCE', - 'VALUES', 'DECIMAL', - 'LIKE', - 'REVOKE', - 'VARCHAR', 'DEFAULT', - 'LOCK', - 'ROW', - 'VARCHAR2', 'DELETE', - 'LONG', - 'ROWID', - 'VIEW', 'DESC', - 'MAXEXTENTS', - 'ROWLABEL', - 'WHENEVER', 'DISTINCT', - 'MINUS', - 'ROWNUM', - 'WHERE', 'DROP', + 'ELSE', + 'EXCLUSIVE', + 'EXISTS', + 'FILE', + 'FLOAT', + 'FOR', + 'FROM', + 'GRANT', + 'GROUP', + 'HAVING', + 'IDENTIFIED', + 'IMMEDIATE', + 'IN', + 'INCREMENT', + 'INDEX', + 'INITIAL', + 'INSERT', + 'INTEGER', + 'INTERSECT', + 'INTO', + 'IS', + 'LEVEL', + 'LIKE', + 'LOCK', + 'LONG', + 'MAXEXTENTS', + 'MINUS', 'MODE', + 'MODIFY', + 'NOAUDIT', + 'NOCOMPRESS', + 'NOT', + 'NOTFOUND', + 'NOWAIT', + 'NULL', + 'NUMBER', + 'OF', + 'OFFLINE', + 'ON', + 'ONLINE', + 'OPTION', + 'OR', + 'ORDER', + 'PCTFREE', + 'PRIOR', + 'PRIVILEGES', + 'PUBLIC', + 'RANGE', + 'RAW', + 'RENAME', + 'RESOURCE', + 'REVOKE', + 'ROW', + 'ROWID', + 'ROWLABEL', + 'ROWNUM', 'ROWS', + 'SELECT', + 'SESSION', + 'SET', + 'SHARE', + 'SIZE', + 'SMALLINT', + 'SQLBUF', + 'START', + 'SUCCESSFUL', + 'SYNONYM', + 'SYSDATE', + 'TABLE', + 'THEN', + 'TO', + 'TRIGGER', + 'UID', + 'UNION', + 'UNIQUE', + 'UPDATE', + 'USER', + 'VALIDATE', + 'VALUES', + 'VARCHAR', + 'VARCHAR2', + 'VIEW', + 'WHENEVER', + 'WHERE', 'WITH', - 'RANGE', ]; } } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQL100Keywords.php b/app/vendor/doctrine/dbal/src/Platforms/Keywords/PostgreSQL100Keywords.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQL100Keywords.php rename to app/vendor/doctrine/dbal/src/Platforms/Keywords/PostgreSQL100Keywords.php diff --git a/app/vendor/doctrine/dbal/src/Platforms/Keywords/PostgreSQL94Keywords.php b/app/vendor/doctrine/dbal/src/Platforms/Keywords/PostgreSQL94Keywords.php new file mode 100644 index 000000000..eeff23cab --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Platforms/Keywords/PostgreSQL94Keywords.php @@ -0,0 +1,125 @@ +doctrineTypeMapping['json'] = Types::JSON; + } +} diff --git a/app/vendor/doctrine/dbal/src/Platforms/MySQL57Platform.php b/app/vendor/doctrine/dbal/src/Platforms/MySQL57Platform.php new file mode 100644 index 000000000..f29204cf4 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Platforms/MySQL57Platform.php @@ -0,0 +1,87 @@ +getQuotedName($this)]; + } + + /** + * {@inheritdoc} + * + * @deprecated Implement {@link createReservedKeywordsList()} instead. + */ + protected function getReservedKeywordsClass() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'MySQL57Platform::getReservedKeywordsClass() is deprecated,' + . ' use MySQL57Platform::createReservedKeywordsList() instead.' + ); + + return Keywords\MySQL57Keywords::class; + } + + /** + * {@inheritdoc} + */ + protected function initializeDoctrineTypeMappings() + { + parent::initializeDoctrineTypeMappings(); + + $this->doctrineTypeMapping['json'] = Types::JSON; + } +} diff --git a/app/vendor/doctrine/dbal/src/Platforms/MySQL80Platform.php b/app/vendor/doctrine/dbal/src/Platforms/MySQL80Platform.php new file mode 100644 index 000000000..808e93449 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Platforms/MySQL80Platform.php @@ -0,0 +1,28 @@ + 0) { + $query .= ' OFFSET ' . $offset; + } + } elseif ($offset > 0) { + // 2^64-1 is the maximum of unsigned BIGINT, the biggest limit possible + $query .= ' LIMIT 18446744073709551615 OFFSET ' . $offset; + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getIdentifierQuoteCharacter() + { + return '`'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos === false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } + + return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + return sprintf('CONCAT(%s)', implode(', ', func_get_args())); + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $function = $operator === '+' ? 'DATE_ADD' : 'DATE_SUB'; + + return $function . '(' . $date . ', INTERVAL ' . $interval . ' ' . $unit . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; + } + + public function getCurrentDatabaseExpression(): string + { + return 'DATABASE()'; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SHOW DATABASES'; + } + + /** + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + return 'SHOW INDEX FROM ' . $table; + } + + /** + * {@inheritDoc} + * + * Two approaches to listing the table indexes. The information_schema is + * preferred, because it doesn't cause problems with SQL keywords such as "order" or "table". + */ + public function getListTableIndexesSQL($table, $database = null) + { + if ($database !== null) { + $database = $this->quoteStringLiteral($database); + $table = $this->quoteStringLiteral($table); + + return 'SELECT NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, COLUMN_NAME AS Column_Name,' . + ' SUB_PART AS Sub_Part, INDEX_TYPE AS Index_Type' . + ' FROM information_schema.STATISTICS WHERE TABLE_NAME = ' . $table . + ' AND TABLE_SCHEMA = ' . $database . + ' ORDER BY SEQ_IN_INDEX ASC'; + } + + return 'SHOW INDEX FROM ' . $table; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + $database = $this->quoteStringLiteral($database); + + return 'SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = ' . $database; + } + + /** + * @param string $table + * @param string|null $database + * + * @return string + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + $table = $this->quoteStringLiteral($table); + + if ($database !== null) { + $database = $this->quoteStringLiteral($database); + } + + $sql = 'SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, ' . + 'k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ ' . + 'FROM information_schema.key_column_usage k /*!50116 ' . + 'INNER JOIN information_schema.referential_constraints c ON ' . + ' c.constraint_name = k.constraint_name AND ' . + ' c.table_name = ' . $table . ' */ WHERE k.table_name = ' . $table; + + $databaseNameSql = $database ?? 'DATABASE()'; + + return $sql . ' AND k.table_schema = ' . $databaseNameSql + . ' /*!50116 AND c.constraint_schema = ' . $databaseNameSql . ' */' + . ' AND k.`REFERENCED_COLUMN_NAME` is not NULL'; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW ' . $name; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed + ? 'BINARY(' . ($length > 0 ? $length : 255) . ')' + : 'VARBINARY(' . ($length > 0 ? $length : 255) . ')'; + } + + /** + * Gets the SQL snippet used to declare a CLOB column type. + * TINYTEXT : 2 ^ 8 - 1 = 255 + * TEXT : 2 ^ 16 - 1 = 65535 + * MEDIUMTEXT : 2 ^ 24 - 1 = 16777215 + * LONGTEXT : 2 ^ 32 - 1 = 4294967295 + * + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $column) + { + if (! empty($column['length']) && is_numeric($column['length'])) { + $length = $column['length']; + + if ($length <= static::LENGTH_LIMIT_TINYTEXT) { + return 'TINYTEXT'; + } + + if ($length <= static::LENGTH_LIMIT_TEXT) { + return 'TEXT'; + } + + if ($length <= static::LENGTH_LIMIT_MEDIUMTEXT) { + return 'MEDIUMTEXT'; + } + } + + return 'LONGTEXT'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $column) + { + if (isset($column['version']) && $column['version'] === true) { + return 'TIMESTAMP'; + } + + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $column) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $column) + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $column) + { + return 'TINYINT(1)'; + } + + /** + * {@inheritDoc} + * + * MySQL prefers "autoincrement" identity columns since sequences can only + * be emulated with a table. + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * MySQL supports this through AUTO_INCREMENT columns. + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsInlineColumnComments() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + $table = $this->quoteStringLiteral($table); + + if ($database !== null) { + $database = $this->quoteStringLiteral($database); + } else { + $database = 'DATABASE()'; + } + + return 'SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, ' . + 'COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, ' . + 'CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation ' . + 'FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ' . $database . ' AND TABLE_NAME = ' . $table . + ' ORDER BY ORDINAL_POSITION ASC'; + } + + public function getListTableMetadataSQL(string $table, ?string $database = null): string + { + return sprintf( + <<<'SQL' +SELECT ENGINE, AUTO_INCREMENT, TABLE_COLLATION, TABLE_COMMENT, CREATE_OPTIONS +FROM information_schema.TABLES +WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = %s AND TABLE_NAME = %s +SQL + , + $database !== null ? $this->quoteStringLiteral($database) : 'DATABASE()', + $this->quoteStringLiteral($table) + ); + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($name, array $columns, array $options = []) + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $constraintName => $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition); + } + } + + // add all indexes + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $indexName => $definition) { + $queryFields .= ', ' . $this->getIndexDeclarationSQL($indexName, $definition); + } + } + + // attach all primary keys + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE '; + + if (! empty($options['temporary'])) { + $query .= 'TEMPORARY '; + } + + $query .= 'TABLE ' . $name . ' (' . $queryFields . ') '; + $query .= $this->buildTableOptions($options); + $query .= $this->buildPartitionOptions($options); + + $sql = [$query]; + $engine = 'INNODB'; + + if (isset($options['engine'])) { + $engine = strtoupper(trim($options['engine'])); + } + + // Propagate foreign key constraints only for InnoDB. + if (isset($options['foreignKeys']) && $engine === 'INNODB') { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $name); + } + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function getDefaultValueDeclarationSQL($column) + { + // Unset the default value if the given column definition does not allow default values. + if ($column['type'] instanceof TextType || $column['type'] instanceof BlobType) { + $column['default'] = null; + } + + return parent::getDefaultValueDeclarationSQL($column); + } + + /** + * Build SQL for table options + * + * @param mixed[] $options + * + * @return string + */ + private function buildTableOptions(array $options) + { + if (isset($options['table_options'])) { + return $options['table_options']; + } + + $tableOptions = []; + + // Charset + if (! isset($options['charset'])) { + $options['charset'] = 'utf8'; + } + + $tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']); + + // Collate + if (! isset($options['collate'])) { + $options['collate'] = $options['charset'] . '_unicode_ci'; + } + + $tableOptions[] = $this->getColumnCollationDeclarationSQL($options['collate']); + + // Engine + if (! isset($options['engine'])) { + $options['engine'] = 'InnoDB'; + } + + $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); + + // Auto increment + if (isset($options['auto_increment'])) { + $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); + } + + // Comment + if (isset($options['comment'])) { + $tableOptions[] = sprintf('COMMENT = %s ', $this->quoteStringLiteral($options['comment'])); + } + + // Row format + if (isset($options['row_format'])) { + $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']); + } + + return implode(' ', $tableOptions); + } + + /** + * Build SQL for partition options. + * + * @param mixed[] $options + * + * @return string + */ + private function buildPartitionOptions(array $options) + { + return isset($options['partition_options']) + ? ' ' . $options['partition_options'] + : ''; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $columnSql = []; + $queryParts = []; + $newName = $diff->getNewName(); + + if ($newName !== false) { + $queryParts[] = 'RENAME TO ' . $newName->getQuotedName($this); + } + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnArray = array_merge($column->toArray(), [ + 'comment' => $this->getColumnComment($column), + ]); + + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP ' . $column->getQuotedName($this); + } + + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $column = $columnDiff->column; + $columnArray = $column->toArray(); + + // Don't propagate default value changes for unsupported column types. + if ( + $columnDiff->hasChanged('default') && + count($columnDiff->changedProperties) === 1 && + ($columnArray['type'] instanceof TextType || $columnArray['type'] instanceof BlobType) + ) { + continue; + } + + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + if (isset($diff->addedIndexes['primary'])) { + $keyColumns = array_unique(array_values($diff->addedIndexes['primary']->getColumns())); + $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; + unset($diff->addedIndexes['primary']); + } elseif (isset($diff->changedIndexes['primary'])) { + // Necessary in case the new primary key includes a new auto_increment column + foreach ($diff->changedIndexes['primary']->getColumns() as $columnName) { + if (isset($diff->addedColumns[$columnName]) && $diff->addedColumns[$columnName]->getAutoincrement()) { + $keyColumns = array_unique(array_values($diff->changedIndexes['primary']->getColumns())); + $queryParts[] = 'DROP PRIMARY KEY'; + $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; + unset($diff->changedIndexes['primary']); + break; + } + } + } + + $sql = []; + $tableSql = []; + + if (! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' + . implode(', ', $queryParts); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $sql = []; + $table = $diff->getName($this)->getQuotedName($this); + + foreach ($diff->changedIndexes as $changedIndex) { + $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $changedIndex)); + } + + foreach ($diff->removedIndexes as $remKey => $remIndex) { + $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $remIndex)); + + foreach ($diff->addedIndexes as $addKey => $addIndex) { + if ($remIndex->getColumns() !== $addIndex->getColumns()) { + continue; + } + + $indexClause = 'INDEX ' . $addIndex->getName(); + + if ($addIndex->isPrimary()) { + $indexClause = 'PRIMARY KEY'; + } elseif ($addIndex->isUnique()) { + $indexClause = 'UNIQUE INDEX ' . $addIndex->getName(); + } + + $query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', '; + $query .= 'ADD ' . $indexClause; + $query .= ' (' . $this->getIndexFieldDeclarationListSQL($addIndex) . ')'; + + $sql[] = $query; + + unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]); + + break; + } + } + + $engine = 'INNODB'; + + if ($diff->fromTable instanceof Table && $diff->fromTable->hasOption('engine')) { + $engine = strtoupper(trim($diff->fromTable->getOption('engine'))); + } + + // Suppress foreign key constraint propagation on non-supporting engines. + if ($engine !== 'INNODB') { + $diff->addedForeignKeys = []; + $diff->changedForeignKeys = []; + $diff->removedForeignKeys = []; + } + + $sql = array_merge( + $sql, + $this->getPreAlterTableAlterIndexForeignKeySQL($diff), + parent::getPreAlterTableIndexForeignKeySQL($diff), + $this->getPreAlterTableRenameIndexForeignKeySQL($diff) + ); + + return $sql; + } + + /** + * @return string[] + * + * @throws Exception + */ + private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $index) + { + $sql = []; + + if (! $index->isPrimary() || ! $diff->fromTable instanceof Table) { + return $sql; + } + + $tableName = $diff->getName($this)->getQuotedName($this); + + // Dropping primary keys requires to unset autoincrement attribute on the particular column first. + foreach ($index->getColumns() as $columnName) { + if (! $diff->fromTable->hasColumn($columnName)) { + continue; + } + + $column = $diff->fromTable->getColumn($columnName); + + if ($column->getAutoincrement() !== true) { + continue; + } + + $column->setAutoincrement(false); + + $sql[] = 'ALTER TABLE ' . $tableName . ' MODIFY ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + + // original autoincrement information might be needed later on by other parts of the table alteration + $column->setAutoincrement(true); + } + + return $sql; + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return string[] + * + * @throws Exception + */ + private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff) + { + $sql = []; + $table = $diff->getName($this)->getQuotedName($this); + + foreach ($diff->changedIndexes as $changedIndex) { + // Changed primary key + if (! $changedIndex->isPrimary() || ! ($diff->fromTable instanceof Table)) { + continue; + } + + foreach ($diff->fromTable->getPrimaryKeyColumns() as $columnName => $column) { + // Check if an autoincrement column was dropped from the primary key. + if (! $column->getAutoincrement() || in_array($columnName, $changedIndex->getColumns(), true)) { + continue; + } + + // The autoincrement attribute needs to be removed from the dropped column + // before we can drop and recreate the primary key. + $column->setAutoincrement(false); + + $sql[] = 'ALTER TABLE ' . $table . ' MODIFY ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + + // Restore the autoincrement attribute as it might be needed later on + // by other parts of the table alteration. + $column->setAutoincrement(true); + } + } + + return $sql; + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return string[] + */ + protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + $sql = []; + $tableName = $diff->getName($this)->getQuotedName($this); + + foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { + if (in_array($foreignKey, $diff->changedForeignKeys, true)) { + continue; + } + + $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + } + + return $sql; + } + + /** + * Returns the remaining foreign key constraints that require one of the renamed indexes. + * + * "Remaining" here refers to the diff between the foreign keys currently defined in the associated + * table and the foreign keys to be removed. + * + * @param TableDiff $diff The table diff to evaluate. + * + * @return ForeignKeyConstraint[] + */ + private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff) + { + if (empty($diff->renamedIndexes) || ! $diff->fromTable instanceof Table) { + return []; + } + + $foreignKeys = []; + /** @var ForeignKeyConstraint[] $remainingForeignKeys */ + $remainingForeignKeys = array_diff_key( + $diff->fromTable->getForeignKeys(), + $diff->removedForeignKeys + ); + + foreach ($remainingForeignKeys as $foreignKey) { + foreach ($diff->renamedIndexes as $index) { + if ($foreignKey->intersectsIndexColumns($index)) { + $foreignKeys[] = $foreignKey; + + break; + } + } + } + + return $foreignKeys; + } + + /** + * {@inheritdoc} + */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + return array_merge( + parent::getPostAlterTableIndexForeignKeySQL($diff), + $this->getPostAlterTableRenameIndexForeignKeySQL($diff) + ); + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return string[] + */ + protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + $sql = []; + $newName = $diff->getNewName(); + + if ($newName !== false) { + $tableName = $newName->getQuotedName($this); + } else { + $tableName = $diff->getName($this)->getQuotedName($this); + } + + foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { + if (in_array($foreignKey, $diff->changedForeignKeys, true)) { + continue; + } + + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } elseif ($index->hasFlag('fulltext')) { + $type .= 'FULLTEXT '; + } elseif ($index->hasFlag('spatial')) { + $type .= 'SPATIAL '; + } + + return $type; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $column) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $column) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $column) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritdoc} + */ + public function getFloatDeclarationSQL(array $column) + { + return 'DOUBLE PRECISION' . $this->getUnsignedDeclaration($column); + } + + /** + * {@inheritdoc} + */ + public function getDecimalTypeDeclarationSQL(array $column) + { + return parent::getDecimalTypeDeclarationSQL($column) . $this->getUnsignedDeclaration($column); + } + + /** + * Get unsigned declaration for a column. + * + * @param mixed[] $columnDef + * + * @return string + */ + private function getUnsignedDeclaration(array $columnDef) + { + return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : ''; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $column) + { + $autoinc = ''; + if (! empty($column['autoincrement'])) { + $autoinc = ' AUTO_INCREMENT'; + } + + return $this->getUnsignedDeclaration($column) . $autoinc; + } + + /** + * {@inheritDoc} + */ + public function getColumnCharsetDeclarationSQL($charset) + { + return 'CHARACTER SET ' . $charset; + } + + /** + * {@inheritDoc} + */ + public function getColumnCollationDeclarationSQL($collation) + { + return 'COLLATE ' . $this->quoteSingleIdentifier($collation); + } + + /** + * {@inheritDoc} + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = ''; + if ($foreignKey->hasOption('match')) { + $query .= ' MATCH ' . $foreignKey->getOption('match'); + } + + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index) { + $indexName = $index->getQuotedName($this); + } elseif (is_string($index)) { + $indexName = $index; + } else { + throw new InvalidArgumentException( + __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.' + ); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (! is_string($table)) { + throw new InvalidArgumentException( + __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.' + ); + } + + if ($index instanceof Index && $index->isPrimary()) { + // MySQL primary keys are always named "PRIMARY", + // so we cannot use them in statements because of them being keyword. + return $this->getDropPrimaryKeySQL($table); + } + + return 'DROP INDEX ' . $indexName . ' ON ' . $table; + } + + /** + * @param string $table + * + * @return string + */ + protected function getDropPrimaryKeySQL($table) + { + return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'mysql'; + } + + /** + * {@inheritDoc} + */ + public function getReadLockSQL() + { + return 'LOCK IN SHARE MODE'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = [ + 'bigint' => 'bigint', + 'binary' => 'binary', + 'blob' => 'blob', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'decimal' => 'decimal', + 'double' => 'float', + 'float' => 'float', + 'int' => 'integer', + 'integer' => 'integer', + 'longblob' => 'blob', + 'longtext' => 'text', + 'mediumblob' => 'blob', + 'mediumint' => 'integer', + 'mediumtext' => 'text', + 'numeric' => 'decimal', + 'real' => 'float', + 'set' => 'simple_array', + 'smallint' => 'smallint', + 'string' => 'string', + 'text' => 'text', + 'time' => 'time', + 'timestamp' => 'datetime', + 'tinyblob' => 'blob', + 'tinyint' => 'boolean', + 'tinytext' => 'text', + 'varbinary' => 'binary', + 'varchar' => 'string', + 'year' => 'date', + ]; + } + + /** + * {@inheritDoc} + */ + public function getVarcharMaxLength() + { + return 65535; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 65535; + } + + /** + * {@inheritDoc} + * + * @deprecated Implement {@link createReservedKeywordsList()} instead. + */ + protected function getReservedKeywordsClass() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'MySQLPlatform::getReservedKeywordsClass() is deprecated,' + . ' use MySQLPlatform::createReservedKeywordsList() instead.' + ); + + return Keywords\MySQLKeywords::class; + } + + /** + * {@inheritDoc} + * + * MySQL commits a transaction implicitly when DROP TABLE is executed, however not + * if DROP TEMPORARY TABLE is executed. + */ + public function getDropTemporaryTableSQL($table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (! is_string($table)) { + throw new InvalidArgumentException( + __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.' + ); + } + + return 'DROP TEMPORARY TABLE ' . $table; + } + + /** + * Gets the SQL Snippet used to declare a BLOB column type. + * TINYBLOB : 2 ^ 8 - 1 = 255 + * BLOB : 2 ^ 16 - 1 = 65535 + * MEDIUMBLOB : 2 ^ 24 - 1 = 16777215 + * LONGBLOB : 2 ^ 32 - 1 = 4294967295 + * + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $column) + { + if (! empty($column['length']) && is_numeric($column['length'])) { + $length = $column['length']; + + if ($length <= static::LENGTH_LIMIT_TINYBLOB) { + return 'TINYBLOB'; + } + + if ($length <= static::LENGTH_LIMIT_BLOB) { + return 'BLOB'; + } + + if ($length <= static::LENGTH_LIMIT_MEDIUMBLOB) { + return 'MEDIUMBLOB'; + } + } + + return 'LONGBLOB'; + } + + /** + * {@inheritdoc} + */ + public function quoteStringLiteral($str) + { + $str = str_replace('\\', '\\\\', $str); // MySQL requires backslashes to be escaped aswell. + + return parent::quoteStringLiteral($str); + } + + /** + * {@inheritdoc} + */ + public function getDefaultTransactionIsolationLevel() + { + return TransactionIsolationLevel::REPEATABLE_READ; + } + + public function supportsColumnLengthIndexes(): bool + { + return true; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php b/app/vendor/doctrine/dbal/src/Platforms/OraclePlatform.php similarity index 89% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php rename to app/vendor/doctrine/dbal/src/Platforms/OraclePlatform.php index a05937de6..7be7621d3 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php +++ b/app/vendor/doctrine/dbal/src/Platforms/OraclePlatform.php @@ -45,7 +45,7 @@ class OraclePlatform extends AbstractPlatform */ public static function assertValidIdentifier($identifier) { - if (! preg_match('(^(([a-zA-Z]{1}[a-zA-Z0-9_$#]{0,})|("[^"]+"))$)', $identifier)) { + if (preg_match('(^(([a-zA-Z]{1}[a-zA-Z0-9_$#]{0,})|("[^"]+"))$)', $identifier) === 0) { throw new Exception('Invalid Oracle identifier'); } } @@ -90,16 +90,6 @@ public function getLocateExpression($str, $substr, $startPos = false) return 'INSTR(' . $str . ', ' . $substr . ', ' . $startPos . ')'; } - /** - * {@inheritDoc} - * - * @deprecated Use application-generated UUIDs instead - */ - public function getGuidExpression() - { - return 'SYS_GUID()'; - } - /** * {@inheritdoc} */ @@ -162,6 +152,11 @@ public function getBitAndComparisonExpression($value1, $value2) return 'BITAND(' . $value1 . ', ' . $value2 . ')'; } + public function getCurrentDatabaseExpression(): string + { + return "SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA')"; + } + /** * {@inheritDoc} */ @@ -334,8 +329,8 @@ protected function _getCommonIntegerTypeDeclarationSQL(array $column) */ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) { - return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(2000)') - : ($length ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)'); + return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(2000)') + : ($length > 0 ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)'); } /** @@ -343,7 +338,7 @@ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) */ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) { - return 'RAW(' . ($length ?: $this->getBinaryMaxLength()) . ')'; + return 'RAW(' . ($length > 0 ? $length : $this->getBinaryMaxLength()) . ')'; } /** @@ -536,7 +531,6 @@ public function getCreateAutoincrementSql($name, $table, $start = 1) last_Sequence NUMBER; last_InsertID NUMBER; BEGIN - SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL; IF (:NEW.' . $quotedName . ' IS NULL OR :NEW.' . $quotedName . ' = 0) THEN SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL; ELSE @@ -547,6 +541,7 @@ public function getCreateAutoincrementSql($name, $table, $start = 1) WHILE (last_InsertID > last_Sequence) LOOP SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; END LOOP; + SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; END IF; END;'; @@ -751,13 +746,17 @@ public function getDropForeignKeySQL($foreignKey, $table) */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { - $referentialAction = null; + $referentialAction = ''; if ($foreignKey->hasOption('onDelete')) { $referentialAction = $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); } - return $referentialAction ? ' ON DELETE ' . $referentialAction : ''; + if ($referentialAction !== '') { + return ' ON DELETE ' . $referentialAction; + } + + return ''; } /** @@ -811,7 +810,7 @@ public function getAlterTableSQL(TableDiff $diff) $fields[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); $comment = $this->getColumnComment($column); - if (! $comment) { + if ($comment === null || $comment === '') { continue; } @@ -822,7 +821,7 @@ public function getAlterTableSQL(TableDiff $diff) ); } - if (count($fields)) { + if (count($fields) > 0) { $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ADD (' . implode(', ', $fields) . ')'; } @@ -872,7 +871,7 @@ public function getAlterTableSQL(TableDiff $diff) ); } - if (count($fields)) { + if (count($fields) > 0) { $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' MODIFY (' . implode(', ', $fields) . ')'; } @@ -897,7 +896,7 @@ public function getAlterTableSQL(TableDiff $diff) $fields[] = $column->getQuotedName($this); } - if (count($fields)) { + if (count($fields) > 0) { $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' DROP (' . implode(', ', $fields) . ')'; } @@ -943,10 +942,10 @@ public function getColumnDeclarationSQL($name, array $column) $notnull = $column['notnull'] ? ' NOT NULL' : ' NULL'; } - $unique = isset($column['unique']) && $column['unique'] ? + $unique = ! empty($column['unique']) ? ' ' . $this->getUniqueFieldDeclarationSQL() : ''; - $check = isset($column['check']) && $column['check'] ? + $check = ! empty($column['check']) ? ' ' . $column['check'] : ''; $typeDecl = $column['type']->getSQLDeclaration($column, $this); @@ -969,22 +968,6 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)]; } - /** - * {@inheritDoc} - * - * @deprecated - */ - public function prefersSequences() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4229', - 'AbstractPlatform::prefersSequences() is deprecated without replacement and removed in DBAL 3.0' - ); - - return true; - } - /** * {@inheritdoc} */ @@ -1031,14 +1014,14 @@ public function getName() /** * {@inheritDoc} */ - protected function doModifyLimitQuery($query, $limit, $offset = null) + protected function doModifyLimitQuery($query, $limit, $offset) { if ($limit === null && $offset <= 0) { return $query; } - if (preg_match('/^\s*SELECT/i', $query)) { - if (! preg_match('/\sFROM\s/i', $query)) { + if (preg_match('/^\s*SELECT/i', $query) === 1) { + if (preg_match('/\sFROM\s/i', $query) === 0) { $query .= ' FROM dual'; } @@ -1062,18 +1045,6 @@ protected function doModifyLimitQuery($query, $limit, $offset = null) return $query; } - /** - * {@inheritDoc} - * - * Oracle returns all column names in SQL result sets in uppercase. - * - * @deprecated - */ - public function getSQLResultCasing($column) - { - return strtoupper($column); - } - /** * {@inheritDoc} */ @@ -1106,27 +1077,6 @@ public function getTimeFormatString() return '1900-01-01 H:i:s'; } - /** - * {@inheritDoc} - * - * @deprecated - */ - public function fixSchemaElementName($schemaElementName) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4132', - 'AbstractPlatform::fixSchemaElementName is deprecated with no replacement and removed in DBAL 3.0' - ); - - if (strlen($schemaElementName) > 30) { - // Trim it - return substr($schemaElementName, 0, 30); - } - - return $schemaElementName; - } - /** * {@inheritDoc} */ @@ -1143,22 +1093,6 @@ public function supportsSequences() return true; } - /** - * {@inheritDoc} - * - * @deprecated - */ - public function supportsForeignKeyOnUpdate() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4229', - 'AbstractPlatform::supportsForeignKeyOnUpdate() is deprecated without replacement and removed in DBAL 3.0' - ); - - return false; - } - /** * {@inheritDoc} */ @@ -1193,29 +1127,29 @@ public function getDummySelectSQL() protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'integer' => 'integer', - 'number' => 'integer', - 'pls_integer' => 'boolean', - 'binary_integer' => 'boolean', - 'varchar' => 'string', - 'varchar2' => 'string', - 'nvarchar2' => 'string', - 'char' => 'string', - 'nchar' => 'string', - 'date' => 'date', - 'timestamp' => 'datetime', - 'timestamptz' => 'datetimetz', - 'float' => 'float', - 'binary_float' => 'float', - 'binary_double' => 'float', - 'long' => 'string', - 'clob' => 'text', - 'nclob' => 'text', - 'raw' => 'binary', - 'long raw' => 'blob', - 'rowid' => 'string', - 'urowid' => 'string', - 'blob' => 'blob', + 'binary_double' => 'float', + 'binary_float' => 'float', + 'binary_integer' => 'boolean', + 'blob' => 'blob', + 'char' => 'string', + 'clob' => 'text', + 'date' => 'date', + 'float' => 'float', + 'integer' => 'integer', + 'long' => 'string', + 'long raw' => 'blob', + 'nchar' => 'string', + 'nclob' => 'text', + 'number' => 'integer', + 'nvarchar2' => 'string', + 'pls_integer' => 'boolean', + 'raw' => 'binary', + 'rowid' => 'string', + 'timestamp' => 'datetime', + 'timestamptz' => 'datetimetz', + 'urowid' => 'string', + 'varchar' => 'string', + 'varchar2' => 'string', ]; } @@ -1229,9 +1163,18 @@ public function releaseSavePoint($savepoint) /** * {@inheritDoc} + * + * @deprecated Implement {@link createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'OraclePlatform::getReservedKeywordsClass() is deprecated,' + . ' use OraclePlatform::createReservedKeywordsList() instead.' + ); + return Keywords\OracleKeywords::class; } diff --git a/app/vendor/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php b/app/vendor/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php new file mode 100644 index 000000000..c9ca509d0 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php @@ -0,0 +1,44 @@ +quoteStringLiteral($database) . " + AND sequence_schema NOT LIKE 'pg\_%' + AND sequence_schema != 'information_schema'"; + } +} diff --git a/app/vendor/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php b/app/vendor/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php new file mode 100644 index 000000000..bf21477c6 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php @@ -0,0 +1,1307 @@ + [ + 't', + 'true', + 'y', + 'yes', + 'on', + '1', + ], + 'false' => [ + 'f', + 'false', + 'n', + 'no', + 'off', + '0', + ], + ]; + + /** + * PostgreSQL has different behavior with some drivers + * with regard to how booleans have to be handled. + * + * Enables use of 'true'/'false' or otherwise 1 and 0 instead. + * + * @param bool $flag + * + * @return void + */ + public function setUseBooleanTrueFalseStrings($flag) + { + $this->useBooleanTrueFalseStrings = (bool) $flag; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($string, $start, $length = null) + { + if ($length === null) { + return 'SUBSTRING(' . $string . ' FROM ' . $start . ')'; + } + + return 'SUBSTRING(' . $string . ' FROM ' . $start . ' FOR ' . $length . ')'; + } + + /** + * {@inheritDoc} + */ + public function getNowExpression() + { + return 'LOCALTIMESTAMP(0)'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'SIMILAR TO'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos !== false) { + $str = $this->getSubstringExpression($str, $startPos); + + return 'CASE WHEN (POSITION(' . $substr . ' IN ' . $str . ') = 0) THEN 0' + . ' ELSE (POSITION(' . $substr . ' IN ' . $str . ') + ' . ($startPos - 1) . ') END'; + } + + return 'POSITION(' . $substr . ' IN ' . $str . ')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + if ($unit === DateIntervalUnit::QUARTER) { + $interval *= 3; + $unit = DateIntervalUnit::MONTH; + } + + return '(' . $date . ' ' . $operator . ' (' . $interval . " || ' " . $unit . "')::interval)"; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))'; + } + + public function getCurrentDatabaseExpression(): string + { + return 'CURRENT_DATABASE()'; + } + + /** + * {@inheritDoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsSchemas() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getDefaultSchemaName() + { + return 'public'; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsPartialIndexes() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function usesSequenceEmulatedIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getIdentitySequenceName($tableName, $columnName) + { + return $tableName . '_' . $columnName . '_seq'; + } + + /** + * {@inheritDoc} + */ + public function supportsCommentOnStatement() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function hasNativeGuidType() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SELECT datname FROM pg_database'; + } + + /** + * {@inheritDoc} + * + * @deprecated Use {@link PostgreSQLSchemaManager::listSchemaNames()} instead. + */ + public function getListNamespacesSQL() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'PostgreSQL94Platform::getListNamespacesSQL() is deprecated,' + . ' use PostgreSQLSchemaManager::listSchemaNames() instead.' + ); + + return "SELECT schema_name AS nspname + FROM information_schema.schemata + WHERE schema_name NOT LIKE 'pg\_%' + AND schema_name != 'information_schema'"; + } + + /** + * {@inheritDoc} + */ + public function getListSequencesSQL($database) + { + return "SELECT sequence_name AS relname, + sequence_schema AS schemaname + FROM information_schema.sequences + WHERE sequence_schema NOT LIKE 'pg\_%' + AND sequence_schema != 'information_schema'"; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SELECT quote_ident(table_name) AS table_name, + table_schema AS schema_name + FROM information_schema.tables + WHERE table_schema NOT LIKE 'pg\_%' + AND table_schema != 'information_schema' + AND table_name != 'geometry_columns' + AND table_name != 'spatial_ref_sys' + AND table_type != 'VIEW'"; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return 'SELECT quote_ident(table_name) AS viewname, + table_schema AS schemaname, + view_definition AS definition + FROM information_schema.views + WHERE view_definition IS NOT NULL'; + } + + /** + * @param string $table + * @param string|null $database + * + * @return string + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + return 'SELECT quote_ident(r.conname) as conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef + FROM pg_catalog.pg_constraint r + WHERE r.conrelid = + ( + SELECT c.oid + FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n + WHERE ' . $this->getTableWhereClause($table) . " AND n.oid = c.relnamespace + ) + AND r.contype = 'f'"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + $table = new Identifier($table); + $table = $this->quoteStringLiteral($table->getName()); + + return sprintf( + <<<'SQL' +SELECT + quote_ident(relname) as relname +FROM + pg_class +WHERE oid IN ( + SELECT indexrelid + FROM pg_index, pg_class + WHERE pg_class.relname = %s + AND pg_class.oid = pg_index.indrelid + AND (indisunique = 't' OR indisprimary = 't') + ) +SQL + , + $table + ); + } + + /** + * {@inheritDoc} + * + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + public function getListTableIndexesSQL($table, $database = null) + { + return 'SELECT quote_ident(relname) as relname, pg_index.indisunique, pg_index.indisprimary, + pg_index.indkey, pg_index.indrelid, + pg_get_expr(indpred, indrelid) AS where + FROM pg_class, pg_index + WHERE oid IN ( + SELECT indexrelid + FROM pg_index si, pg_class sc, pg_namespace sn + WHERE ' . $this->getTableWhereClause($table, 'sc', 'sn') . ' + AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid + ) AND pg_index.indexrelid = oid'; + } + + /** + * @param string $table + * @param string $classAlias + * @param string $namespaceAlias + * + * @return string + */ + private function getTableWhereClause($table, $classAlias = 'c', $namespaceAlias = 'n') + { + $whereClause = $namespaceAlias . ".nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND "; + if (strpos($table, '.') !== false) { + [$schema, $table] = explode('.', $table); + $schema = $this->quoteStringLiteral($schema); + } else { + $schema = 'ANY(current_schemas(false))'; + } + + $table = new Identifier($table); + $table = $this->quoteStringLiteral($table->getName()); + + return $whereClause . sprintf( + '%s.relname = %s AND %s.nspname = %s', + $classAlias, + $table, + $namespaceAlias, + $schema + ); + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT + a.attnum, + quote_ident(a.attname) AS field, + t.typname AS type, + format_type(a.atttypid, a.atttypmod) AS complete_type, + (SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation, + (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type, + (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM + pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type, + a.attnotnull AS isnotnull, + (SELECT 't' + FROM pg_index + WHERE c.oid = pg_index.indrelid + AND pg_index.indkey[0] = a.attnum + AND pg_index.indisprimary = 't' + ) AS pri, + (SELECT pg_get_expr(adbin, adrelid) + FROM pg_attrdef + WHERE c.oid = pg_attrdef.adrelid + AND pg_attrdef.adnum=a.attnum + ) AS default, + (SELECT pg_description.description + FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid + ) AS comment + FROM pg_attribute a, pg_class c, pg_type t, pg_namespace n + WHERE " . $this->getTableWhereClause($table, 'c', 'n') . ' + AND a.attnum > 0 + AND a.attrelid = c.oid + AND a.atttypid = t.oid + AND n.oid = c.relnamespace + ORDER BY a.attnum'; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = ''; + + if ($foreignKey->hasOption('match')) { + $query .= ' MATCH ' . $foreignKey->getOption('match'); + } + + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + if ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) { + $query .= ' DEFERRABLE'; + } else { + $query .= ' NOT DEFERRABLE'; + } + + if ( + ($foreignKey->hasOption('feferred') && $foreignKey->getOption('feferred') !== false) + || ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) + ) { + $query .= ' INITIALLY DEFERRED'; + } else { + $query .= ' INITIALLY IMMEDIATE'; + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = []; + $commentsSQL = []; + $columnSql = []; + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $query = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + + $comment = $this->getColumnComment($column); + + if ($comment === null || $comment === '') { + continue; + } + + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $comment + ); + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $query = 'DROP ' . $column->getQuotedName($this); + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + if ($this->isUnchangedBinaryColumn($columnDiff)) { + continue; + } + + $oldColumnName = $columnDiff->getOldColumnName()->getQuotedName($this); + $column = $columnDiff->column; + + if ( + $columnDiff->hasChanged('type') + || $columnDiff->hasChanged('precision') + || $columnDiff->hasChanged('scale') + || $columnDiff->hasChanged('fixed') + ) { + $type = $column->getType(); + + // SERIAL/BIGSERIAL are not "real" types and we can't alter a column to that type + $columnDefinition = $column->toArray(); + $columnDefinition['autoincrement'] = false; + + // here was a server version check before, but DBAL API does not support this anymore. + $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $type->getSQLDeclaration($columnDefinition, $this); + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + if ($columnDiff->hasChanged('default') || $this->typeChangeBreaksDefaultValue($columnDiff)) { + $defaultClause = $column->getDefault() === null + ? ' DROP DEFAULT' + : ' SET' . $this->getDefaultValueDeclarationSQL($column->toArray()); + $query = 'ALTER ' . $oldColumnName . $defaultClause; + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + if ($columnDiff->hasChanged('notnull')) { + $query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotnull() ? 'SET' : 'DROP') . ' NOT NULL'; + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + if ($columnDiff->hasChanged('autoincrement')) { + if ($column->getAutoincrement()) { + // add autoincrement + $seqName = $this->getIdentitySequenceName($diff->name, $oldColumnName); + + $sql[] = 'CREATE SEQUENCE ' . $seqName; + $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ') FROM ' + . $diff->getName($this)->getQuotedName($this) . '))'; + $query = 'ALTER ' . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')"; + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } else { + // Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have + $query = 'ALTER ' . $oldColumnName . ' DROP DEFAULT'; + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + } + + $newComment = $this->getColumnComment($column); + $oldComment = $this->getOldColumnComment($columnDiff); + + if ( + $columnDiff->hasChanged('comment') + || ($columnDiff->fromColumn !== null && $oldComment !== $newComment) + ) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $newComment + ); + } + + if (! $columnDiff->hasChanged('length')) { + continue; + } + + $query = 'ALTER ' . $oldColumnName . ' TYPE ' + . $column->getType()->getSQLDeclaration($column->toArray(), $this); + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . + ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); + } + + $tableSql = []; + + if (! $this->onSchemaAlterTable($diff, $tableSql)) { + $sql = array_merge($sql, $commentsSQL); + + $newName = $diff->getNewName(); + + if ($newName !== false) { + $sql[] = sprintf( + 'ALTER TABLE %s RENAME TO %s', + $diff->getName($this)->getQuotedName($this), + $newName->getQuotedName($this) + ); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Checks whether a given column diff is a logically unchanged binary type column. + * + * Used to determine whether a column alteration for a binary type column can be skipped. + * Doctrine's {@link BinaryType} and {@link BlobType} are mapped to the same database column type on this platform + * as this platform does not have a native VARBINARY/BINARY column type. Therefore the comparator + * might detect differences for binary type columns which do not have to be propagated + * to database as there actually is no difference at database level. + * + * @param ColumnDiff $columnDiff The column diff to check against. + * + * @return bool True if the given column diff is an unchanged binary type column, false otherwise. + */ + private function isUnchangedBinaryColumn(ColumnDiff $columnDiff) + { + $columnType = $columnDiff->column->getType(); + + if (! $columnType instanceof BinaryType && ! $columnType instanceof BlobType) { + return false; + } + + $fromColumn = $columnDiff->fromColumn instanceof Column ? $columnDiff->fromColumn : null; + + if ($fromColumn !== null) { + $fromColumnType = $fromColumn->getType(); + + if (! $fromColumnType instanceof BinaryType && ! $fromColumnType instanceof BlobType) { + return false; + } + + return count(array_diff($columnDiff->changedProperties, ['type', 'length', 'fixed'])) === 0; + } + + if ($columnDiff->hasChanged('type')) { + return false; + } + + return count(array_diff($columnDiff->changedProperties, ['length', 'fixed'])) === 0; + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + if (strpos($tableName, '.') !== false) { + [$schema] = explode('.', $tableName); + $oldIndexName = $schema . '.' . $oldIndexName; + } + + return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)]; + } + + /** + * {@inheritdoc} + */ + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + $tableName = new Identifier($tableName); + $columnName = new Identifier($columnName); + $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment); + + return sprintf( + 'COMMENT ON COLUMN %s.%s IS %s', + $tableName->getQuotedName($this), + $columnName->getQuotedName($this), + $comment + ); + } + + /** + * {@inheritDoc} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' MINVALUE ' . $sequence->getInitialValue() . + ' START ' . $sequence->getInitialValue() . + $this->getSequenceCacheSQL($sequence); + } + + /** + * {@inheritDoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + $this->getSequenceCacheSQL($sequence); + } + + /** + * Cache definition for sequences + * + * @return string + */ + private function getSequenceCacheSQL(Sequence $sequence) + { + if ($sequence->getCache() > 1) { + return ' CACHE ' . $sequence->getCache(); + } + + return ''; + } + + /** + * {@inheritDoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence . ' CASCADE'; + } + + /** + * {@inheritDoc} + */ + public function getCreateSchemaSQL($schemaName) + { + return 'CREATE SCHEMA ' . $schemaName; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + return $this->getDropConstraintSQL($foreignKey, $table); + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($name, array $columns, array $options = []) + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')'; + + $sql = [$query]; + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index) { + $sql[] = $this->getCreateIndexSQL($index, $name); + } + } + + if (isset($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $uniqueConstraint) { + $sql[] = $this->getCreateConstraintSQL($uniqueConstraint, $name); + } + } + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $name); + } + } + + return $sql; + } + + /** + * Converts a single boolean value. + * + * First converts the value to its native PHP boolean type + * and passes it to the given callback function to be reconverted + * into any custom representation. + * + * @param mixed $value The value to convert. + * @param callable $callback The callback function to use for converting the real boolean value. + * + * @return mixed + * + * @throws UnexpectedValueException + */ + private function convertSingleBooleanValue($value, $callback) + { + if ($value === null) { + return $callback(null); + } + + if (is_bool($value) || is_numeric($value)) { + return $callback((bool) $value); + } + + if (! is_string($value)) { + return $callback(true); + } + + /** + * Better safe than sorry: http://php.net/in_array#106319 + */ + if (in_array(strtolower(trim($value)), $this->booleanLiterals['false'], true)) { + return $callback(false); + } + + if (in_array(strtolower(trim($value)), $this->booleanLiterals['true'], true)) { + return $callback(true); + } + + throw new UnexpectedValueException("Unrecognized boolean literal '${value}'"); + } + + /** + * Converts one or multiple boolean values. + * + * First converts the value(s) to their native PHP boolean type + * and passes them to the given callback function to be reconverted + * into any custom representation. + * + * @param mixed $item The value(s) to convert. + * @param callable $callback The callback function to use for converting the real boolean value(s). + * + * @return mixed + */ + private function doConvertBooleans($item, $callback) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + $item[$key] = $this->convertSingleBooleanValue($value, $callback); + } + + return $item; + } + + return $this->convertSingleBooleanValue($item, $callback); + } + + /** + * {@inheritDoc} + * + * Postgres wants boolean values converted to the strings 'true'/'false'. + */ + public function convertBooleans($item) + { + if (! $this->useBooleanTrueFalseStrings) { + return parent::convertBooleans($item); + } + + return $this->doConvertBooleans( + $item, + /** + * @param mixed $value + */ + static function ($value) { + if ($value === null) { + return 'NULL'; + } + + return $value === true ? 'true' : 'false'; + } + ); + } + + /** + * {@inheritDoc} + */ + public function convertBooleansToDatabaseValue($item) + { + if (! $this->useBooleanTrueFalseStrings) { + return parent::convertBooleansToDatabaseValue($item); + } + + return $this->doConvertBooleans( + $item, + /** + * @param mixed $value + */ + static function ($value): ?int { + return $value === null ? null : (int) $value; + } + ); + } + + /** + * {@inheritDoc} + */ + public function convertFromBoolean($item) + { + if ($item !== null && in_array(strtolower($item), $this->booleanLiterals['false'], true)) { + return false; + } + + return parent::convertFromBoolean($item); + } + + /** + * {@inheritDoc} + */ + public function getSequenceNextValSQL($sequence) + { + return "SELECT NEXTVAL('" . $sequence . "')"; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ' + . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $column) + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $column) + { + if (! empty($column['autoincrement'])) { + return 'SERIAL'; + } + + return 'INT'; + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $column) + { + if (! empty($column['autoincrement'])) { + return 'BIGSERIAL'; + } + + return 'BIGINT'; + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $column) + { + if (! empty($column['autoincrement'])) { + return 'SMALLSERIAL'; + } + + return 'SMALLINT'; + } + + /** + * {@inheritDoc} + */ + public function getGuidTypeDeclarationSQL(array $column) + { + return 'UUID'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $column) + { + return 'TIMESTAMP(0) WITHOUT TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $column) + { + return 'TIMESTAMP(0) WITH TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $column) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $column) + { + return 'TIME(0) WITHOUT TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $column) + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return 'BYTEA'; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $column) + { + return 'TEXT'; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'postgresql'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:sO'; + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)'; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + $tableIdentifier = new Identifier($tableName); + $sql = 'TRUNCATE ' . $tableIdentifier->getQuotedName($this); + + if ($cascade) { + $sql .= ' CASCADE'; + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + public function getReadLockSQL() + { + return 'FOR SHARE'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = [ + 'bigint' => 'bigint', + 'bigserial' => 'bigint', + 'bool' => 'boolean', + 'boolean' => 'boolean', + 'bpchar' => 'string', + 'bytea' => 'blob', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'decimal' => 'decimal', + 'double' => 'float', + 'double precision' => 'float', + 'float' => 'float', + 'float4' => 'float', + 'float8' => 'float', + 'inet' => 'string', + 'int' => 'integer', + 'int2' => 'smallint', + 'int4' => 'integer', + 'int8' => 'bigint', + 'integer' => 'integer', + 'interval' => 'string', + 'json' => 'json', + 'jsonb' => 'json', + 'money' => 'decimal', + 'numeric' => 'decimal', + 'serial' => 'integer', + 'serial4' => 'integer', + 'serial8' => 'bigint', + 'real' => 'float', + 'smallint' => 'smallint', + 'text' => 'text', + 'time' => 'time', + 'timestamp' => 'datetime', + 'timestamptz' => 'datetimetz', + 'timetz' => 'time', + 'tsvector' => 'text', + 'uuid' => 'guid', + 'varchar' => 'string', + 'year' => 'date', + '_varchar' => 'string', + ]; + } + + /** + * {@inheritDoc} + */ + public function getVarcharMaxLength() + { + return 65535; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 0; + } + + /** + * {@inheritdoc} + */ + public function getBinaryDefaultLength() + { + return 0; + } + + /** + * {@inheritdoc} + */ + public function hasNativeJsonType() + { + return true; + } + + /** + * {@inheritDoc} + * + * @deprecated Implement {@link createReservedKeywordsList()} instead. + */ + protected function getReservedKeywordsClass() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'PostgreSQL94Platform::getReservedKeywordsClass() is deprecated,' + . ' use PostgreSQL94Platform::createReservedKeywordsList() instead.' + ); + + return Keywords\PostgreSQL94Keywords::class; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $column) + { + return 'BYTEA'; + } + + /** + * {@inheritdoc} + */ + public function getDefaultValueDeclarationSQL($column) + { + if ($this->isSerialColumn($column)) { + return ''; + } + + return parent::getDefaultValueDeclarationSQL($column); + } + + /** + * {@inheritdoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getColumnCollationDeclarationSQL($collation) + { + return 'COLLATE ' . $this->quoteSingleIdentifier($collation); + } + + /** + * {@inheritdoc} + */ + public function getJsonTypeDeclarationSQL(array $column) + { + if (! empty($column['jsonb'])) { + return 'JSONB'; + } + + return 'JSON'; + } + + /** + * @param mixed[] $column + */ + private function isSerialColumn(array $column): bool + { + return isset($column['type'], $column['autoincrement']) + && $column['autoincrement'] === true + && $this->isNumericType($column['type']); + } + + /** + * Check whether the type of a column is changed in a way that invalidates the default value for the column + */ + private function typeChangeBreaksDefaultValue(ColumnDiff $columnDiff): bool + { + if ($columnDiff->fromColumn === null) { + return $columnDiff->hasChanged('type'); + } + + $oldTypeIsNumeric = $this->isNumericType($columnDiff->fromColumn->getType()); + $newTypeIsNumeric = $this->isNumericType($columnDiff->column->getType()); + + // default should not be changed when switching between numeric types and the default comes from a sequence + return $columnDiff->hasChanged('type') + && ! ($oldTypeIsNumeric && $newTypeIsNumeric && $columnDiff->column->getAutoincrement()); + } + + private function isNumericType(Type $type): bool + { + return $type instanceof IntegerType || $type instanceof BigIntType; + } + + private function getOldColumnComment(ColumnDiff $columnDiff): ?string + { + return $columnDiff->fromColumn !== null ? $this->getColumnComment($columnDiff->fromColumn) : null; + } + + public function getListTableMetadataSQL(string $table, ?string $schema = null): string + { + if ($schema !== null) { + $table = $schema . '.' . $table; + } + + return sprintf( + <<<'SQL' +SELECT obj_description(%s::regclass) AS table_comment; +SQL + , + $this->quoteStringLiteral($table) + ); + } +} diff --git a/app/vendor/doctrine/dbal/src/Platforms/SQLServer2012Platform.php b/app/vendor/doctrine/dbal/src/Platforms/SQLServer2012Platform.php new file mode 100644 index 000000000..d94784d6c --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Platforms/SQLServer2012Platform.php @@ -0,0 +1,1731 @@ +getConvertExpression('date', 'GETDATE()'); + } + + /** + * {@inheritdoc} + */ + public function getCurrentTimeSQL() + { + return $this->getConvertExpression('time', 'GETDATE()'); + } + + /** + * Returns an expression that converts an expression of one data type to another. + * + * @param string $dataType The target native data type. Alias data types cannot be used. + * @param string $expression The SQL expression to convert. + * + * @return string + */ + private function getConvertExpression($dataType, $expression) + { + return sprintf('CONVERT(%s, %s)', $dataType, $expression); + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $factorClause = ''; + + if ($operator === '-') { + $factorClause = '-1 * '; + } + + return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')'; + } + + /** + * {@inheritDoc} + * + * Microsoft SQL Server prefers "autoincrement" identity columns + * since sequences can only be emulated with a table. + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * Microsoft SQL Server supports this through AUTO_INCREMENT columns. + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function supportsSchemas() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getDefaultSchemaName() + { + return 'dbo'; + } + + /** + * {@inheritDoc} + */ + public function supportsColumnCollation() + { + return true; + } + + public function supportsSequences(): bool + { + return true; + } + + public function getAlterSequenceSQL(Sequence $sequence): string + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + public function getCreateSequenceSQL(Sequence $sequence): string + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' START WITH ' . $sequence->getInitialValue() . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' MINVALUE ' . $sequence->getInitialValue(); + } + + /** + * {@inheritdoc} + */ + public function getDropSequenceSQL($sequence): string + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * {@inheritdoc} + */ + public function getListSequencesSQL($database) + { + return 'SELECT seq.name, + CAST( + seq.increment AS VARCHAR(MAX) + ) AS increment, -- CAST avoids driver error for sql_variant type + CAST( + seq.start_value AS VARCHAR(MAX) + ) AS start_value -- CAST avoids driver error for sql_variant type + FROM sys.sequences AS seq'; + } + + /** + * {@inheritdoc} + */ + public function getSequenceNextValSQL($sequence) + { + return 'SELECT NEXT VALUE FOR ' . $sequence; + } + + /** + * {@inheritDoc} + */ + public function hasNativeGuidType() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function supportsCreateDropDatabase() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getCreateSchemaSQL($schemaName) + { + return 'CREATE SCHEMA ' . $schemaName; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if (! $foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = new Identifier($foreignKey); + } + + if (! $table instanceof Table) { + $table = new Identifier($table); + } + + $foreignKey = $foreignKey->getQuotedName($this); + $table = $table->getQuotedName($this); + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; + } + + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this); + } elseif (! is_string($index)) { + throw new InvalidArgumentException( + __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.' + ); + } + + if (! isset($table)) { + return 'DROP INDEX ' . $index; + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return sprintf( + <<getCommentOnTableSQL($name, $tableComment); + } + + // @todo does other code breaks because of this? + // force primary keys to be not null + foreach ($columns as &$column) { + if (! empty($column['primary'])) { + $column['notnull'] = true; + } + + // Build default constraints SQL statements. + if (isset($column['default'])) { + $defaultConstraintsSql[] = 'ALTER TABLE ' . $name . + ' ADD' . $this->getDefaultConstraintDeclarationSQL($name, $column); + } + + if (empty($column['comment']) && ! is_numeric($column['comment'])) { + continue; + } + + $commentsSql[] = $this->getCreateColumnCommentSQL($name, $column['name'], $column['comment']); + } + + $columnListSql = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $constraintName => $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition); + } + } + + if (isset($options['primary']) && ! empty($options['primary'])) { + $flags = ''; + if (isset($options['primary_index']) && $options['primary_index']->hasFlag('nonclustered')) { + $flags = ' NONCLUSTERED'; + } + + $columnListSql .= ', PRIMARY KEY' . $flags + . ' (' . implode(', ', array_unique(array_values($options['primary']))) . ')'; + } + + $query = 'CREATE TABLE ' . $name . ' (' . $columnListSql; + + $check = $this->getCheckDeclarationSQL($columns); + if (! empty($check)) { + $query .= ', ' . $check; + } + + $query .= ')'; + + $sql = [$query]; + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index) { + $sql[] = $this->getCreateIndexSQL($index, $name); + } + } + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $name); + } + } + + return array_merge($sql, $commentsSql, $defaultConstraintsSql); + } + + /** + * {@inheritDoc} + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + if ($table instanceof Table) { + $identifier = $table->getQuotedName($this); + } else { + $identifier = $table; + } + + $sql = 'ALTER TABLE ' . $identifier . ' ADD PRIMARY KEY'; + + if ($index->hasFlag('nonclustered')) { + $sql .= ' NONCLUSTERED'; + } + + return $sql . ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')'; + } + + /** + * Returns the SQL statement for creating a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to create the comment for. + * @param string|null $comment The column's comment. + * + * @return string + */ + protected function getCreateColumnCommentSQL($tableName, $columnName, $comment) + { + if (strpos($tableName, '.') !== false) { + [$schemaSQL, $tableSQL] = explode('.', $tableName); + $schemaSQL = $this->quoteStringLiteral($schemaSQL); + $tableSQL = $this->quoteStringLiteral($tableSQL); + } else { + $schemaSQL = "'dbo'"; + $tableSQL = $this->quoteStringLiteral($tableName); + } + + return $this->getAddExtendedPropertySQL( + 'MS_Description', + $comment, + 'SCHEMA', + $schemaSQL, + 'TABLE', + $tableSQL, + 'COLUMN', + $columnName + ); + } + + /** + * Returns the SQL snippet for declaring a default constraint. + * + * @param string $table Name of the table to return the default constraint declaration for. + * @param mixed[] $column Column definition. + * + * @return string + * + * @throws InvalidArgumentException + */ + public function getDefaultConstraintDeclarationSQL($table, array $column) + { + if (! isset($column['default'])) { + throw new InvalidArgumentException("Incomplete column definition. 'default' required."); + } + + $columnName = new Identifier($column['name']); + + return ' CONSTRAINT ' . + $this->generateDefaultConstraintName($table, $column['name']) . + $this->getDefaultValueDeclarationSQL($column) . + ' FOR ' . $columnName->getQuotedName($this); + } + + /** + * {@inheritDoc} + */ + public function getCreateIndexSQL(Index $index, $table) + { + $constraint = parent::getCreateIndexSQL($index, $table); + + if ($index->isUnique() && ! $index->isPrimary()) { + $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); + } + + return $constraint; + } + + /** + * {@inheritDoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } + + if ($index->hasFlag('clustered')) { + $type .= 'CLUSTERED '; + } elseif ($index->hasFlag('nonclustered')) { + $type .= 'NONCLUSTERED '; + } + + return $type; + } + + /** + * Extend unique key constraint with required filters + * + * @param string $sql + * + * @return string + */ + private function _appendUniqueConstraintDefinition($sql, Index $index) + { + $fields = []; + + foreach ($index->getQuotedColumns($this) as $field) { + $fields[] = $field . ' IS NOT NULL'; + } + + return $sql . ' WHERE ' . implode(' AND ', $fields); + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $queryParts = []; + $sql = []; + $columnSql = []; + $commentsSql = []; + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnDef = $column->toArray(); + $addColumnSql = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + if (isset($columnDef['default'])) { + $addColumnSql .= ' CONSTRAINT ' . + $this->generateDefaultConstraintName($diff->name, $column->getQuotedName($this)) . + $this->getDefaultValueDeclarationSQL($columnDef); + } + + $queryParts[] = $addColumnSql; + + $comment = $this->getColumnComment($column); + + if (empty($comment) && ! is_numeric($comment)) { + continue; + } + + $commentsSql[] = $this->getCreateColumnCommentSQL( + $diff->name, + $column->getQuotedName($this), + $comment + ); + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); + } + + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $column = $columnDiff->column; + $comment = $this->getColumnComment($column); + $hasComment = ! empty($comment) || is_numeric($comment); + + if ($columnDiff->fromColumn instanceof Column) { + $fromComment = $this->getColumnComment($columnDiff->fromColumn); + $hasFromComment = ! empty($fromComment) || is_numeric($fromComment); + + if ($hasFromComment && $hasComment && $fromComment !== $comment) { + $commentsSql[] = $this->getAlterColumnCommentSQL( + $diff->name, + $column->getQuotedName($this), + $comment + ); + } elseif ($hasFromComment && ! $hasComment) { + $commentsSql[] = $this->getDropColumnCommentSQL($diff->name, $column->getQuotedName($this)); + } elseif (! $hasFromComment && $hasComment) { + $commentsSql[] = $this->getCreateColumnCommentSQL( + $diff->name, + $column->getQuotedName($this), + $comment + ); + } + } + + // Do not add query part if only comment has changed. + if ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1) { + continue; + } + + $requireDropDefaultConstraint = $this->alterColumnRequiresDropDefaultConstraint($columnDiff); + + if ($requireDropDefaultConstraint) { + $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( + $diff->name, + $columnDiff->oldColumnName + ); + } + + $columnDef = $column->toArray(); + + $queryParts[] = 'ALTER COLUMN ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + + if ( + ! isset($columnDef['default']) + || (! $requireDropDefaultConstraint && ! $columnDiff->hasChanged('default')) + ) { + continue; + } + + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $sql[] = "sp_rename '" . + $diff->getName($this)->getQuotedName($this) . '.' . $oldColumnName->getQuotedName($this) . + "', '" . $column->getQuotedName($this) . "', 'COLUMN'"; + + // Recreate default constraint with new column name if necessary (for future reference). + if ($column->getDefault() === null) { + continue; + } + + $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( + $diff->name, + $oldColumnName->getQuotedName($this) + ); + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); + } + + $tableSql = []; + + if ($this->onSchemaAlterTable($diff, $tableSql)) { + return array_merge($tableSql, $columnSql); + } + + foreach ($queryParts as $query) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + $sql = array_merge($sql, $commentsSql); + + $newName = $diff->getNewName(); + + if ($newName !== false) { + $sql[] = "sp_rename '" . $diff->getName($this)->getQuotedName($this) . "', '" . $newName->getName() . "'"; + + /** + * Rename table's default constraints names + * to match the new table name. + * This is necessary to ensure that the default + * constraints can be referenced in future table + * alterations as the table name is encoded in + * default constraints' names. + */ + $sql[] = "DECLARE @sql NVARCHAR(MAX) = N''; " . + "SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' " . + "+ REPLACE(dc.name, '" . $this->generateIdentifierName($diff->name) . "', " . + "'" . $this->generateIdentifierName($newName->getName()) . "') + ''', ''OBJECT'';' " . + 'FROM sys.default_constraints dc ' . + 'JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id ' . + "WHERE tbl.name = '" . $newName->getName() . "';" . + 'EXEC sp_executesql @sql'; + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Returns the SQL clause for adding a default constraint in an ALTER TABLE statement. + * + * @param string $tableName The name of the table to generate the clause for. + * @param Column $column The column to generate the clause for. + * + * @return string + */ + private function getAlterTableAddDefaultConstraintClause($tableName, Column $column) + { + $columnDef = $column->toArray(); + $columnDef['name'] = $column->getQuotedName($this); + + return 'ADD' . $this->getDefaultConstraintDeclarationSQL($tableName, $columnDef); + } + + /** + * Returns the SQL clause for dropping an existing default constraint in an ALTER TABLE statement. + * + * @param string $tableName The name of the table to generate the clause for. + * @param string $columnName The name of the column to generate the clause for. + * + * @return string + */ + private function getAlterTableDropDefaultConstraintClause($tableName, $columnName) + { + return 'DROP CONSTRAINT ' . $this->generateDefaultConstraintName($tableName, $columnName); + } + + /** + * Checks whether a column alteration requires dropping its default constraint first. + * + * Different to other database vendors SQL Server implements column default values + * as constraints and therefore changes in a column's default value as well as changes + * in a column's type require dropping the default constraint first before being to + * alter the particular column to the new definition. + * + * @param ColumnDiff $columnDiff The column diff to evaluate. + * + * @return bool True if the column alteration requires dropping its default constraint first, false otherwise. + */ + private function alterColumnRequiresDropDefaultConstraint(ColumnDiff $columnDiff) + { + // We can only decide whether to drop an existing default constraint + // if we know the original default value. + if (! $columnDiff->fromColumn instanceof Column) { + return false; + } + + // We only need to drop an existing default constraint if we know the + // column was defined with a default value before. + if ($columnDiff->fromColumn->getDefault() === null) { + return false; + } + + // We need to drop an existing default constraint if the column was + // defined with a default value before and it has changed. + if ($columnDiff->hasChanged('default')) { + return true; + } + + // We need to drop an existing default constraint if the column was + // defined with a default value before and the native column type has changed. + return $columnDiff->hasChanged('type') || $columnDiff->hasChanged('fixed'); + } + + /** + * Returns the SQL statement for altering a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to alter the comment for. + * @param string|null $comment The column's comment. + * + * @return string + */ + protected function getAlterColumnCommentSQL($tableName, $columnName, $comment) + { + if (strpos($tableName, '.') !== false) { + [$schemaSQL, $tableSQL] = explode('.', $tableName); + $schemaSQL = $this->quoteStringLiteral($schemaSQL); + $tableSQL = $this->quoteStringLiteral($tableSQL); + } else { + $schemaSQL = "'dbo'"; + $tableSQL = $this->quoteStringLiteral($tableName); + } + + return $this->getUpdateExtendedPropertySQL( + 'MS_Description', + $comment, + 'SCHEMA', + $schemaSQL, + 'TABLE', + $tableSQL, + 'COLUMN', + $columnName + ); + } + + /** + * Returns the SQL statement for dropping a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to drop the comment for. + * + * @return string + */ + protected function getDropColumnCommentSQL($tableName, $columnName) + { + if (strpos($tableName, '.') !== false) { + [$schemaSQL, $tableSQL] = explode('.', $tableName); + $schemaSQL = $this->quoteStringLiteral($schemaSQL); + $tableSQL = $this->quoteStringLiteral($tableSQL); + } else { + $schemaSQL = "'dbo'"; + $tableSQL = $this->quoteStringLiteral($tableName); + } + + return $this->getDropExtendedPropertySQL( + 'MS_Description', + 'SCHEMA', + $schemaSQL, + 'TABLE', + $tableSQL, + 'COLUMN', + $columnName + ); + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + return [sprintf( + "EXEC sp_rename N'%s.%s', N'%s', N'INDEX'", + $tableName, + $oldIndexName, + $index->getQuotedName($this) + ), + ]; + } + + /** + * Returns the SQL statement for adding an extended property to a database object. + * + * @link http://msdn.microsoft.com/en-us/library/ms180047%28v=sql.90%29.aspx + * + * @param string $name The name of the property to add. + * @param string|null $value The value of the property to add. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + */ + public function getAddExtendedPropertySQL( + $name, + $value = null, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return 'EXEC sp_addextendedproperty ' . + 'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value) . ', ' . + 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . + 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . + 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; + } + + /** + * Returns the SQL statement for dropping an extended property from a database object. + * + * @link http://technet.microsoft.com/en-gb/library/ms178595%28v=sql.90%29.aspx + * + * @param string $name The name of the property to drop. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + */ + public function getDropExtendedPropertySQL( + $name, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return 'EXEC sp_dropextendedproperty ' . + 'N' . $this->quoteStringLiteral($name) . ', ' . + 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . + 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . + 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; + } + + /** + * Returns the SQL statement for updating an extended property of a database object. + * + * @link http://msdn.microsoft.com/en-us/library/ms186885%28v=sql.90%29.aspx + * + * @param string $name The name of the property to update. + * @param string|null $value The value of the property to update. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + */ + public function getUpdateExtendedPropertySQL( + $name, + $value = null, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return 'EXEC sp_updateextendedproperty ' . + 'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value) . ', ' . + 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . + 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . + 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES'; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + // "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams + // Category 2 must be ignored as it is "MS SQL Server 'pseudo-system' object[s]" for replication + return 'SELECT name, SCHEMA_NAME (uid) AS schema_name FROM sysobjects' + . " WHERE type = 'U' AND name != 'sysdiagrams' AND category != 2 ORDER BY name"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT col.name, + type.name AS type, + col.max_length AS length, + ~col.is_nullable AS notnull, + def.definition AS [default], + col.scale, + col.precision, + col.is_identity AS autoincrement, + col.collation_name AS collation, + CAST(prop.value AS NVARCHAR(MAX)) AS comment -- CAST avoids driver error for sql_variant type + FROM sys.columns AS col + JOIN sys.types AS type + ON col.user_type_id = type.user_type_id + JOIN sys.objects AS obj + ON col.object_id = obj.object_id + JOIN sys.schemas AS scm + ON obj.schema_id = scm.schema_id + LEFT JOIN sys.default_constraints def + ON col.default_object_id = def.object_id + AND col.object_id = def.parent_object_id + LEFT JOIN sys.extended_properties AS prop + ON obj.object_id = prop.major_id + AND col.column_id = prop.minor_id + AND prop.name = 'MS_Description' + WHERE obj.type = 'U' + AND " . $this->getTableWhereClause($table, 'scm.name', 'obj.name'); + } + + /** + * @param string $table + * @param string|null $database + * + * @return string + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + return 'SELECT f.name AS ForeignKey, + SCHEMA_NAME (f.SCHEMA_ID) AS SchemaName, + OBJECT_NAME (f.parent_object_id) AS TableName, + COL_NAME (fc.parent_object_id,fc.parent_column_id) AS ColumnName, + SCHEMA_NAME (o.SCHEMA_ID) ReferenceSchemaName, + OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName, + COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS ReferenceColumnName, + f.delete_referential_action_desc, + f.update_referential_action_desc + FROM sys.foreign_keys AS f + INNER JOIN sys.foreign_key_columns AS fc + INNER JOIN sys.objects AS o ON o.OBJECT_ID = fc.referenced_object_id + ON f.OBJECT_ID = fc.constraint_object_id + WHERE ' . + $this->getTableWhereClause($table, 'SCHEMA_NAME (f.schema_id)', 'OBJECT_NAME (f.parent_object_id)'); + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $database = null) + { + return "SELECT idx.name AS key_name, + col.name AS column_name, + ~idx.is_unique AS non_unique, + idx.is_primary_key AS [primary], + CASE idx.type + WHEN '1' THEN 'clustered' + WHEN '2' THEN 'nonclustered' + ELSE NULL + END AS flags + FROM sys.tables AS tbl + JOIN sys.schemas AS scm ON tbl.schema_id = scm.schema_id + JOIN sys.indexes AS idx ON tbl.object_id = idx.object_id + JOIN sys.index_columns AS idxcol ON idx.object_id = idxcol.object_id AND idx.index_id = idxcol.index_id + JOIN sys.columns AS col ON idxcol.object_id = col.object_id AND idxcol.column_id = col.column_id + WHERE " . $this->getTableWhereClause($table, 'scm.name', 'tbl.name') . ' + ORDER BY idx.index_id ASC, idxcol.key_ordinal ASC'; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT name FROM sysobjects WHERE type = 'V' ORDER BY name"; + } + + /** + * Returns the where clause to filter schema and table name in a query. + * + * @param string $table The full qualified name of the table. + * @param string $schemaColumn The name of the column to compare the schema to in the where clause. + * @param string $tableColumn The name of the column to compare the table to in the where clause. + * + * @return string + */ + private function getTableWhereClause($table, $schemaColumn, $tableColumn) + { + if (strpos($table, '.') !== false) { + [$schema, $table] = explode('.', $table); + $schema = $this->quoteStringLiteral($schema); + $table = $this->quoteStringLiteral($table); + } else { + $schema = 'SCHEMA_NAME()'; + $table = $this->quoteStringLiteral($table); + } + + return sprintf('(%s = %s AND %s = %s)', $tableColumn, $table, $schemaColumn, $schema); + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos === false) { + return 'CHARINDEX(' . $substr . ', ' . $str . ')'; + } + + return 'CHARINDEX(' . $substr . ', ' . $str . ', ' . $startPos . ')'; + } + + /** + * {@inheritDoc} + */ + public function getModExpression($expression1, $expression2) + { + return $expression1 . ' % ' . $expression2; + } + + /** + * {@inheritDoc} + */ + public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false) + { + if ($char === false) { + switch ($mode) { + case TrimMode::LEADING: + $trimFn = 'LTRIM'; + break; + + case TrimMode::TRAILING: + $trimFn = 'RTRIM'; + break; + + default: + return 'LTRIM(RTRIM(' . $str . '))'; + } + + return $trimFn . '(' . $str . ')'; + } + + $pattern = "'%[^' + " . $char . " + ']%'"; + + if ($mode === TrimMode::LEADING) { + return 'stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)'; + } + + if ($mode === TrimMode::TRAILING) { + return 'reverse(stuff(reverse(' . $str . '), 1, ' + . 'patindex(' . $pattern . ', reverse(' . $str . ')) - 1, null))'; + } + + return 'reverse(stuff(reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)), 1, ' + . 'patindex(' . $pattern . ', reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str + . ') - 1, null))) - 1, null))'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + $args = func_get_args(); + + return '(' . implode(' + ', $args) . ')'; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SELECT * FROM sys.databases'; + } + + /** + * {@inheritDoc} + * + * @deprecated Use {@link SQLServerSchemaManager::listSchemaNames()} instead. + */ + public function getListNamespacesSQL() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'SQLServer2012Platform::getListNamespacesSQL() is deprecated,' + . ' use SQLServerSchemaManager::listSchemaNames() instead.' + ); + + return "SELECT name FROM sys.schemas WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys')"; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($string, $start, $length = null) + { + if ($length !== null) { + return 'SUBSTRING(' . $string . ', ' . $start . ', ' . $length . ')'; + } + + return 'SUBSTRING(' . $string . ', ' . $start . ', LEN(' . $string . ') - ' . $start . ' + 1)'; + } + + /** + * {@inheritDoc} + */ + public function getLengthExpression($column) + { + return 'LEN(' . $column . ')'; + } + + public function getCurrentDatabaseExpression(): string + { + return 'DB_NAME()'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $column) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $column) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $column) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritDoc} + */ + public function getGuidTypeDeclarationSQL(array $column) + { + return 'UNIQUEIDENTIFIER'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $column) + { + return 'DATETIMEOFFSET(6)'; + } + + /** + * {@inheritDoc} + */ + public function getAsciiStringTypeDeclarationSQL(array $column): string + { + $length = $column['length'] ?? null; + + if (! isset($column['fixed'])) { + return sprintf('VARCHAR(%d)', $length ?? 255); + } + + return sprintf('CHAR(%d)', $length ?? 255); + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed + ? ($length > 0 ? 'NCHAR(' . $length . ')' : 'CHAR(255)') + : ($length > 0 ? 'NVARCHAR(' . $length . ')' : 'NVARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed + ? 'BINARY(' . ($length > 0 ? $length : 255) . ')' + : 'VARBINARY(' . ($length > 0 ? $length : 255) . ')'; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 8000; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $column) + { + return 'VARCHAR(MAX)'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $column) + { + return ! empty($column['autoincrement']) ? ' IDENTITY' : ''; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $column) + { + // 3 - microseconds precision length + // http://msdn.microsoft.com/en-us/library/ms187819.aspx + return 'DATETIME2(6)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $column) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $column) + { + return 'TIME(0)'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $column) + { + return 'BIT'; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if ($limit === null && $offset <= 0) { + return $query; + } + + if ($this->shouldAddOrderBy($query)) { + if (preg_match('/^SELECT\s+DISTINCT/im', $query) > 0) { + // SQL Server won't let us order by a non-selected column in a DISTINCT query, + // so we have to do this madness. This says, order by the first column in the + // result. SQL Server's docs say that a nonordered query's result order is non- + // deterministic anyway, so this won't do anything that a bunch of update and + // deletes to the table wouldn't do anyway. + $query .= ' ORDER BY 1'; + } else { + // In another DBMS, we could do ORDER BY 0, but SQL Server gets angry if you + // use constant expressions in the order by list. + $query .= ' ORDER BY (SELECT 0)'; + } + } + + // This looks somewhat like MYSQL, but limit/offset are in inverse positions + // Supposedly SQL:2008 core standard. + // Per TSQL spec, FETCH NEXT n ROWS ONLY is not valid without OFFSET n ROWS. + $query .= sprintf(' OFFSET %d ROWS', $offset); + + if ($limit !== null) { + $query .= sprintf(' FETCH NEXT %d ROWS ONLY', $limit); + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function supportsLimitOffset() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + if (! is_bool($value) && ! is_numeric($value)) { + continue; + } + + $item[$key] = (int) (bool) $value; + } + } elseif (is_bool($item) || is_numeric($item)) { + $item = (int) (bool) $item; + } + + return $item; + } + + /** + * {@inheritDoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return 'CREATE TABLE'; + } + + /** + * {@inheritDoc} + */ + public function getTemporaryTableName($tableName) + { + return '#' . $tableName; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.u'; + } + + /** + * {@inheritDoc} + */ + public function getDateFormatString() + { + return 'Y-m-d'; + } + + /** + * {@inheritDoc} + */ + public function getTimeFormatString() + { + return 'H:i:s'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:s.u P'; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'mssql'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = [ + 'bigint' => 'bigint', + 'binary' => 'binary', + 'bit' => 'boolean', + 'blob' => 'blob', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'datetime2' => 'datetime', + 'datetimeoffset' => 'datetimetz', + 'decimal' => 'decimal', + 'double' => 'float', + 'double precision' => 'float', + 'float' => 'float', + 'image' => 'blob', + 'int' => 'integer', + 'money' => 'integer', + 'nchar' => 'string', + 'ntext' => 'text', + 'numeric' => 'decimal', + 'nvarchar' => 'string', + 'real' => 'float', + 'smalldatetime' => 'datetime', + 'smallint' => 'smallint', + 'smallmoney' => 'integer', + 'text' => 'text', + 'time' => 'time', + 'tinyint' => 'smallint', + 'uniqueidentifier' => 'guid', + 'varbinary' => 'binary', + 'varchar' => 'string', + ]; + } + + /** + * {@inheritDoc} + */ + public function createSavePoint($savepoint) + { + return 'SAVE TRANSACTION ' . $savepoint; + } + + /** + * {@inheritDoc} + */ + public function releaseSavePoint($savepoint) + { + return ''; + } + + /** + * {@inheritDoc} + */ + public function rollbackSavePoint($savepoint) + { + return 'ROLLBACK TRANSACTION ' . $savepoint; + } + + /** + * {@inheritdoc} + */ + public function getForeignKeyReferentialActionSQL($action) + { + // RESTRICT is not supported, therefore falling back to NO ACTION. + if (strtoupper($action) === 'RESTRICT') { + return 'NO ACTION'; + } + + return parent::getForeignKeyReferentialActionSQL($action); + } + + public function appendLockHint(string $fromClause, int $lockMode): string + { + switch ($lockMode) { + case LockMode::NONE: + case LockMode::OPTIMISTIC: + return $fromClause; + + case LockMode::PESSIMISTIC_READ: + return $fromClause . ' WITH (HOLDLOCK, ROWLOCK)'; + + case LockMode::PESSIMISTIC_WRITE: + return $fromClause . ' WITH (UPDLOCK, ROWLOCK)'; + + default: + throw InvalidLockMode::fromLockMode($lockMode); + } + } + + /** + * {@inheritDoc} + */ + public function getForUpdateSQL() + { + return ' '; + } + + /** + * {@inheritDoc} + * + * @deprecated Implement {@link createReservedKeywordsList()} instead. + */ + protected function getReservedKeywordsClass() + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'QLServer2012Platform::getReservedKeywordsClass() is deprecated,' + . ' use QLServer2012Platform::createReservedKeywordsList() instead.' + ); + + return Keywords\SQLServer2012Keywords::class; + } + + /** + * {@inheritDoc} + */ + public function quoteSingleIdentifier($str) + { + return '[' . str_replace(']', ']]', $str) . ']'; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + $tableIdentifier = new Identifier($tableName); + + return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this); + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $column) + { + return 'VARBINARY(MAX)'; + } + + /** + * {@inheritdoc} + * + * Modifies column declaration order as it differs in Microsoft SQL Server. + */ + public function getColumnDeclarationSQL($name, array $column) + { + if (isset($column['columnDefinition'])) { + $columnDef = $this->getCustomTypeDeclarationSQL($column); + } else { + $collation = ! empty($column['collation']) ? + ' ' . $this->getColumnCollationDeclarationSQL($column['collation']) : ''; + + $notnull = ! empty($column['notnull']) ? ' NOT NULL' : ''; + + $unique = ! empty($column['unique']) ? + ' ' . $this->getUniqueFieldDeclarationSQL() : ''; + + $check = ! empty($column['check']) ? + ' ' . $column['check'] : ''; + + $typeDecl = $column['type']->getSQLDeclaration($column, $this); + $columnDef = $typeDecl . $collation . $notnull . $unique . $check; + } + + return $name . ' ' . $columnDef; + } + + protected function getLikeWildcardCharacters(): string + { + return parent::getLikeWildcardCharacters() . '[]^'; + } + + /** + * Returns a unique default constraint name for a table and column. + * + * @param string $table Name of the table to generate the unique default constraint name for. + * @param string $column Name of the column in the table to generate the unique default constraint name for. + * + * @return string + */ + private function generateDefaultConstraintName($table, $column) + { + return 'DF_' . $this->generateIdentifierName($table) . '_' . $this->generateIdentifierName($column); + } + + /** + * Returns a hash value for a given identifier. + * + * @param string $identifier Identifier to generate a hash value for. + * + * @return string + */ + private function generateIdentifierName($identifier) + { + // Always generate name for unquoted identifiers to ensure consistency. + $identifier = new Identifier($identifier); + + return strtoupper(dechex(crc32($identifier->getName()))); + } + + protected function getCommentOnTableSQL(string $tableName, ?string $comment): string + { + return sprintf( + <<<'SQL' + EXEC sys.sp_addextendedproperty @name=N'MS_Description', + @value=N%s, @level0type=N'SCHEMA', @level0name=N'dbo', + @level1type=N'TABLE', @level1name=N%s + SQL + , + $this->quoteStringLiteral((string) $comment), + $this->quoteStringLiteral($tableName) + ); + } + + public function getListTableMetadataSQL(string $table): string + { + return sprintf( + <<<'SQL' + SELECT + p.value AS [table_comment] + FROM + sys.tables AS tbl + INNER JOIN sys.extended_properties AS p ON p.major_id=tbl.object_id AND p.minor_id=0 AND p.class=1 + WHERE + (tbl.name=N%s and SCHEMA_NAME(tbl.schema_id)=N'dbo' and p.name=N'MS_Description') + SQL + , + $this->quoteStringLiteral($table) + ); + } + + /** + * @param string $query + */ + private function shouldAddOrderBy($query): bool + { + // Find the position of the last instance of ORDER BY and ensure it is not within a parenthetical statement + // but can be in a newline + $matches = []; + $matchesCount = preg_match_all('/[\\s]+order\\s+by\\s/im', $query, $matches, PREG_OFFSET_CAPTURE); + if ($matchesCount === 0) { + return true; + } + + // ORDER BY instance may be in a subquery after ORDER BY + // e.g. SELECT col1 FROM test ORDER BY (SELECT col2 from test ORDER BY col2) + // if in the searched query ORDER BY clause was found where + // number of open parentheses after the occurrence of the clause is equal to + // number of closed brackets after the occurrence of the clause, + // it means that ORDER BY is included in the query being checked + while ($matchesCount > 0) { + $orderByPos = $matches[0][--$matchesCount][1]; + $openBracketsCount = substr_count($query, '(', $orderByPos); + $closedBracketsCount = substr_count($query, ')', $orderByPos); + if ($openBracketsCount === $closedBracketsCount) { + return false; + } + } + + return true; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php b/app/vendor/doctrine/dbal/src/Platforms/SqlitePlatform.php similarity index 92% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php rename to app/vendor/doctrine/dbal/src/Platforms/SqlitePlatform.php index e42a6bd89..5dc1747cc 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php +++ b/app/vendor/doctrine/dbal/src/Platforms/SqlitePlatform.php @@ -8,12 +8,17 @@ use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\Identifier; use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\SchemaException; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\TransactionIsolationLevel; use Doctrine\DBAL\Types; +use Doctrine\Deprecations\Deprecation; +use function array_combine; +use function array_keys; use function array_merge; +use function array_search; use function array_unique; use function array_values; use function implode; @@ -42,19 +47,6 @@ public function getRegexpExpression() return 'REGEXP'; } - /** - * {@inheritDoc} - * - * @deprecated Use application-generated UUIDs instead - */ - public function getGuidExpression() - { - return "HEX(RANDOMBLOB(4)) || '-' || HEX(RANDOMBLOB(2)) || '-4' || " - . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || " - . "SUBSTR('89AB', 1 + (ABS(RANDOM()) % 4), 1) || " - . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || HEX(RANDOMBLOB(6))"; - } - /** * @param string $type * @@ -163,6 +155,19 @@ public function getDateDiffExpression($date1, $date2) return sprintf("JULIANDAY(%s, 'start of day') - JULIANDAY(%s, 'start of day')", $date1, $date2); } + /** + * {@inheritDoc} + * + * The SQLite platform doesn't support the concept of a database, therefore, it always returns an empty string + * as an indicator of an implicitly selected database. + * + * @see \Doctrine\DBAL\Connection::getDatabase() + */ + public function getCurrentDatabaseExpression(): string + { + return "''"; + } + /** * {@inheritDoc} */ @@ -232,7 +237,7 @@ public function getBigIntTypeDeclarationSQL(array $column) * * @return string */ - public function getTinyIntTypeDeclarationSql(array $column) + public function getTinyIntTypeDeclarationSQL(array $column) { // SQLite autoincrement is implicit for INTEGER PKs, but not for TINYINT columns if (! empty($column['autoincrement'])) { @@ -260,7 +265,7 @@ public function getSmallIntTypeDeclarationSQL(array $column) * * @return string */ - public function getMediumIntTypeDeclarationSql(array $column) + public function getMediumIntTypeDeclarationSQL(array $column) { // SQLite autoincrement is implicit for INTEGER PKs, but not for MEDIUMINT columns if (! empty($column['autoincrement'])) { @@ -330,8 +335,8 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] $queryFields = $this->getColumnDeclarationListSQL($columns); if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { - foreach ($options['uniqueConstraints'] as $name => $definition) { - $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + foreach ($options['uniqueConstraints'] as $constraintName => $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition); } } @@ -399,8 +404,8 @@ private function getNonAutoincrementPrimaryKeyDefinition(array $columns, array $ */ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) { - return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') - : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT'); + return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'TEXT'); } /** @@ -648,46 +653,55 @@ private function getInlineTableCommentSQL(string $comment): string protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'boolean' => 'boolean', - 'tinyint' => 'boolean', - 'smallint' => 'smallint', - 'mediumint' => 'integer', - 'int' => 'integer', - 'integer' => 'integer', - 'serial' => 'integer', 'bigint' => 'bigint', 'bigserial' => 'bigint', - 'clob' => 'text', - 'tinytext' => 'text', - 'mediumtext' => 'text', - 'longtext' => 'text', - 'text' => 'text', - 'varchar' => 'string', - 'longvarchar' => 'string', - 'varchar2' => 'string', - 'nvarchar' => 'string', - 'image' => 'string', - 'ntext' => 'string', + 'blob' => 'blob', + 'boolean' => 'boolean', 'char' => 'string', + 'clob' => 'text', 'date' => 'date', 'datetime' => 'datetime', - 'timestamp' => 'datetime', - 'time' => 'time', - 'float' => 'float', + 'decimal' => 'decimal', 'double' => 'float', 'double precision' => 'float', - 'real' => 'float', - 'decimal' => 'decimal', + 'float' => 'float', + 'image' => 'string', + 'int' => 'integer', + 'integer' => 'integer', + 'longtext' => 'text', + 'longvarchar' => 'string', + 'mediumint' => 'integer', + 'mediumtext' => 'text', + 'ntext' => 'string', 'numeric' => 'decimal', - 'blob' => 'blob', + 'nvarchar' => 'string', + 'real' => 'float', + 'serial' => 'integer', + 'smallint' => 'smallint', + 'text' => 'text', + 'time' => 'time', + 'timestamp' => 'datetime', + 'tinyint' => 'boolean', + 'tinytext' => 'text', + 'varchar' => 'string', + 'varchar2' => 'string', ]; } /** * {@inheritDoc} + * + * @deprecated Implement {@link createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'SqlitePlatform::getReservedKeywordsClass() is deprecated,' + . ' use SqlitePlatform::createReservedKeywordsList() instead.' + ); + return Keywords\SQLiteKeywords::class; } @@ -906,12 +920,7 @@ public function getAlterTableSQL(TableDiff $diff) continue; } - $oldColumnName = strtolower($oldColumnName); - if (isset($columns[$oldColumnName])) { - unset($columns[$oldColumnName]); - } - - $columns[strtolower($column->getName())] = $column; + $columns = $this->replaceColumn($diff->name, $columns, $oldColumnName, $column); if (! isset($newColumnNames[$oldColumnName])) { continue; @@ -925,11 +934,7 @@ public function getAlterTableSQL(TableDiff $diff) continue; } - if (isset($columns[$oldColumnName])) { - unset($columns[$oldColumnName]); - } - - $columns[strtolower($columnDiff->column->getName())] = $columnDiff->column; + $columns = $this->replaceColumn($diff->name, $columns, $oldColumnName, $columnDiff->column); if (! isset($newColumnNames[$oldColumnName])) { continue; @@ -955,8 +960,8 @@ public function getAlterTableSQL(TableDiff $diff) $table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff, $fromTable), + [], $this->getForeignKeysInAlteredTable($diff, $fromTable), - 0, $table->getOptions() ); $newTable->addOption('alter', true); @@ -997,16 +1002,45 @@ public function getAlterTableSQL(TableDiff $diff) return array_merge($sql, $tableSql, $columnSql); } + /** + * Replace the column with the given name with the new column. + * + * @param string $tableName + * @param array $columns + * @param string $columnName + * + * @return array + * + * @throws Exception + */ + private function replaceColumn($tableName, array $columns, $columnName, Column $column): array + { + $keys = array_keys($columns); + $index = array_search(strtolower($columnName), $keys, true); + + if ($index === false) { + throw SchemaException::columnDoesNotExist($columnName, $tableName); + } + + $values = array_values($columns); + + $keys[$index] = strtolower($column->getName()); + $values[$index] = $column; + + return array_combine($keys, $values); + } + /** * @return string[]|false + * + * @throws Exception */ private function getSimpleAlterTableSQL(TableDiff $diff) { // Suppress changes on integer type autoincrement columns. foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { if ( - ! $columnDiff->fromColumn instanceof Column || - ! $columnDiff->column instanceof Column || + $columnDiff->fromColumn === null || ! $columnDiff->column->getAutoincrement() || ! $columnDiff->column->getType() instanceof Types\IntegerType ) { @@ -1180,7 +1214,7 @@ private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable) foreach ($diff->removedIndexes as $index) { $indexName = strtolower($index->getName()); - if (! strlen($indexName) || ! isset($indexes[$indexName])) { + if (strlen($indexName) === 0 || ! isset($indexes[$indexName])) { continue; } @@ -1189,7 +1223,7 @@ private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable) foreach (array_merge($diff->changedIndexes, $diff->addedIndexes, $diff->renamedIndexes) as $index) { $indexName = strtolower($index->getName()); - if (strlen($indexName)) { + if (strlen($indexName) > 0) { $indexes[$indexName] = $index; } else { $indexes[] = $index; @@ -1244,7 +1278,7 @@ private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable) } $constraintName = strtolower($constraint->getName()); - if (! strlen($constraintName) || ! isset($foreignKeys[$constraintName])) { + if (strlen($constraintName) === 0 || ! isset($foreignKeys[$constraintName])) { continue; } @@ -1253,7 +1287,7 @@ private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable) foreach (array_merge($diff->changedForeignKeys, $diff->addedForeignKeys) as $constraint) { $constraintName = strtolower($constraint->getName()); - if (strlen($constraintName)) { + if (strlen($constraintName) > 0) { $foreignKeys[$constraintName] = $constraint; } else { $foreignKeys[] = $constraint; diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/TrimMode.php b/app/vendor/doctrine/dbal/src/Platforms/TrimMode.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/TrimMode.php rename to app/vendor/doctrine/dbal/src/Platforms/TrimMode.php diff --git a/app/vendor/doctrine/dbal/src/Portability/Connection.php b/app/vendor/doctrine/dbal/src/Portability/Connection.php new file mode 100644 index 000000000..a56397924 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Portability/Connection.php @@ -0,0 +1,93 @@ +connection = $connection; + $this->converter = $converter; + } + + public function prepare(string $sql): DriverStatement + { + return new Statement( + $this->connection->prepare($sql), + $this->converter + ); + } + + public function query(string $sql): DriverResult + { + return new Result( + $this->connection->query($sql), + $this->converter + ); + } + + /** + * {@inheritDoc} + */ + public function quote($value, $type = ParameterType::STRING) + { + return $this->connection->quote($value, $type); + } + + public function exec(string $sql): int + { + return $this->connection->exec($sql); + } + + /** + * {@inheritDoc} + */ + public function lastInsertId($name = null) + { + return $this->connection->lastInsertId($name); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + return $this->connection->beginTransaction(); + } + + /** + * {@inheritDoc} + */ + public function commit() + { + return $this->connection->commit(); + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + return $this->connection->rollBack(); + } +} diff --git a/app/vendor/doctrine/dbal/src/Portability/Converter.php b/app/vendor/doctrine/dbal/src/Portability/Converter.php new file mode 100644 index 000000000..5763c2603 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Portability/Converter.php @@ -0,0 +1,292 @@ +createConvertValue($convertEmptyStringToNull, $rightTrimString); + $convertNumeric = $this->createConvertRow($convertValue, null); + $convertAssociative = $this->createConvertRow($convertValue, $case); + + $this->convertNumeric = $this->createConvert($convertNumeric, [self::class, 'id']); + $this->convertAssociative = $this->createConvert($convertAssociative, [self::class, 'id']); + $this->convertOne = $this->createConvert($convertValue, [self::class, 'id']); + + $this->convertAllNumeric = $this->createConvertAll($convertNumeric, [self::class, 'id']); + $this->convertAllAssociative = $this->createConvertAll($convertAssociative, [self::class, 'id']); + $this->convertFirstColumn = $this->createConvertAll($convertValue, [self::class, 'id']); + } + + /** + * @param array|false $row + * + * @return list|false + */ + public function convertNumeric($row) + { + return ($this->convertNumeric)($row); + } + + /** + * @param array|false $row + * + * @return array|false + */ + public function convertAssociative($row) + { + return ($this->convertAssociative)($row); + } + + /** + * @param mixed|false $value + * + * @return mixed|false + */ + public function convertOne($value) + { + return ($this->convertOne)($value); + } + + /** + * @param list> $data + * + * @return list> + */ + public function convertAllNumeric(array $data): array + { + return ($this->convertAllNumeric)($data); + } + + /** + * @param list> $data + * + * @return list> + */ + public function convertAllAssociative(array $data): array + { + return ($this->convertAllAssociative)($data); + } + + /** + * @param list $data + * + * @return list + */ + public function convertFirstColumn(array $data): array + { + return ($this->convertFirstColumn)($data); + } + + /** + * @param T $value + * + * @return T + * + * @template T + */ + private static function id($value) + { + return $value; + } + + /** + * @param T $value + * + * @return T|null + * + * @template T + */ + private static function convertEmptyStringToNull($value) + { + if ($value === '') { + return null; + } + + return $value; + } + + /** + * @param T $value + * + * @return T|string + * @psalm-return (T is string ? string : T) + * + * @template T + */ + private static function rightTrimString($value) + { + if (! is_string($value)) { + return $value; + } + + return rtrim($value); + } + + /** + * Creates a function that will convert each individual value retrieved from the database + * + * @param bool $convertEmptyStringToNull Whether each empty string should be converted to NULL + * @param bool $rightTrimString Whether each string should right-trimmed + * + * @return callable|null The resulting function or NULL if no conversion is needed + */ + private function createConvertValue(bool $convertEmptyStringToNull, bool $rightTrimString): ?callable + { + $functions = []; + + if ($convertEmptyStringToNull) { + $functions[] = [self::class, 'convertEmptyStringToNull']; + } + + if ($rightTrimString) { + $functions[] = [self::class, 'rightTrimString']; + } + + return $this->compose(...$functions); + } + + /** + * Creates a function that will convert each array-row retrieved from the database + * + * @param callable|null $function The function that will convert each value + * @param int|null $case Column name case + * + * @return callable|null The resulting function or NULL if no conversion is needed + */ + private function createConvertRow(?callable $function, ?int $case): ?callable + { + $functions = []; + + if ($function !== null) { + $functions[] = $this->createMapper($function); + } + + if ($case !== null) { + $functions[] = static function (array $row) use ($case): array { + return array_change_key_case($row, $case); + }; + } + + return $this->compose(...$functions); + } + + /** + * Creates a function that will be applied to the return value of Statement::fetch*() + * or an identity function if no conversion is needed + * + * @param callable|null $function The function that will convert each tow + * @param callable $id Identity function + */ + private function createConvert(?callable $function, callable $id): callable + { + if ($function === null) { + return $id; + } + + return /** + * @param T $value + * + * @psalm-return (T is false ? false : T) + * + * @template T + */ + static function ($value) use ($function) { + if ($value === false) { + return false; + } + + return $function($value); + }; + } + + /** + * Creates a function that will be applied to the return value of Statement::fetchAll*() + * or an identity function if no transformation is required + * + * @param callable|null $function The function that will transform each value + * @param callable $id Identity function + */ + private function createConvertAll(?callable $function, callable $id): callable + { + if ($function === null) { + return $id; + } + + return $this->createMapper($function); + } + + /** + * Creates a function that maps each value of the array using the given function + * + * @param callable $function The function that maps each value of the array + */ + private function createMapper(callable $function): callable + { + return static function (array $array) use ($function): array { + return array_map($function, $array); + }; + } + + /** + * Creates a composition of the given set of functions + * + * @param callable(T):T ...$functions The functions to compose + * + * @return callable(T):T|null + * + * @template T + */ + private function compose(callable ...$functions): ?callable + { + return array_reduce($functions, static function (?callable $carry, callable $item): callable { + if ($carry === null) { + return $item; + } + + return /** + * @param T $value + * + * @return T + * + * @template T + */ + static function ($value) use ($carry, $item) { + return $item($carry($value)); + }; + }); + } +} diff --git a/app/vendor/doctrine/dbal/src/Portability/Driver.php b/app/vendor/doctrine/dbal/src/Portability/Driver.php new file mode 100644 index 000000000..5ae410cd0 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Portability/Driver.php @@ -0,0 +1,90 @@ +driver = $driver; + $this->mode = $mode; + $this->case = $case; + } + + /** + * {@inheritDoc} + */ + public function connect(array $params) + { + $connection = $this->driver->connect($params); + + $portability = (new OptimizeFlags())( + $this->getDatabasePlatform(), + $this->mode + ); + + $case = 0; + + if ($this->case !== 0 && ($portability & Connection::PORTABILITY_FIX_CASE) !== 0) { + if ($connection instanceof PDO\Connection) { + // make use of c-level support for case handling + $portability &= ~Connection::PORTABILITY_FIX_CASE; + $connection->getWrappedConnection()->setAttribute(\PDO::ATTR_CASE, $this->case); + } else { + $case = $this->case === ColumnCase::LOWER ? CASE_LOWER : CASE_UPPER; + } + } + + $convertEmptyStringToNull = ($portability & Connection::PORTABILITY_EMPTY_TO_NULL) !== 0; + $rightTrimString = ($portability & Connection::PORTABILITY_RTRIM) !== 0; + + if (! $convertEmptyStringToNull && ! $rightTrimString && $case === 0) { + return $connection; + } + + return new Connection( + $connection, + new Converter($convertEmptyStringToNull, $rightTrimString, $case) + ); + } + + /** + * {@inheritDoc} + */ + public function getDatabasePlatform() + { + return $this->driver->getDatabasePlatform(); + } + + /** + * {@inheritDoc} + */ + public function getSchemaManager(DBALConnection $conn, AbstractPlatform $platform) + { + return $this->driver->getSchemaManager($conn, $platform); + } + + public function getExceptionConverter(): ExceptionConverter + { + return $this->driver->getExceptionConverter(); + } +} diff --git a/app/vendor/doctrine/dbal/src/Portability/Middleware.php b/app/vendor/doctrine/dbal/src/Portability/Middleware.php new file mode 100644 index 000000000..b00147062 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Portability/Middleware.php @@ -0,0 +1,32 @@ +mode = $mode; + $this->case = $case; + } + + public function wrap(DriverInterface $driver): DriverInterface + { + if ($this->mode !== 0) { + return new Driver($driver, $this->mode, $this->case); + } + + return $driver; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/OptimizeFlags.php b/app/vendor/doctrine/dbal/src/Portability/OptimizeFlags.php similarity index 92% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/OptimizeFlags.php rename to app/vendor/doctrine/dbal/src/Portability/OptimizeFlags.php index 7d8e55df4..742eb93fb 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/OptimizeFlags.php +++ b/app/vendor/doctrine/dbal/src/Portability/OptimizeFlags.php @@ -8,7 +8,6 @@ use Doctrine\DBAL\Platforms\DB2Platform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\PostgreSQL94Platform; -use Doctrine\DBAL\Platforms\SQLAnywhere16Platform; use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Platforms\SQLServer2012Platform; @@ -24,7 +23,6 @@ final class OptimizeFlags DB2Platform::class => 0, OraclePlatform::class => Connection::PORTABILITY_EMPTY_TO_NULL, PostgreSQL94Platform::class => 0, - SQLAnywhere16Platform::class => 0, SqlitePlatform::class => 0, SQLServer2012Platform::class => 0, ]; diff --git a/app/vendor/doctrine/dbal/src/Portability/Result.php b/app/vendor/doctrine/dbal/src/Portability/Result.php new file mode 100644 index 000000000..1fa91ab48 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Portability/Result.php @@ -0,0 +1,100 @@ +result = $result; + $this->converter = $converter; + } + + /** + * {@inheritDoc} + */ + public function fetchNumeric() + { + return $this->converter->convertNumeric( + $this->result->fetchNumeric() + ); + } + + /** + * {@inheritDoc} + */ + public function fetchAssociative() + { + return $this->converter->convertAssociative( + $this->result->fetchAssociative() + ); + } + + /** + * {@inheritDoc} + */ + public function fetchOne() + { + return $this->converter->convertOne( + $this->result->fetchOne() + ); + } + + /** + * {@inheritDoc} + */ + public function fetchAllNumeric(): array + { + return $this->converter->convertAllNumeric( + $this->result->fetchAllNumeric() + ); + } + + /** + * {@inheritDoc} + */ + public function fetchAllAssociative(): array + { + return $this->converter->convertAllAssociative( + $this->result->fetchAllAssociative() + ); + } + + /** + * {@inheritDoc} + */ + public function fetchFirstColumn(): array + { + return $this->converter->convertFirstColumn( + $this->result->fetchFirstColumn() + ); + } + + public function rowCount(): int + { + return $this->result->rowCount(); + } + + public function columnCount(): int + { + return $this->result->columnCount(); + } + + public function free(): void + { + $this->result->free(); + } +} diff --git a/app/vendor/doctrine/dbal/src/Portability/Statement.php b/app/vendor/doctrine/dbal/src/Portability/Statement.php new file mode 100644 index 000000000..f3f94e413 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Portability/Statement.php @@ -0,0 +1,55 @@ +Statement and applies portability measures. + */ + public function __construct(DriverStatement $stmt, Converter $converter) + { + $this->stmt = $stmt; + $this->converter = $converter; + } + + /** + * {@inheritdoc} + */ + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + { + return $this->stmt->bindParam($param, $variable, $type, $length); + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = ParameterType::STRING) + { + return $this->stmt->bindValue($param, $value, $type); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null): ResultInterface + { + return new Result( + $this->stmt->execute($params), + $this->converter + ); + } +} diff --git a/app/vendor/doctrine/dbal/src/Query.php b/app/vendor/doctrine/dbal/src/Query.php new file mode 100644 index 000000000..ea6024cd8 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Query.php @@ -0,0 +1,70 @@ + + */ + private $params; + + /** + * The types of the parameters bound to the query. + * + * @var array + */ + private $types; + + /** + * @param array $params + * @param array $types + * + * @psalm-suppress ImpurePropertyAssignment + */ + public function __construct(string $sql, array $params, array $types) + { + $this->sql = $sql; + $this->params = $params; + $this->types = $types; + } + + public function getSQL(): string + { + return $this->sql; + } + + /** + * @return array + */ + public function getParams(): array + { + return $this->params; + } + + /** + * @return array + */ + public function getTypes(): array + { + return $this->types; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php b/app/vendor/doctrine/dbal/src/Query/Expression/CompositeExpression.php similarity index 96% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php rename to app/vendor/doctrine/dbal/src/Query/Expression/CompositeExpression.php index e0194c246..85de9ae93 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php +++ b/app/vendor/doctrine/dbal/src/Query/Expression/CompositeExpression.php @@ -117,7 +117,7 @@ public function add($part) 'CompositeExpression::add() is deprecated, use CompositeExpression::with() instead.' ); - if (empty($part)) { + if ($part === null) { return $this; } @@ -140,11 +140,7 @@ public function with($part, ...$parts): self { $that = clone $this; - $that->parts[] = $part; - - foreach ($parts as $part) { - $that->parts[] = $part; - } + $that->parts = array_merge($that->parts, [$part], $parts); return $that; } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php b/app/vendor/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php similarity index 91% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php rename to app/vendor/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php index c86cfc324..8cb531503 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php +++ b/app/vendor/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php @@ -256,8 +256,8 @@ public function isNotNull($x) /** * Creates a LIKE() comparison expression with the given arguments. * - * @param string $x Field in string format to be inspected by LIKE() comparison. - * @param mixed $y Argument to be used in LIKE() comparison. + * @param string $x The expression to be inspected by the LIKE comparison + * @param mixed $y The pattern to compare against * * @return string */ @@ -270,8 +270,8 @@ public function like($x, $y/*, ?string $escapeChar = null */) /** * Creates a NOT LIKE() comparison expression with the given arguments. * - * @param string $x Field in string format to be inspected by NOT LIKE() comparison. - * @param mixed $y Argument to be used in NOT LIKE() comparison. + * @param string $x The expression to be inspected by the NOT LIKE comparison + * @param mixed $y The pattern to compare against * * @return string */ @@ -282,10 +282,10 @@ public function notLike($x, $y/*, ?string $escapeChar = null */) } /** - * Creates a IN () comparison expression with the given arguments. + * Creates an IN () comparison expression with the given arguments. * - * @param string $x The field in string format to be inspected by IN() comparison. - * @param string|string[] $y The placeholder or the array of values to be used by IN() comparison. + * @param string $x The SQL expression to be matched against the set. + * @param string|string[] $y The SQL expression or an array of SQL expressions representing the set. * * @return string */ @@ -297,8 +297,8 @@ public function in($x, $y) /** * Creates a NOT IN () comparison expression with the given arguments. * - * @param string $x The expression to be inspected by NOT IN() comparison. - * @param string|string[] $y The placeholder or the array of values to be used by NOT IN() comparison. + * @param string $x The SQL expression to be matched against the set. + * @param string|string[] $y The SQL expression or an array of SQL expressions representing the set. * * @return string */ diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php b/app/vendor/doctrine/dbal/src/Query/QueryBuilder.php similarity index 87% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php rename to app/vendor/doctrine/dbal/src/Query/QueryBuilder.php index 419937d35..246cde1a6 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php +++ b/app/vendor/doctrine/dbal/src/Query/QueryBuilder.php @@ -4,14 +4,14 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; -use Doctrine\DBAL\ForwardCompatibility; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Query\Expression\CompositeExpression; use Doctrine\DBAL\Query\Expression\ExpressionBuilder; +use Doctrine\DBAL\Result; +use Doctrine\DBAL\Statement; use Doctrine\DBAL\Types\Type; use Doctrine\Deprecations\Deprecation; -use function array_filter; use function array_key_exists; use function array_keys; use function array_unshift; @@ -91,7 +91,7 @@ class QueryBuilder /** * The query parameters. * - * @var array|array + * @var list|array */ private $params = []; @@ -198,21 +198,159 @@ public function getState() return $this->state; } + /** + * Prepares and executes an SQL query and returns the first row of the result + * as an associative array. + * + * @return array|false False is returned if no rows are found. + * + * @throws Exception + */ + public function fetchAssociative() + { + return $this->connection->fetchAssociative($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Prepares and executes an SQL query and returns the first row of the result + * as a numerically indexed array. + * + * @return array|false False is returned if no rows are found. + * + * @throws Exception + */ + public function fetchNumeric() + { + return $this->connection->fetchNumeric($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Prepares and executes an SQL query and returns the value of a single column + * of the first row of the result. + * + * @return mixed|false False is returned if no rows are found. + * + * @throws Exception + */ + public function fetchOne() + { + return $this->connection->fetchOne($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Prepares and executes an SQL query and returns the result as an array of numeric arrays. + * + * @return array> + * + * @throws Exception + */ + public function fetchAllNumeric(): array + { + return $this->connection->fetchAllNumeric($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Prepares and executes an SQL query and returns the result as an array of associative arrays. + * + * @return array> + * + * @throws Exception + */ + public function fetchAllAssociative(): array + { + return $this->connection->fetchAllAssociative($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Prepares and executes an SQL query and returns the result as an associative array with the keys + * mapped to the first column and the values mapped to the second column. + * + * @return array + * + * @throws Exception + */ + public function fetchAllKeyValue(): array + { + return $this->connection->fetchAllKeyValue($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Prepares and executes an SQL query and returns the result as an associative array with the keys mapped + * to the first column and the values being an associative array representing the rest of the columns + * and their values. + * + * @return array> + * + * @throws Exception + */ + public function fetchAllAssociativeIndexed(): array + { + return $this->connection->fetchAllAssociativeIndexed($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Prepares and executes an SQL query and returns the result as an array of the first column values. + * + * @return array + * + * @throws Exception + */ + public function fetchFirstColumn(): array + { + return $this->connection->fetchFirstColumn($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Executes an SQL query (SELECT) and returns a Result. + * + * @throws Exception + */ + public function executeQuery(): Result + { + return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Executes an SQL statement and returns the number of affected rows. + * + * Should be used for INSERT, UPDATE and DELETE + * + * @return int The number of affected rows. + * + * @throws Exception + */ + public function executeStatement(): int + { + return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes); + } + /** * Executes this query using the bound parameters and their types. * - * @return ForwardCompatibility\DriverStatement|int + * @deprecated Use {@link executeQuery()} or {@link executeStatement()} instead. + * + * @return Result|int * * @throws Exception */ public function execute() { if ($this->type === self::SELECT) { - return ForwardCompatibility\Result::ensure( - $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes) + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4578', + 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeQuery() for SQL queries instead.' ); + + return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes); } + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4578', + 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeStatement() for SQL statements instead.' + ); + return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes); } @@ -301,7 +439,7 @@ public function setParameter($key, $value, $type = null) * )); * * - * @param array|array $params Parameters to set + * @param list|array $params Parameters to set * @param array|array $types Parameter types * * @return $this This QueryBuilder instance. @@ -317,7 +455,7 @@ public function setParameters(array $params, array $types = []) /** * Gets all defined query parameters for the query being constructed indexed by parameter index or name. * - * @return array|array The currently defined query parameters + * @return list|array The currently defined query parameters */ public function getParameters() { @@ -482,7 +620,7 @@ public function select($select = null/*, string ...$selects*/) { $this->type = self::SELECT; - if (empty($select)) { + if ($select === null) { return $this; } @@ -490,7 +628,7 @@ public function select($select = null/*, string ...$selects*/) Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', - 'Passing an array for the first argument to QueryBuilder::select is deprecated, ' . + 'Passing an array for the first argument to QueryBuilder::select() is deprecated, ' . 'pass each value as an individual variadic argument instead.' ); } @@ -541,7 +679,7 @@ public function addSelect($select = null/*, string ...$selects*/) { $this->type = self::SELECT; - if (empty($select)) { + if ($select === null) { return $this; } @@ -549,7 +687,7 @@ public function addSelect($select = null/*, string ...$selects*/) Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', - 'Passing an array for the first argument to QueryBuilder::addSelect is deprecated, ' . + 'Passing an array for the first argument to QueryBuilder::addSelect() is deprecated, ' . 'pass each value as an individual variadic argument instead.' ); } @@ -579,7 +717,7 @@ public function delete($delete = null, $alias = null) { $this->type = self::DELETE; - if (! $delete) { + if ($delete === null) { return $this; } @@ -609,7 +747,7 @@ public function update($update = null, $alias = null) { $this->type = self::UPDATE; - if (! $update) { + if ($update === null) { return $this; } @@ -642,7 +780,7 @@ public function insert($insert = null) { $this->type = self::INSERT; - if (! $insert) { + if ($insert === null) { return $this; } @@ -857,13 +995,10 @@ public function where($predicates) public function andWhere($where) { $args = func_get_args(); - $args = array_filter($args); // https://github.com/doctrine/dbal/issues/4282 $where = $this->getQueryPart('where'); if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) { - if (count($args) > 0) { - $where = $where->with(...$args); - } + $where = $where->with(...$args); } else { array_unshift($args, $where); $where = CompositeExpression::and(...$args); @@ -893,13 +1028,10 @@ public function andWhere($where) public function orWhere($where) { $args = func_get_args(); - $args = array_filter($args); // https://github.com/doctrine/dbal/issues/4282 $where = $this->getQueryPart('where'); if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) { - if (count($args) > 0) { - $where = $where->with(...$args); - } + $where = $where->with(...$args); } else { array_unshift($args, $where); $where = CompositeExpression::or(...$args); @@ -928,7 +1060,7 @@ public function orWhere($where) */ public function groupBy($groupBy/*, string ...$groupBys*/) { - if (empty($groupBy)) { + if (is_array($groupBy) && count($groupBy) === 0) { return $this; } @@ -936,7 +1068,7 @@ public function groupBy($groupBy/*, string ...$groupBys*/) Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', - 'Passing an array for the first argument to QueryBuilder::groupBy is deprecated, ' . + 'Passing an array for the first argument to QueryBuilder::groupBy() is deprecated, ' . 'pass each value as an individual variadic argument instead.' ); } @@ -966,7 +1098,7 @@ public function groupBy($groupBy/*, string ...$groupBys*/) */ public function addGroupBy($groupBy/*, string ...$groupBys*/) { - if (empty($groupBy)) { + if (is_array($groupBy) && count($groupBy) === 0) { return $this; } @@ -974,7 +1106,7 @@ public function addGroupBy($groupBy/*, string ...$groupBys*/) Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', - 'Passing an array for the first argument to QueryBuilder::addGroupBy is deprecated, ' . + 'Passing an array for the first argument to QueryBuilder::addGroupBy() is deprecated, ' . 'pass each value as an individual variadic argument instead.' ); } @@ -1062,7 +1194,6 @@ public function having($having) public function andHaving($having) { $args = func_get_args(); - $args = array_filter($args); // https://github.com/doctrine/dbal/issues/4282 $having = $this->getQueryPart('having'); if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) { @@ -1086,7 +1217,6 @@ public function andHaving($having) public function orHaving($having) { $args = func_get_args(); - $args = array_filter($args); // https://github.com/doctrine/dbal/issues/4282 $having = $this->getQueryPart('having'); if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) { @@ -1110,7 +1240,7 @@ public function orHaving($having) */ public function orderBy($sort, $order = null) { - return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), false); + return $this->add('orderBy', $sort . ' ' . ($order ?? 'ASC'), false); } /** @@ -1123,7 +1253,7 @@ public function orderBy($sort, $order = null) */ public function addOrderBy($sort, $order = null) { - return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), true); + return $this->add('orderBy', $sort . ' ' . ($order ?? 'ASC'), true); } /** @@ -1213,6 +1343,8 @@ private function getSQLForSelect() /** * @return string[] + * + * @throws QueryException */ private function getFromClauses() { @@ -1316,7 +1448,7 @@ public function __toString() /** * Creates a new named parameter and bind the value $value to it. * - * This method provides a shortcut for PDOStatement::bindValue + * This method provides a shortcut for {@link Statement::bindValue()} * when using prepared statements. * * The parameter $value specifies the value that you want to bind. If @@ -1324,8 +1456,6 @@ public function __toString() * placeholder for you. An automatic placeholder will be of the name * ':dcValue1', ':dcValue2' etc. * - * For more information see {@link http://php.net/pdostatement-bindparam} - * * Example: * * $value = 2; @@ -1377,8 +1507,8 @@ public function createNamedParameter($value, $type = ParameterType::STRING, $pla */ public function createPositionalParameter($value, $type = ParameterType::STRING) { - $this->boundCounter++; $this->setParameter($this->boundCounter, $value, $type); + $this->boundCounter++; return '?'; } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryException.php b/app/vendor/doctrine/dbal/src/Query/QueryException.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryException.php rename to app/vendor/doctrine/dbal/src/Query/QueryException.php diff --git a/app/vendor/doctrine/dbal/src/Result.php b/app/vendor/doctrine/dbal/src/Result.php new file mode 100644 index 000000000..59c1065a0 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Result.php @@ -0,0 +1,339 @@ +result = $result; + $this->connection = $connection; + } + + /** + * Returns the next row of the result as a numeric array or FALSE if there are no more rows. + * + * @return list|false + * + * @throws Exception + */ + public function fetchNumeric() + { + try { + return $this->result->fetchNumeric(); + } catch (DriverException $e) { + throw $this->connection->convertException($e); + } + } + + /** + * Returns the next row of the result as an associative array or FALSE if there are no more rows. + * + * @return array|false + * + * @throws Exception + */ + public function fetchAssociative() + { + try { + return $this->result->fetchAssociative(); + } catch (DriverException $e) { + throw $this->connection->convertException($e); + } + } + + /** + * Returns the first value of the next row of the result or FALSE if there are no more rows. + * + * @return mixed|false + * + * @throws Exception + */ + public function fetchOne() + { + try { + return $this->result->fetchOne(); + } catch (DriverException $e) { + throw $this->connection->convertException($e); + } + } + + /** + * Returns an array containing all of the result rows represented as numeric arrays. + * + * @return list> + * + * @throws Exception + */ + public function fetchAllNumeric(): array + { + try { + return $this->result->fetchAllNumeric(); + } catch (DriverException $e) { + throw $this->connection->convertException($e); + } + } + + /** + * Returns an array containing all of the result rows represented as associative arrays. + * + * @return list> + * + * @throws Exception + */ + public function fetchAllAssociative(): array + { + try { + return $this->result->fetchAllAssociative(); + } catch (DriverException $e) { + throw $this->connection->convertException($e); + } + } + + /** + * Returns an array containing the values of the first column of the result. + * + * @return array + * + * @throws Exception + */ + public function fetchAllKeyValue(): array + { + $this->ensureHasKeyValue(); + + $data = []; + + foreach ($this->fetchAllNumeric() as [$key, $value]) { + $data[$key] = $value; + } + + return $data; + } + + /** + * Returns an associative array with the keys mapped to the first column and the values being + * an associative array representing the rest of the columns and their values. + * + * @return array> + * + * @throws Exception + */ + public function fetchAllAssociativeIndexed(): array + { + $data = []; + + foreach ($this->fetchAllAssociative() as $row) { + $data[array_shift($row)] = $row; + } + + return $data; + } + + /** + * @return list + * + * @throws Exception + */ + public function fetchFirstColumn(): array + { + try { + return $this->result->fetchFirstColumn(); + } catch (DriverException $e) { + throw $this->connection->convertException($e); + } + } + + /** + * @return Traversable> + * + * @throws Exception + */ + public function iterateNumeric(): Traversable + { + try { + while (($row = $this->result->fetchNumeric()) !== false) { + yield $row; + } + } catch (DriverException $e) { + throw $this->connection->convertException($e); + } + } + + /** + * @return Traversable> + * + * @throws Exception + */ + public function iterateAssociative(): Traversable + { + try { + while (($row = $this->result->fetchAssociative()) !== false) { + yield $row; + } + } catch (DriverException $e) { + throw $this->connection->convertException($e); + } + } + + /** + * {@inheritDoc} + * + * @throws Exception + */ + public function iterateKeyValue(): Traversable + { + $this->ensureHasKeyValue(); + + foreach ($this->iterateNumeric() as [$key, $value]) { + yield $key => $value; + } + } + + /** + * Returns an iterator over the result set with the keys mapped to the first column and the values being + * an associative array representing the rest of the columns and their values. + * + * @return Traversable> + * + * @throws Exception + */ + public function iterateAssociativeIndexed(): Traversable + { + foreach ($this->iterateAssociative() as $row) { + yield array_shift($row) => $row; + } + } + + /** + * @return Traversable + * + * @throws Exception + */ + public function iterateColumn(): Traversable + { + try { + while (($value = $this->result->fetchOne()) !== false) { + yield $value; + } + } catch (DriverException $e) { + throw $this->connection->convertException($e); + } + } + + /** + * @throws Exception + */ + public function rowCount(): int + { + try { + return $this->result->rowCount(); + } catch (DriverException $e) { + throw $this->connection->convertException($e); + } + } + + /** + * @throws Exception + */ + public function columnCount(): int + { + try { + return $this->result->columnCount(); + } catch (DriverException $e) { + throw $this->connection->convertException($e); + } + } + + public function free(): void + { + $this->result->free(); + } + + /** + * @throws Exception + */ + private function ensureHasKeyValue(): void + { + $columnCount = $this->columnCount(); + + if ($columnCount < 2) { + throw NoKeyValue::fromColumnCount($columnCount); + } + } + + /** + * BC layer for a wide-spread use-case of old DBAL APIs + * + * @deprecated This API is deprecated and will be removed after 2022 + * + * @return mixed + */ + public function fetch(int $mode = FetchMode::ASSOCIATIVE) + { + if (func_num_args() > 1) { + throw new LogicException('Only invocations with one argument are still supported by this legecy API.'); + } + + if ($mode === FetchMode::ASSOCIATIVE) { + return $this->fetchAssociative(); + } + + if ($mode === FetchMode::NUMERIC) { + return $this->fetchNumeric(); + } + + if ($mode === FetchMode::COLUMN) { + return $this->fetchOne(); + } + + throw new LogicException('Only fetch modes declared on Doctrine\DBAL\FetchMode are supported by legacy API.'); + } + + /** + * BC layer for a wide-spread use-case of old DBAL APIs + * + * @deprecated This API is deprecated and will be removed after 2022 + * + * @return list + */ + public function fetchAll(int $mode = FetchMode::ASSOCIATIVE): array + { + if (func_num_args() > 1) { + throw new LogicException('Only invocations with one argument are still supported by this legecy API.'); + } + + if ($mode === FetchMode::ASSOCIATIVE) { + return $this->fetchAllAssociative(); + } + + if ($mode === FetchMode::NUMERIC) { + return $this->fetchAllNumeric(); + } + + if ($mode === FetchMode::COLUMN) { + return $this->fetchFirstColumn(); + } + + throw new LogicException('Only fetch modes declared on Doctrine\DBAL\FetchMode are supported by legacy API.'); + } +} diff --git a/app/vendor/doctrine/dbal/src/SQL/Parser.php b/app/vendor/doctrine/dbal/src/SQL/Parser.php new file mode 100644 index 000000000..32d306ca0 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/SQL/Parser.php @@ -0,0 +1,117 @@ +getMySQLStringLiteralPattern("'"), + $this->getMySQLStringLiteralPattern('"'), + ]; + } else { + $patterns = [ + $this->getAnsiSQLStringLiteralPattern("'"), + $this->getAnsiSQLStringLiteralPattern('"'), + ]; + } + + $patterns = array_merge($patterns, [ + self::BACKTICK_IDENTIFIER, + self::BRACKET_IDENTIFIER, + self::MULTICHAR, + self::ONE_LINE_COMMENT, + self::MULTI_LINE_COMMENT, + self::OTHER, + ]); + + $this->sqlPattern = sprintf('(%s)+', implode('|', $patterns)); + } + + /** + * Parses the given SQL statement + */ + public function parse(string $sql, Visitor $visitor): void + { + /** @var array $patterns */ + $patterns = [ + self::NAMED_PARAMETER => static function (string $sql) use ($visitor): void { + $visitor->acceptNamedParameter($sql); + }, + self::POSITIONAL_PARAMETER => static function (string $sql) use ($visitor): void { + $visitor->acceptPositionalParameter($sql); + }, + $this->sqlPattern => static function (string $sql) use ($visitor): void { + $visitor->acceptOther($sql); + }, + self::SPECIAL => static function (string $sql) use ($visitor): void { + $visitor->acceptOther($sql); + }, + ]; + + $offset = 0; + + while (($handler = current($patterns)) !== false) { + if (preg_match('~\G' . key($patterns) . '~s', $sql, $matches, 0, $offset) === 1) { + $handler($matches[0]); + reset($patterns); + + $offset += strlen($matches[0]); + } else { + next($patterns); + } + } + + assert($offset === strlen($sql)); + } + + private function getMySQLStringLiteralPattern(string $delimiter): string + { + return $delimiter . '((\\\\' . self::ANY . ')|(?![' . $delimiter . '\\\\])' . self::ANY . ')*' . $delimiter; + } + + private function getAnsiSQLStringLiteralPattern(string $delimiter): string + { + return $delimiter . '[^' . $delimiter . ']*' . $delimiter; + } +} diff --git a/app/vendor/doctrine/dbal/src/SQL/Parser/Visitor.php b/app/vendor/doctrine/dbal/src/SQL/Parser/Visitor.php new file mode 100644 index 000000000..574ba1bf1 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/SQL/Parser/Visitor.php @@ -0,0 +1,26 @@ +getName(); - if (! $this->_namespace) { + if ($this->_namespace === null) { $name = $defaultNamespaceName . '.' . $name; } @@ -165,7 +165,7 @@ protected function trimQuotes($identifier) */ public function getName() { - if ($this->_namespace) { + if ($this->_namespace !== null) { return $this->_namespace . '.' . $this->_name; } @@ -204,7 +204,7 @@ public function getQuotedName(AbstractPlatform $platform) */ protected function _generateIdentifierName($columnNames, $prefix = '', $maxSize = 30) { - $hash = implode('', array_map(static function ($column) { + $hash = implode('', array_map(static function ($column): string { return dechex(crc32($column)); }, $columnNames)); diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php b/app/vendor/doctrine/dbal/src/Schema/AbstractSchemaManager.php similarity index 87% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php rename to app/vendor/doctrine/dbal/src/Schema/AbstractSchemaManager.php index 2cad11a86..50b9facee 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php +++ b/app/vendor/doctrine/dbal/src/Schema/AbstractSchemaManager.php @@ -3,7 +3,6 @@ namespace Doctrine\DBAL\Schema; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\ConnectionException; use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs; use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; use Doctrine\DBAL\Events; @@ -46,13 +45,10 @@ abstract class AbstractSchemaManager */ protected $_platform; - /** - * Constructor. Accepts the Connection instance to manage the schema for. - */ - public function __construct(Connection $conn, ?AbstractPlatform $platform = null) + public function __construct(Connection $connection, AbstractPlatform $platform) { - $this->_conn = $conn; - $this->_platform = $platform ?: $this->_conn->getDatabasePlatform(); + $this->_conn = $connection; + $this->_platform = $platform; } /** @@ -98,6 +94,8 @@ public function tryMethod() * Lists the available databases for this connection. * * @return string[] + * + * @throws Exception */ public function listDatabases() { @@ -111,10 +109,21 @@ public function listDatabases() /** * Returns a list of all namespaces in the current database. * + * @deprecated Use {@link listSchemaNames()} instead. + * * @return string[] + * + * @throws Exception */ public function listNamespaceNames() { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'AbstractSchemaManager::listNamespaceNames() is deprecated,' + . ' use AbstractSchemaManager::listSchemaNames() instead.' + ); + $sql = $this->_platform->getListNamespacesSQL(); $namespaces = $this->_conn->fetchAllAssociative($sql); @@ -122,12 +131,26 @@ public function listNamespaceNames() return $this->getPortableNamespacesList($namespaces); } + /** + * Returns a list of the names of all schemata in the current database. + * + * @return list + * + * @throws Exception + */ + public function listSchemaNames(): array + { + throw Exception::notSupported(__METHOD__); + } + /** * Lists the available sequences for this connection. * * @param string|null $database * * @return Sequence[] + * + * @throws Exception */ public function listSequences($database = null) { @@ -156,10 +179,12 @@ public function listSequences($database = null) * @param string|null $database * * @return Column[] + * + * @throws Exception */ public function listTableColumns($table, $database = null) { - if (! $database) { + if ($database === null) { $database = $this->_conn->getDatabase(); } @@ -178,6 +203,8 @@ public function listTableColumns($table, $database = null) * @param string $table The name of the table. * * @return Index[] + * + * @throws Exception */ public function listTableIndexes($table) { @@ -196,6 +223,8 @@ public function listTableIndexes($table) * @param string|string[] $names * * @return bool + * + * @throws Exception */ public function tablesExist($names) { @@ -203,7 +232,7 @@ public function tablesExist($names) Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3580', - 'The usage of a string $tableNames in AbstractSchemaManager::tablesExist is deprecated. ' . + 'The usage of a string $tableNames in AbstractSchemaManager::tablesExist() is deprecated. ' . 'Pass a one-element array instead.' ); } @@ -217,6 +246,8 @@ public function tablesExist($names) * Returns a list of all tables in the current database. * * @return string[] + * + * @throws Exception */ public function listTableNames() { @@ -239,27 +270,19 @@ public function listTableNames() protected function filterAssetNames($assetNames) { $filter = $this->_conn->getConfiguration()->getSchemaAssetsFilter(); - if (! $filter) { + if ($filter === null) { return $assetNames; } return array_values(array_filter($assetNames, $filter)); } - /** - * @deprecated Use Configuration::getSchemaAssetsFilter() instead - * - * @return string|null - */ - protected function getFilterSchemaAssetsExpression() - { - return $this->_conn->getConfiguration()->getFilterSchemaAssetsExpression(); - } - /** * Lists the tables for this connection. * * @return Table[] + * + * @throws Exception */ public function listTables() { @@ -277,24 +300,29 @@ public function listTables() * @param string $name * * @return Table + * + * @throws Exception */ public function listTableDetails($name) { $columns = $this->listTableColumns($name); $foreignKeys = []; + if ($this->_platform->supportsForeignKeyConstraints()) { $foreignKeys = $this->listTableForeignKeys($name); } $indexes = $this->listTableIndexes($name); - return new Table($name, $columns, $indexes, $foreignKeys); + return new Table($name, $columns, $indexes, [], $foreignKeys); } /** * Lists the views this connection has. * * @return View[] + * + * @throws Exception */ public function listViews() { @@ -312,6 +340,8 @@ public function listViews() * @param string|null $database * * @return ForeignKeyConstraint[] + * + * @throws Exception */ public function listTableForeignKeys($table, $database = null) { @@ -335,18 +365,32 @@ public function listTableForeignKeys($table, $database = null) * @param string $database The name of the database to drop. * * @return void + * + * @throws Exception */ public function dropDatabase($database) { $this->_execSql($this->_platform->getDropDatabaseSQL($database)); } + /** + * Drops a schema. + * + * @throws Exception + */ + public function dropSchema(string $schemaName): void + { + $this->_execSql($this->_platform->getDropSchemaSQL($schemaName)); + } + /** * Drops the given table. * * @param string $name The name of the table to drop. * * @return void + * + * @throws Exception */ public function dropTable($name) { @@ -360,6 +404,8 @@ public function dropTable($name) * @param Table|string $table The name of the table. * * @return void + * + * @throws Exception */ public function dropIndex($index, $table) { @@ -376,6 +422,8 @@ public function dropIndex($index, $table) * @param Table|string $table The name of the table. * * @return void + * + * @throws Exception */ public function dropConstraint(Constraint $constraint, $table) { @@ -389,6 +437,8 @@ public function dropConstraint(Constraint $constraint, $table) * @param Table|string $table The name of the table with the foreign key. * * @return void + * + * @throws Exception */ public function dropForeignKey($foreignKey, $table) { @@ -401,6 +451,8 @@ public function dropForeignKey($foreignKey, $table) * @param string $name The name of the sequence to drop. * * @return void + * + * @throws Exception */ public function dropSequence($name) { @@ -413,6 +465,8 @@ public function dropSequence($name) * @param string $name The name of the view. * * @return void + * + * @throws Exception */ public function dropView($name) { @@ -427,6 +481,8 @@ public function dropView($name) * @param string $database The name of the database to create. * * @return void + * + * @throws Exception */ public function createDatabase($database) { @@ -437,6 +493,8 @@ public function createDatabase($database) * Creates a new table. * * @return void + * + * @throws Exception */ public function createTable(Table $table) { @@ -451,7 +509,7 @@ public function createTable(Table $table) * * @return void * - * @throws ConnectionException If something fails at database level. + * @throws Exception */ public function createSequence($sequence) { @@ -464,6 +522,8 @@ public function createSequence($sequence) * @param Table|string $table * * @return void + * + * @throws Exception */ public function createConstraint(Constraint $constraint, $table) { @@ -476,6 +536,8 @@ public function createConstraint(Constraint $constraint, $table) * @param Table|string $table The name of the table on which the index is to be created. * * @return void + * + * @throws Exception */ public function createIndex(Index $index, $table) { @@ -489,6 +551,8 @@ public function createIndex(Index $index, $table) * @param Table|string $table The name of the table on which the foreign key is to be created. * * @return void + * + * @throws Exception */ public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) { @@ -499,6 +563,8 @@ public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) * Creates a new view. * * @return void + * + * @throws Exception */ public function createView(View $view) { @@ -516,6 +582,8 @@ public function createView(View $view) * @param Table|string $table * * @return void + * + * @throws Exception */ public function dropAndCreateConstraint(Constraint $constraint, $table) { @@ -529,6 +597,8 @@ public function dropAndCreateConstraint(Constraint $constraint, $table) * @param Table|string $table The name of the table on which the index is to be created. * * @return void + * + * @throws Exception */ public function dropAndCreateIndex(Index $index, $table) { @@ -544,6 +614,8 @@ public function dropAndCreateIndex(Index $index, $table) * @param Table|string $table The name of the table on which the foreign key is to be created. * * @return void + * + * @throws Exception */ public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) { @@ -556,7 +628,7 @@ public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table * * @return void * - * @throws ConnectionException If something fails at database level. + * @throws Exception */ public function dropAndCreateSequence(Sequence $sequence) { @@ -568,6 +640,8 @@ public function dropAndCreateSequence(Sequence $sequence) * Drops and creates a new table. * * @return void + * + * @throws Exception */ public function dropAndCreateTable(Table $table) { @@ -581,6 +655,8 @@ public function dropAndCreateTable(Table $table) * @param string $database The name of the database to create. * * @return void + * + * @throws Exception */ public function dropAndCreateDatabase($database) { @@ -592,6 +668,8 @@ public function dropAndCreateDatabase($database) * Drops and creates a new view. * * @return void + * + * @throws Exception */ public function dropAndCreateView(View $view) { @@ -605,6 +683,8 @@ public function dropAndCreateView(View $view) * Alters an existing tables schema. * * @return void + * + * @throws Exception */ public function alterTable(TableDiff $tableDiff) { @@ -620,6 +700,8 @@ public function alterTable(TableDiff $tableDiff) * @param string $newName The new name of the table. * * @return void + * + * @throws Exception */ public function renameTable($name, $newName) { @@ -642,13 +724,7 @@ protected function _getPortableDatabasesList($databases) { $list = []; foreach ($databases as $value) { - $value = $this->_getPortableDatabaseDefinition($value); - - if (! $value) { - continue; - } - - $list[] = $value; + $list[] = $this->_getPortableDatabaseDefinition($value); } return $list; @@ -657,12 +733,22 @@ protected function _getPortableDatabasesList($databases) /** * Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition. * - * @param mixed[][] $namespaces The list of namespace names in the native DBMS data definition. + * @deprecated Use {@link listSchemaNames()} instead. + * + * @param array> $namespaces The list of namespace names + * in the native DBMS data definition. * * @return string[] */ protected function getPortableNamespacesList(array $namespaces) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'AbstractSchemaManager::getPortableNamespacesList() is deprecated,' + . ' use AbstractSchemaManager::listSchemaNames() instead.' + ); + $namespacesList = []; foreach ($namespaces as $namespace) { @@ -685,48 +771,22 @@ protected function _getPortableDatabaseDefinition($database) /** * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition. * - * @param mixed[] $namespace The native DBMS namespace definition. + * @deprecated Use {@link listSchemaNames()} instead. + * + * @param array $namespace The native DBMS namespace definition. * * @return mixed */ protected function getPortableNamespaceDefinition(array $namespace) { - return $namespace; - } - - /** - * @deprecated - * - * @param mixed[][] $functions - * - * @return mixed[][] - */ - protected function _getPortableFunctionsList($functions) - { - $list = []; - foreach ($functions as $value) { - $value = $this->_getPortableFunctionDefinition($value); - - if (! $value) { - continue; - } + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'AbstractSchemaManager::getPortableNamespaceDefinition() is deprecated,' + . ' use AbstractSchemaManager::listSchemaNames() instead.' + ); - $list[] = $value; - } - - return $list; - } - - /** - * @deprecated - * - * @param mixed[] $function - * - * @return mixed - */ - protected function _getPortableFunctionDefinition($function) - { - return $function; + return $namespace; } /** @@ -764,6 +824,8 @@ protected function _getPortableTriggerDefinition($trigger) * @param mixed[][] $sequences * * @return Sequence[] + * + * @throws Exception */ protected function _getPortableSequencesList($sequences) { @@ -798,6 +860,8 @@ protected function _getPortableSequenceDefinition($sequence) * @param mixed[][] $tableColumns * * @return Column[] + * + * @throws Exception */ protected function _getPortableTableColumnList($table, $database, $tableColumns) { @@ -820,7 +884,7 @@ protected function _getPortableTableColumnList($table, $database, $tableColumns) $column = $this->_getPortableTableColumnDefinition($tableColumn); } - if (! $column) { + if ($column === null) { continue; } @@ -837,6 +901,8 @@ protected function _getPortableTableColumnList($table, $database, $tableColumns) * @param mixed[] $tableColumn * * @return Column + * + * @throws Exception */ abstract protected function _getPortableTableColumnDefinition($tableColumn); @@ -847,6 +913,8 @@ abstract protected function _getPortableTableColumnDefinition($tableColumn); * @param string|null $tableName * * @return Index[] + * + * @throws Exception */ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) { @@ -908,7 +976,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null ); } - if (! $index) { + if ($index === null) { continue; } @@ -927,13 +995,7 @@ protected function _getPortableTablesList($tables) { $list = []; foreach ($tables as $value) { - $value = $this->_getPortableTableDefinition($value); - - if (! $value) { - continue; - } - - $list[] = $value; + $list[] = $this->_getPortableTableDefinition($value); } return $list; @@ -958,13 +1020,7 @@ protected function _getPortableUsersList($users) { $list = []; foreach ($users as $value) { - $value = $this->_getPortableUserDefinition($value); - - if (! $value) { - continue; - } - - $list[] = $value; + $list[] = $this->_getPortableUserDefinition($value); } return $list; @@ -991,7 +1047,7 @@ protected function _getPortableViewsList($views) foreach ($views as $value) { $view = $this->_getPortableViewDefinition($value); - if (! $view) { + if ($view === false) { continue; } @@ -1042,6 +1098,8 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) * @param string[]|string $sql * * @return void + * + * @throws Exception */ protected function _execSql($sql) { @@ -1054,13 +1112,15 @@ protected function _execSql($sql) * Creates a schema instance for the current database. * * @return Schema + * + * @throws Exception */ public function createSchema() { - $namespaces = []; + $schemaNames = []; if ($this->_platform->supportsSchemas()) { - $namespaces = $this->listNamespaceNames(); + $schemaNames = $this->listNamespaceNames(); } $sequences = []; @@ -1071,13 +1131,15 @@ public function createSchema() $tables = $this->listTables(); - return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces); + return new Schema($tables, $sequences, $this->createSchemaConfig(), $schemaNames); } /** * Creates the configuration for this schema. * * @return SchemaConfig + * + * @throws Exception */ public function createSchemaConfig() { @@ -1114,10 +1176,18 @@ public function createSchemaConfig() * returns the name of the currently connected database. * * @return string[] + * + * @throws Exception */ public function getSchemaSearchPaths() { - return [$this->_conn->getDatabase()]; + $database = $this->_conn->getDatabase(); + + if ($database !== null) { + return [$database]; + } + + return []; } /** @@ -1131,7 +1201,7 @@ public function getSchemaSearchPaths() */ public function extractDoctrineTypeFromComment($comment, $currentType) { - if ($comment !== null && preg_match('(\(DC2Type:(((?!\)).)+)\))', $comment, $match)) { + if ($comment !== null && preg_match('(\(DC2Type:(((?!\)).)+)\))', $comment, $match) === 1) { return $match[1]; } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php b/app/vendor/doctrine/dbal/src/Schema/Column.php similarity index 94% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php rename to app/vendor/doctrine/dbal/src/Schema/Column.php index b2392d41b..714bbc428 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php +++ b/app/vendor/doctrine/dbal/src/Schema/Column.php @@ -2,8 +2,8 @@ namespace Doctrine\DBAL\Schema; +use Doctrine\DBAL\Schema\Exception\UnknownColumnOption; use Doctrine\DBAL\Types\Type; -use Doctrine\Deprecations\Deprecation; use function array_merge; use function is_numeric; @@ -58,6 +58,8 @@ class Column extends AbstractAsset * * @param string $name * @param mixed[] $options + * + * @throws SchemaException */ public function __construct($name, Type $type, array $options = []) { @@ -70,22 +72,16 @@ public function __construct($name, Type $type, array $options = []) * @param mixed[] $options * * @return Column + * + * @throws SchemaException */ public function setOptions(array $options) { foreach ($options as $name => $value) { $method = 'set' . $name; + if (! method_exists($this, $method)) { - // next major: throw an exception - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/2846', - 'The "%s" column option is not supported,' . - ' setting unknown options is deprecated and will cause an error in Doctrine DBAL 3.0', - $name - ); - - continue; + throw UnknownColumnOption::new($name); } $this->$method($value); diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php b/app/vendor/doctrine/dbal/src/Schema/ColumnDiff.php similarity index 87% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php rename to app/vendor/doctrine/dbal/src/Schema/ColumnDiff.php index 2f6ae6520..c9c2a52bc 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php +++ b/app/vendor/doctrine/dbal/src/Schema/ColumnDiff.php @@ -44,7 +44,7 @@ public function __construct( */ public function hasChanged($propertyName) { - return in_array($propertyName, $this->changedProperties); + return in_array($propertyName, $this->changedProperties, true); } /** @@ -52,7 +52,7 @@ public function hasChanged($propertyName) */ public function getOldColumnName() { - $quote = $this->fromColumn && $this->fromColumn->isQuoted(); + $quote = $this->fromColumn !== null && $this->fromColumn->isQuoted(); return new Identifier($this->oldColumnName, $quote); } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php b/app/vendor/doctrine/dbal/src/Schema/Comparator.php similarity index 94% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php rename to app/vendor/doctrine/dbal/src/Schema/Comparator.php index 7e24b3e8c..7ddf5238d 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php +++ b/app/vendor/doctrine/dbal/src/Schema/Comparator.php @@ -9,7 +9,6 @@ use function array_keys; use function array_map; use function array_merge; -use function array_shift; use function array_unique; use function assert; use function count; @@ -23,6 +22,8 @@ class Comparator { /** * @return SchemaDiff + * + * @throws SchemaException */ public static function compareSchemas(Schema $fromSchema, Schema $toSchema) { @@ -39,6 +40,8 @@ public static function compareSchemas(Schema $fromSchema, Schema $toSchema) * stored in $toSchema. * * @return SchemaDiff + * + * @throws SchemaException */ public function compare(Schema $fromSchema, Schema $toSchema) { @@ -193,6 +196,8 @@ public function diffSequence(Sequence $sequence1, Sequence $sequence2) * If there are no differences this method returns the boolean false. * * @return TableDiff|false + * + * @throws SchemaException */ public function diffTable(Table $fromTable, Table $toTable) { @@ -225,7 +230,7 @@ public function diffTable(Table $fromTable, Table $toTable) // See if column has changed properties in "to" table. $changedProperties = $this->diffColumn($column, $toTable->getColumn($columnName)); - if (empty($changedProperties)) { + if (count($changedProperties) === 0) { continue; } @@ -304,7 +309,7 @@ public function diffTable(Table $fromTable, Table $toTable) $changes++; } - return $changes ? $tableDifferences : false; + return $changes > 0 ? $tableDifferences : false; } /** @@ -451,14 +456,6 @@ public function diffColumn(Column $column1, Column $column2) $changedProperties[] = $property; } - // This is a very nasty hack to make comparator work with the legacy json_array type, - // which should be killed in v3 - if ($this->isALegacyJsonComparison($properties1['type'], $properties2['type'])) { - array_shift($changedProperties); - - $changedProperties[] = 'comment'; - } - // Null values need to be checked additionally as they tell whether to create or drop a default value. // null != 0, null != false, null != '' etc. This affects platform's table alteration SQL generation. if ( @@ -473,8 +470,8 @@ public function diffColumn(Column $column1, Column $column2) $properties1['type'] instanceof Types\BinaryType ) { // check if value of length is set at all, default value assumed otherwise. - $length1 = $properties1['length'] ?: 255; - $length2 = $properties2['length'] ?: 255; + $length1 = $properties1['length'] ?? 255; + $length2 = $properties2['length'] ?? 255; if ($length1 !== $length2) { $changedProperties[] = 'length'; } @@ -483,7 +480,7 @@ public function diffColumn(Column $column1, Column $column2) $changedProperties[] = 'fixed'; } } elseif ($properties1['type'] instanceof Types\DecimalType) { - if (($properties1['precision'] ?: 10) !== ($properties2['precision'] ?: 10)) { + if (($properties1['precision'] ?? 10) !== ($properties2['precision'] ?? 10)) { $changedProperties[] = 'precision'; } @@ -526,21 +523,6 @@ public function diffColumn(Column $column1, Column $column2) return array_unique($changedProperties); } - /** - * TODO: kill with fire on v3.0 - * - * @deprecated - */ - private function isALegacyJsonComparison(Types\Type $one, Types\Type $other): bool - { - if (! $one instanceof Types\JsonType || ! $other instanceof Types\JsonType) { - return false; - } - - return (! $one instanceof Types\JsonArrayType && $other instanceof Types\JsonArrayType) - || (! $other instanceof Types\JsonArrayType && $one instanceof Types\JsonArrayType); - } - /** * Finds the difference between the indexes $index1 and $index2. * diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php b/app/vendor/doctrine/dbal/src/Schema/Constraint.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php rename to app/vendor/doctrine/dbal/src/Schema/Constraint.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php b/app/vendor/doctrine/dbal/src/Schema/DB2SchemaManager.php similarity index 93% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php rename to app/vendor/doctrine/dbal/src/Schema/DB2SchemaManager.php index 9b01e8732..82c287e00 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php +++ b/app/vendor/doctrine/dbal/src/Schema/DB2SchemaManager.php @@ -2,8 +2,10 @@ namespace Doctrine\DBAL\Schema; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\DB2Platform; use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Types\Types; use function array_change_key_case; use function assert; @@ -28,8 +30,7 @@ class DB2SchemaManager extends AbstractSchemaManager */ public function listTableNames() { - $sql = $this->_platform->getListTablesSQL(); - $sql .= ' AND CREATOR = UPPER(' . $this->_conn->quote($this->_conn->getUsername()) . ')'; + $sql = $this->_platform->getListTablesSQL() . ' AND CREATOR = CURRENT_USER'; $tables = $this->_conn->fetchAllAssociative($sql); @@ -38,6 +39,8 @@ public function listTableNames() /** * {@inheritdoc} + * + * @throws Exception */ protected function _getPortableTableColumnDefinition($tableColumn) { @@ -53,7 +56,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) if ($tableColumn['default'] !== null && $tableColumn['default'] !== 'NULL') { $default = $tableColumn['default']; - if (preg_match('/^\'(.*)\'$/s', $default, $matches)) { + if (preg_match('/^\'(.*)\'$/s', $default, $matches) === 1) { $default = str_replace("''", "'", $matches[1]); } } @@ -67,11 +70,19 @@ protected function _getPortableTableColumnDefinition($tableColumn) switch (strtolower($tableColumn['typename'])) { case 'varchar': + if ($tableColumn['codepage'] === 0) { + $type = Types::BINARY; + } + $length = $tableColumn['length']; $fixed = false; break; case 'character': + if ($tableColumn['codepage'] === 0) { + $type = Types::BINARY; + } + $length = $tableColumn['length']; $fixed = true; break; diff --git a/app/vendor/doctrine/dbal/src/Schema/Exception/InvalidTableName.php b/app/vendor/doctrine/dbal/src/Schema/Exception/InvalidTableName.php new file mode 100644 index 000000000..440e60785 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Schema/Exception/InvalidTableName.php @@ -0,0 +1,20 @@ +_options[$event])) { $onEvent = strtoupper($this->_options[$event]); - if (! in_array($onEvent, ['NO ACTION', 'RESTRICT'])) { + if ($onEvent !== 'NO ACTION' && $onEvent !== 'RESTRICT') { return $onEvent; } } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Identifier.php b/app/vendor/doctrine/dbal/src/Schema/Identifier.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Identifier.php rename to app/vendor/doctrine/dbal/src/Schema/Identifier.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php b/app/vendor/doctrine/dbal/src/Schema/Index.php similarity index 96% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php rename to app/vendor/doctrine/dbal/src/Schema/Index.php index 4880da717..508aa4031 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php +++ b/app/vendor/doctrine/dbal/src/Schema/Index.php @@ -11,7 +11,6 @@ use function array_search; use function array_shift; use function count; -use function is_string; use function strtolower; class Index extends AbstractAsset implements Constraint @@ -44,7 +43,7 @@ class Index extends AbstractAsset implements Constraint * @todo $_flags should eventually be refactored into options * @var mixed[] */ - private $options = []; + private $options; /** * @param string $name @@ -79,18 +78,10 @@ public function __construct( } /** - * @param string $column - * - * @return void - * * @throws InvalidArgumentException */ - protected function _addColumn($column) + protected function _addColumn(string $column): void { - if (! is_string($column)) { - throw new InvalidArgumentException('Expecting a string as Index Column'); - } - $this->_columns[$column] = new Identifier($column); } @@ -172,7 +163,7 @@ public function hasColumnAtPosition($name, $pos = 0) $name = $this->trimQuotes(strtolower($name)); $indexColumns = array_map('strtolower', $this->getUnquotedColumns()); - return array_search($name, $indexColumns) === $pos; + return array_search($name, $indexColumns, true) === $pos; } /** diff --git a/app/vendor/doctrine/dbal/src/Schema/MySQLSchemaManager.php b/app/vendor/doctrine/dbal/src/Schema/MySQLSchemaManager.php new file mode 100644 index 000000000..4d54f7159 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Schema/MySQLSchemaManager.php @@ -0,0 +1,381 @@ + "\0", + "\\'" => "'", + '\\"' => '"', + '\\b' => "\b", + '\\n' => "\n", + '\\r' => "\r", + '\\t' => "\t", + '\\Z' => "\x1a", + '\\\\' => '\\', + '\\%' => '%', + '\\_' => '_', + + // Internally, MariaDB escapes single quotes using the standard syntax + "''" => "'", + ]; + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return array_shift($table); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableUserDefinition($user) + { + return [ + 'user' => $user['User'], + 'password' => $user['Password'], + ]; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) + { + foreach ($tableIndexes as $k => $v) { + $v = array_change_key_case($v, CASE_LOWER); + if ($v['key_name'] === 'PRIMARY') { + $v['primary'] = true; + } else { + $v['primary'] = false; + } + + if (strpos($v['index_type'], 'FULLTEXT') !== false) { + $v['flags'] = ['FULLTEXT']; + } elseif (strpos($v['index_type'], 'SPATIAL') !== false) { + $v['flags'] = ['SPATIAL']; + } + + // Ignore prohibited prefix `length` for spatial index + if (strpos($v['index_type'], 'SPATIAL') === false) { + $v['length'] = isset($v['sub_part']) ? (int) $v['sub_part'] : null; + } + + $tableIndexes[$k] = $v; + } + + return parent::_getPortableTableIndexesList($tableIndexes, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['Database']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); + + $dbType = strtolower($tableColumn['type']); + $dbType = strtok($dbType, '(), '); + assert(is_string($dbType)); + + $length = $tableColumn['length'] ?? strtok('(), '); + + $fixed = null; + + if (! isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $scale = null; + $precision = null; + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + + // In cases where not connected to a database DESCRIBE $table does not return 'Comment' + if (isset($tableColumn['comment'])) { + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + } + + switch ($dbType) { + case 'char': + case 'binary': + $fixed = true; + break; + + case 'float': + case 'double': + case 'real': + case 'numeric': + case 'decimal': + if ( + preg_match( + '([A-Za-z]+\(([0-9]+),([0-9]+)\))', + $tableColumn['type'], + $match + ) === 1 + ) { + $precision = $match[1]; + $scale = $match[2]; + $length = null; + } + + break; + + case 'tinytext': + $length = MySQLPlatform::LENGTH_LIMIT_TINYTEXT; + break; + + case 'text': + $length = MySQLPlatform::LENGTH_LIMIT_TEXT; + break; + + case 'mediumtext': + $length = MySQLPlatform::LENGTH_LIMIT_MEDIUMTEXT; + break; + + case 'tinyblob': + $length = MySQLPlatform::LENGTH_LIMIT_TINYBLOB; + break; + + case 'blob': + $length = MySQLPlatform::LENGTH_LIMIT_BLOB; + break; + + case 'mediumblob': + $length = MySQLPlatform::LENGTH_LIMIT_MEDIUMBLOB; + break; + + case 'tinyint': + case 'smallint': + case 'mediumint': + case 'int': + case 'integer': + case 'bigint': + case 'year': + $length = null; + break; + } + + if ($this->_platform instanceof MariaDb1027Platform) { + $columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default']); + } else { + $columnDefault = $tableColumn['default']; + } + + $options = [ + 'length' => $length !== null ? (int) $length : null, + 'unsigned' => strpos($tableColumn['type'], 'unsigned') !== false, + 'fixed' => (bool) $fixed, + 'default' => $columnDefault, + 'notnull' => $tableColumn['null'] !== 'YES', + 'scale' => null, + 'precision' => null, + 'autoincrement' => strpos($tableColumn['extra'], 'auto_increment') !== false, + 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' + ? $tableColumn['comment'] + : null, + ]; + + if ($scale !== null && $precision !== null) { + $options['scale'] = (int) $scale; + $options['precision'] = (int) $precision; + } + + $column = new Column($tableColumn['field'], Type::getType($type), $options); + + if (isset($tableColumn['characterset'])) { + $column->setPlatformOption('charset', $tableColumn['characterset']); + } + + if (isset($tableColumn['collation'])) { + $column->setPlatformOption('collation', $tableColumn['collation']); + } + + return $column; + } + + /** + * Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers. + * + * - Since MariaDb 10.2.7 column defaults stored in information_schema are now quoted + * to distinguish them from expressions (see MDEV-10134). + * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema + * as current_timestamp(), currdate(), currtime() + * - Quoted 'NULL' is not enforced by Maria, it is technically possible to have + * null in some circumstances (see https://jira.mariadb.org/browse/MDEV-14053) + * - \' is always stored as '' in information_schema (normalized) + * + * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ + * @link https://jira.mariadb.org/browse/MDEV-13132 + * + * @param string|null $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7 + */ + private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault): ?string + { + if ($columnDefault === 'NULL' || $columnDefault === null) { + return null; + } + + if (preg_match('/^\'(.*)\'$/', $columnDefault, $matches) === 1) { + return strtr($matches[1], self::MARIADB_ESCAPE_SEQUENCES); + } + + switch ($columnDefault) { + case 'current_timestamp()': + return $platform->getCurrentTimestampSQL(); + + case 'curdate()': + return $platform->getCurrentDateSQL(); + + case 'curtime()': + return $platform->getCurrentTimeSQL(); + } + + return $columnDefault; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = []; + foreach ($tableForeignKeys as $value) { + $value = array_change_key_case($value, CASE_LOWER); + if (! isset($list[$value['constraint_name']])) { + if (! isset($value['delete_rule']) || $value['delete_rule'] === 'RESTRICT') { + $value['delete_rule'] = null; + } + + if (! isset($value['update_rule']) || $value['update_rule'] === 'RESTRICT') { + $value['update_rule'] = null; + } + + $list[$value['constraint_name']] = [ + 'name' => $value['constraint_name'], + 'local' => [], + 'foreign' => [], + 'foreignTable' => $value['referenced_table_name'], + 'onDelete' => $value['delete_rule'], + 'onUpdate' => $value['update_rule'], + ]; + } + + $list[$value['constraint_name']]['local'][] = $value['column_name']; + $list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name']; + } + + $result = []; + foreach ($list as $constraint) { + $result[] = new ForeignKeyConstraint( + array_values($constraint['local']), + $constraint['foreignTable'], + array_values($constraint['foreign']), + $constraint['name'], + [ + 'onDelete' => $constraint['onDelete'], + 'onUpdate' => $constraint['onUpdate'], + ] + ); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function listTableDetails($name) + { + $table = parent::listTableDetails($name); + + $platform = $this->_platform; + assert($platform instanceof MySQLPlatform); + $sql = $platform->getListTableMetadataSQL($name); + + $tableOptions = $this->_conn->fetchAssociative($sql); + + if ($tableOptions === false) { + return $table; + } + + $table->addOption('engine', $tableOptions['ENGINE']); + + if ($tableOptions['TABLE_COLLATION'] !== null) { + $table->addOption('collation', $tableOptions['TABLE_COLLATION']); + } + + if ($tableOptions['AUTO_INCREMENT'] !== null) { + $table->addOption('autoincrement', $tableOptions['AUTO_INCREMENT']); + } + + $table->addOption('comment', $tableOptions['TABLE_COMMENT']); + $table->addOption('create_options', $this->parseCreateOptions($tableOptions['CREATE_OPTIONS'])); + + return $table; + } + + /** + * @return string[]|true[] + */ + private function parseCreateOptions(?string $string): array + { + $options = []; + + if ($string === null || $string === '') { + return $options; + } + + foreach (explode(' ', $string) as $pair) { + $parts = explode('=', $pair, 2); + + $options[$parts[0]] = $parts[1] ?? true; + } + + return $options; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php b/app/vendor/doctrine/dbal/src/Schema/OracleSchemaManager.php similarity index 78% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php rename to app/vendor/doctrine/dbal/src/Schema/OracleSchemaManager.php index 5676f04c4..e24f79ffb 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php +++ b/app/vendor/doctrine/dbal/src/Schema/OracleSchemaManager.php @@ -2,21 +2,17 @@ namespace Doctrine\DBAL\Schema; -use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\Driver\Exception; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Types\Type; -use Throwable; use function array_change_key_case; use function array_values; use function assert; use function preg_match; -use function sprintf; use function str_replace; use function strpos; use function strtolower; -use function strtoupper; use function trim; use const CASE_LOWER; @@ -26,35 +22,6 @@ */ class OracleSchemaManager extends AbstractSchemaManager { - /** - * {@inheritdoc} - */ - public function dropDatabase($database) - { - try { - parent::dropDatabase($database); - } catch (DBALException $exception) { - $exception = $exception->getPrevious(); - assert($exception instanceof Throwable); - - if (! $exception instanceof Exception) { - throw $exception; - } - - // If we have a error code 1940 (ORA-01940), the drop database operation failed - // because of active connections on the database. - // To force dropping the database, we first have to close all active connections - // on that database and issue the drop database operation again. - if ($exception->getErrorCode() !== 1940) { - throw $exception; - } - - $this->killUserSessions($database); - - parent::dropDatabase($database); - } - } - /** * {@inheritdoc} */ @@ -127,7 +94,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) $dbType = strtolower($tableColumn['data_type']); if (strpos($dbType, 'timestamp(') === 0) { - if (strpos($dbType, 'with time zone')) { + if (strpos($dbType, 'with time zone') !== false) { $dbType = 'timestamptz'; } else { $dbType = 'timestamp'; @@ -149,7 +116,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) if ($tableColumn['data_default'] !== null) { // Default values returned from database are represented as literal expressions - if (preg_match('/^\'(.*)\'$/s', $tableColumn['data_default'], $matches)) { + if (preg_match('/^\'(.*)\'$/s', $tableColumn['data_default'], $matches) === 1) { $tableColumn['data_default'] = str_replace("''", "'", $matches[1]); } } @@ -187,6 +154,11 @@ protected function _getPortableTableColumnDefinition($tableColumn) $fixed = false; break; + case 'raw': + $length = $tableColumn['data_length']; + $fixed = true; + break; + case 'char': case 'nchar': $length = $tableColumn['char_length']; @@ -267,18 +239,6 @@ protected function _getPortableSequenceDefinition($sequence) ); } - /** - * {@inheritdoc} - * - * @deprecated - */ - protected function _getPortableFunctionDefinition($function) - { - $function = array_change_key_case($function, CASE_LOWER); - - return $function['name']; - } - /** * {@inheritdoc} */ @@ -291,17 +251,9 @@ protected function _getPortableDatabaseDefinition($database) /** * {@inheritdoc} - * - * @param string|null $database - * - * Calling this method without an argument or by passing NULL is deprecated. */ - public function createDatabase($database = null) + public function createDatabase($database) { - if ($database === null) { - $database = $this->_conn->getDatabase(); - } - $statement = 'CREATE USER ' . $database; $params = $this->_conn->getParams(); @@ -317,9 +269,13 @@ public function createDatabase($database = null) } /** + * @internal The method should be only used from within the OracleSchemaManager class hierarchy. + * * @param string $table * * @return bool + * + * @throws Exception */ public function dropAutoincrement($table) { @@ -355,51 +311,13 @@ public function dropTable($name) */ private function getQuotedIdentifierName($identifier) { - if (preg_match('/[a-z]/', $identifier)) { + if (preg_match('/[a-z]/', $identifier) === 1) { return $this->_platform->quoteIdentifier($identifier); } return $identifier; } - /** - * Kills sessions connected with the given user. - * - * This is useful to force DROP USER operations which could fail because of active user sessions. - * - * @param string $user The name of the user to kill sessions for. - * - * @return void - */ - private function killUserSessions($user) - { - $sql = <<_conn->fetchAllAssociative($sql, [strtoupper($user)]); - - foreach ($activeUserSessions as $activeUserSession) { - $activeUserSession = array_change_key_case($activeUserSession, CASE_LOWER); - - $this->_execSql( - sprintf( - "ALTER SYSTEM KILL SESSION '%s, %s' IMMEDIATE", - $activeUserSession['sid'], - $activeUserSession['serial#'] - ) - ); - } - } - /** * {@inheritdoc} */ diff --git a/app/vendor/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php b/app/vendor/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php new file mode 100644 index 000000000..e353b53e6 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php @@ -0,0 +1,559 @@ +listNamespaceNames(); + } + + /** + * {@inheritDoc} + */ + public function listSchemaNames(): array + { + return $this->_conn->fetchFirstColumn( + <<<'SQL' +SELECT schema_name +FROM information_schema.schemata +WHERE schema_name NOT LIKE 'pg\_%' +AND schema_name != 'information_schema' +SQL + ); + } + + /** + * {@inheritDoc} + */ + public function getSchemaSearchPaths() + { + $params = $this->_conn->getParams(); + + $searchPaths = $this->_conn->fetchOne('SHOW search_path'); + assert($searchPaths !== false); + + $schema = explode(',', $searchPaths); + + if (isset($params['user'])) { + $schema = str_replace('"$user"', $params['user'], $schema); + } + + return array_map('trim', $schema); + } + + /** + * Gets names of all existing schemas in the current users search path. + * + * This is a PostgreSQL only function. + * + * @internal The method should be only used from within the PostgreSQLSchemaManager class hierarchy. + * + * @return string[] + */ + public function getExistingSchemaSearchPaths() + { + if ($this->existingSchemaPaths === null) { + $this->determineExistingSchemaSearchPaths(); + } + + return $this->existingSchemaPaths; + } + + /** + * Sets or resets the order of the existing schemas in the current search path of the user. + * + * This is a PostgreSQL only function. + * + * @internal The method should be only used from within the PostgreSQLSchemaManager class hierarchy. + * + * @return void + */ + public function determineExistingSchemaSearchPaths() + { + $names = $this->listSchemaNames(); + $paths = $this->getSchemaSearchPaths(); + + $this->existingSchemaPaths = array_filter($paths, static function ($v) use ($names): bool { + return in_array($v, $names, true); + }); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $onUpdate = null; + $onDelete = null; + + if ( + preg_match( + '(ON UPDATE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', + $tableForeignKey['condef'], + $match + ) === 1 + ) { + $onUpdate = $match[1]; + } + + if ( + preg_match( + '(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', + $tableForeignKey['condef'], + $match + ) === 1 + ) { + $onDelete = $match[1]; + } + + $result = preg_match('/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)/', $tableForeignKey['condef'], $values); + assert($result === 1); + + // PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get + // the idea to trim them here. + $localColumns = array_map('trim', explode(',', $values[1])); + $foreignColumns = array_map('trim', explode(',', $values[3])); + $foreignTable = $values[2]; + + return new ForeignKeyConstraint( + $localColumns, + $foreignTable, + $foreignColumns, + $tableForeignKey['conname'], + ['onUpdate' => $onUpdate, 'onDelete' => $onDelete] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTriggerDefinition($trigger) + { + return $trigger['trigger_name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View($view['schemaname'] . '.' . $view['viewname'], $view['definition']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableUserDefinition($user) + { + return [ + 'user' => $user['usename'], + 'password' => $user['passwd'], + ]; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + $schemas = $this->getExistingSchemaSearchPaths(); + $firstSchema = array_shift($schemas); + + if ($table['schema_name'] === $firstSchema) { + return $table['table_name']; + } + + return $table['schema_name'] . '.' . $table['table_name']; + } + + /** + * {@inheritdoc} + * + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) + { + $buffer = []; + foreach ($tableIndexes as $row) { + $colNumbers = array_map('intval', explode(' ', $row['indkey'])); + $columnNameSql = sprintf( + 'SELECT attnum, attname FROM pg_attribute WHERE attrelid=%d AND attnum IN (%s) ORDER BY attnum ASC', + $row['indrelid'], + implode(' ,', $colNumbers) + ); + + $indexColumns = $this->_conn->fetchAllAssociative($columnNameSql); + + // required for getting the order of the columns right. + foreach ($colNumbers as $colNum) { + foreach ($indexColumns as $colRow) { + if ($colNum !== $colRow['attnum']) { + continue; + } + + $buffer[] = [ + 'key_name' => $row['relname'], + 'column_name' => trim($colRow['attname']), + 'non_unique' => ! $row['indisunique'], + 'primary' => $row['indisprimary'], + 'where' => $row['where'], + ]; + } + } + } + + return parent::_getPortableTableIndexesList($buffer, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['datname']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequencesList($sequences) + { + $sequenceDefinitions = []; + + foreach ($sequences as $sequence) { + if ($sequence['schemaname'] !== 'public') { + $sequenceName = $sequence['schemaname'] . '.' . $sequence['relname']; + } else { + $sequenceName = $sequence['relname']; + } + + $sequenceDefinitions[$sequenceName] = $sequence; + } + + $list = []; + + foreach ($this->filterAssetNames(array_keys($sequenceDefinitions)) as $sequenceName) { + $list[] = $this->_getPortableSequenceDefinition($sequenceDefinitions[$sequenceName]); + } + + return $list; + } + + /** + * {@inheritdoc} + * + * @deprecated Use {@link listSchemaNames()} instead. + */ + protected function getPortableNamespaceDefinition(array $namespace) + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'PostgreSQLSchemaManager::getPortableNamespaceDefinition() is deprecated,' + . ' use PostgreSQLSchemaManager::listSchemaNames() instead.' + ); + + return $namespace['nspname']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + if ($sequence['schemaname'] !== 'public') { + $sequenceName = $sequence['schemaname'] . '.' . $sequence['relname']; + } else { + $sequenceName = $sequence['relname']; + } + + if (! isset($sequence['increment_by'], $sequence['min_value'])) { + /** @var string[] $data */ + $data = $this->_conn->fetchAssociative( + 'SELECT min_value, increment_by FROM ' . $this->_platform->quoteIdentifier($sequenceName) + ); + + $sequence += $data; + } + + return new Sequence($sequenceName, (int) $sequence['increment_by'], (int) $sequence['min_value']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); + + if (strtolower($tableColumn['type']) === 'varchar' || strtolower($tableColumn['type']) === 'bpchar') { + // get length from varchar definition + $length = preg_replace('~.*\(([0-9]*)\).*~', '$1', $tableColumn['complete_type']); + $tableColumn['length'] = $length; + } + + $matches = []; + + $autoincrement = false; + if (preg_match("/^nextval\('(.*)'(::.*)?\)$/", $tableColumn['default'], $matches) === 1) { + $tableColumn['sequence'] = $matches[1]; + $tableColumn['default'] = null; + $autoincrement = true; + } + + if (preg_match("/^['(](.*)[')]::/", $tableColumn['default'], $matches) === 1) { + $tableColumn['default'] = $matches[1]; + } elseif (preg_match('/^NULL::/', $tableColumn['default']) === 1) { + $tableColumn['default'] = null; + } + + $length = $tableColumn['length'] ?? null; + if ($length === '-1' && isset($tableColumn['atttypmod'])) { + $length = $tableColumn['atttypmod'] - 4; + } + + if ((int) $length <= 0) { + $length = null; + } + + $fixed = null; + + if (! isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $precision = null; + $scale = null; + $jsonb = null; + + $dbType = strtolower($tableColumn['type']); + if ( + strlen($tableColumn['domain_type']) > 0 + && ! $this->_platform->hasDoctrineTypeMappingFor($tableColumn['type']) + ) { + $dbType = strtolower($tableColumn['domain_type']); + $tableColumn['complete_type'] = $tableColumn['domain_complete_type']; + } + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + + switch ($dbType) { + case 'smallint': + case 'int2': + $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); + $length = null; + break; + + case 'int': + case 'int4': + case 'integer': + $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); + $length = null; + break; + + case 'bigint': + case 'int8': + $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); + $length = null; + break; + + case 'bool': + case 'boolean': + if ($tableColumn['default'] === 'true') { + $tableColumn['default'] = true; + } + + if ($tableColumn['default'] === 'false') { + $tableColumn['default'] = false; + } + + $length = null; + break; + + case 'text': + case '_varchar': + case 'varchar': + $tableColumn['default'] = $this->parseDefaultExpression($tableColumn['default']); + $fixed = false; + break; + case 'interval': + $fixed = false; + break; + + case 'char': + case 'bpchar': + $fixed = true; + break; + + case 'float': + case 'float4': + case 'float8': + case 'double': + case 'double precision': + case 'real': + case 'decimal': + case 'money': + case 'numeric': + $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); + + if ( + preg_match( + '([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', + $tableColumn['complete_type'], + $match + ) === 1 + ) { + $precision = $match[1]; + $scale = $match[2]; + $length = null; + } + + break; + + case 'year': + $length = null; + break; + + // PostgreSQL 9.4+ only + case 'jsonb': + $jsonb = true; + break; + } + + if ( + $tableColumn['default'] !== null && preg_match( + "('([^']+)'::)", + $tableColumn['default'], + $match + ) === 1 + ) { + $tableColumn['default'] = $match[1]; + } + + $options = [ + 'length' => $length, + 'notnull' => (bool) $tableColumn['isnotnull'], + 'default' => $tableColumn['default'], + 'precision' => $precision, + 'scale' => $scale, + 'fixed' => $fixed, + 'unsigned' => false, + 'autoincrement' => $autoincrement, + 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' + ? $tableColumn['comment'] + : null, + ]; + + $column = new Column($tableColumn['field'], Type::getType($type), $options); + + if (isset($tableColumn['collation']) && ! empty($tableColumn['collation'])) { + $column->setPlatformOption('collation', $tableColumn['collation']); + } + + if ($column->getType()->getName() === Types::JSON) { + $column->setPlatformOption('jsonb', $jsonb); + } + + return $column; + } + + /** + * PostgreSQL 9.4 puts parentheses around negative numeric default values that need to be stripped eventually. + * + * @param mixed $defaultValue + * + * @return mixed + */ + private function fixVersion94NegativeNumericDefaultValue($defaultValue) + { + if (strpos($defaultValue, '(') === 0) { + return trim($defaultValue, '()'); + } + + return $defaultValue; + } + + /** + * Parses a default value expression as given by PostgreSQL + */ + private function parseDefaultExpression(?string $default): ?string + { + if ($default === null) { + return $default; + } + + return str_replace("''", "'", $default); + } + + /** + * {@inheritdoc} + */ + public function listTableDetails($name): Table + { + $table = parent::listTableDetails($name); + + $platform = $this->_platform; + assert($platform instanceof PostgreSQL94Platform); + $sql = $platform->getListTableMetadataSQL($name); + + $tableOptions = $this->_conn->fetchAssociative($sql); + + if ($tableOptions !== false) { + $table->addOption('comment', $tableOptions['table_comment']); + } + + return $table; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php b/app/vendor/doctrine/dbal/src/Schema/SQLServerSchemaManager.php similarity index 81% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php rename to app/vendor/doctrine/dbal/src/Schema/SQLServerSchemaManager.php index 63dc9b04f..a448dd147 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php +++ b/app/vendor/doctrine/dbal/src/Schema/SQLServerSchemaManager.php @@ -2,16 +2,14 @@ namespace Doctrine\DBAL\Schema; -use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\Driver\Exception; -use Doctrine\DBAL\Platforms\SQLServerPlatform; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\SQLServer2012Platform; use Doctrine\DBAL\Types\Type; +use Doctrine\Deprecations\Deprecation; use PDOException; -use Throwable; use function assert; use function count; -use function in_array; use function is_string; use function preg_match; use function sprintf; @@ -25,32 +23,17 @@ class SQLServerSchemaManager extends AbstractSchemaManager { /** - * {@inheritdoc} + * {@inheritDoc} */ - public function dropDatabase($database) + public function listSchemaNames(): array { - try { - parent::dropDatabase($database); - } catch (DBALException $exception) { - $exception = $exception->getPrevious(); - assert($exception instanceof Throwable); - - if (! $exception instanceof Exception) { - throw $exception; - } - - // If we have a error code 3702, the drop database operation failed - // because of active connections on the database. - // To force dropping the database, we first have to close all active connections - // on that database and issue the drop database operation again. - if ($exception->getErrorCode() !== 3702) { - throw $exception; - } - - $this->closeActiveDatabaseConnections($database); - - parent::dropDatabase($database); - } + return $this->_conn->fetchFirstColumn( + <<<'SQL' +SELECT name +FROM sys.schemas +WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys') +SQL + ); } /** @@ -95,6 +78,13 @@ protected function _getPortableTableColumnDefinition($tableColumn) $dbType = 'text'; } + break; + + case 'varbinary': + if ($length === -1) { + $dbType = 'blob'; + } + break; } @@ -107,7 +97,6 @@ protected function _getPortableTableColumnDefinition($tableColumn) $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); $options = [ - 'length' => $length === 0 || ! in_array($type, ['text', 'string']) ? null : $length, 'unsigned' => false, 'fixed' => (bool) $fixed, 'default' => $default, @@ -118,6 +107,10 @@ protected function _getPortableTableColumnDefinition($tableColumn) 'comment' => $tableColumn['comment'] !== '' ? $tableColumn['comment'] : null, ]; + if ($length !== 0 && ($type === 'text' || $type === 'string' || $type === 'binary')) { + $options['length'] = $length; + } + $column = new Column($tableColumn['name'], Type::getType($type), $options); if (isset($tableColumn['collation']) && $tableColumn['collation'] !== 'NULL') { @@ -137,7 +130,7 @@ private function parseDefaultExpression(string $value): ?string return null; } - if (preg_match('/^\'(.*)\'$/s', $value, $matches)) { + if (preg_match('/^\'(.*)\'$/s', $value, $matches) === 1) { $value = str_replace("''", "'", $matches[1]); } @@ -228,9 +221,18 @@ protected function _getPortableDatabaseDefinition($database) /** * {@inheritdoc} + * + * @deprecated Use {@link listSchemaNames()} instead. */ protected function getPortableNamespaceDefinition(array $namespace) { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4503', + 'SQLServerSchemaManager::getPortableNamespaceDefinition() is deprecated,' + . ' use SQLServerSchemaManager::listSchemaNames() instead.' + ); + return $namespace['name']; } @@ -258,7 +260,7 @@ public function listTableIndexes($table) } throw $e; - } catch (DBALException $e) { + } catch (Exception $e) { if (strpos($e->getMessage(), 'SQLSTATE [01000, 15472]') === 0) { return []; } @@ -278,7 +280,7 @@ public function alterTable(TableDiff $tableDiff) foreach ($tableDiff->removedColumns as $col) { $columnConstraintSql = $this->getColumnConstraintSQL($tableDiff->name, $col->getName()); foreach ($this->_conn->fetchAllAssociative($columnConstraintSql) as $constraint) { - $this->_conn->exec( + $this->_conn->executeStatement( sprintf( 'ALTER TABLE %s DROP CONSTRAINT %s', $tableDiff->name, @@ -311,36 +313,17 @@ private function getColumnConstraintSQL($table, $column) ORDER BY Col.[Name]'; } - /** - * Closes currently active connections on the given database. - * - * This is useful to force DROP DATABASE operations which could fail because of active connections. - * - * @param string $database The name of the database to close currently active connections for. - * - * @return void - */ - private function closeActiveDatabaseConnections($database) - { - $database = new Identifier($database); - - $this->_execSql( - sprintf( - 'ALTER DATABASE %s SET SINGLE_USER WITH ROLLBACK IMMEDIATE', - $database->getQuotedName($this->_platform) - ) - ); - } - /** * @param string $name + * + * @throws Exception */ public function listTableDetails($name): Table { $table = parent::listTableDetails($name); $platform = $this->_platform; - assert($platform instanceof SQLServerPlatform); + assert($platform instanceof SQLServer2012Platform); $sql = $platform->getListTableMetadataSQL($name); $tableOptions = $this->_conn->fetchAssociative($sql); diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php b/app/vendor/doctrine/dbal/src/Schema/Schema.php similarity index 97% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php rename to app/vendor/doctrine/dbal/src/Schema/Schema.php index 24fc47b59..08eb26833 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php +++ b/app/vendor/doctrine/dbal/src/Schema/Schema.php @@ -58,6 +58,8 @@ class Schema extends AbstractAsset * @param Table[] $tables * @param Sequence[] $sequences * @param string[] $namespaces + * + * @throws SchemaException */ public function __construct( array $tables = [], @@ -70,7 +72,7 @@ public function __construct( } $this->_schemaConfig = $schemaConfig; - $this->_setName($schemaConfig->getName() ?: 'public'); + $this->_setName($schemaConfig->getName() ?? 'public'); foreach ($namespaces as $namespace) { $this->createNamespace($namespace); @@ -316,6 +318,8 @@ public function createNamespace($name) * @param string $name * * @return Table + * + * @throws SchemaException */ public function createTable($name) { @@ -336,6 +340,8 @@ public function createTable($name) * @param string $newName * * @return Schema + * + * @throws SchemaException */ public function renameTable($oldName, $newName) { @@ -354,6 +360,8 @@ public function renameTable($oldName, $newName) * @param string $name * * @return Schema + * + * @throws SchemaException */ public function dropTable($name) { @@ -372,6 +380,8 @@ public function dropTable($name) * @param int $initialValue * * @return Sequence + * + * @throws SchemaException */ public function createSequence($name, $allocationSize = 1, $initialValue = 1) { @@ -422,6 +432,8 @@ public function toDropSql(AbstractPlatform $platform) /** * @return string[] + * + * @throws SchemaException */ public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform) { @@ -433,6 +445,8 @@ public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform) /** * @return string[] + * + * @throws SchemaException */ public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform) { diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php b/app/vendor/doctrine/dbal/src/Schema/SchemaConfig.php similarity index 97% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php rename to app/vendor/doctrine/dbal/src/Schema/SchemaConfig.php index b8c3502f7..56d49c4a7 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php +++ b/app/vendor/doctrine/dbal/src/Schema/SchemaConfig.php @@ -13,7 +13,7 @@ class SchemaConfig /** @var int */ protected $maxIdentifierLength = 63; - /** @var string */ + /** @var string|null */ protected $name; /** @var mixed[] */ @@ -58,7 +58,7 @@ public function getMaxIdentifierLength() /** * Gets the default namespace of schema objects. * - * @return string + * @return string|null */ public function getName() { diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaDiff.php b/app/vendor/doctrine/dbal/src/Schema/SchemaDiff.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaDiff.php rename to app/vendor/doctrine/dbal/src/Schema/SchemaDiff.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php b/app/vendor/doctrine/dbal/src/Schema/SchemaException.php similarity index 90% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php rename to app/vendor/doctrine/dbal/src/Schema/SchemaException.php index 64f4b250a..d4e22380e 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php +++ b/app/vendor/doctrine/dbal/src/Schema/SchemaException.php @@ -22,7 +22,8 @@ class SchemaException extends Exception public const SEQUENCE_ALREADY_EXISTS = 80; public const INDEX_INVALID_NAME = 90; public const FOREIGNKEY_DOESNT_EXIST = 100; - public const NAMESPACE_ALREADY_EXISTS = 110; + public const CONSTRAINT_DOESNT_EXIST = 110; + public const NAMESPACE_ALREADY_EXISTS = 120; /** * @param string $tableName @@ -146,6 +147,20 @@ public static function sequenceDoesNotExist($name) return new self("There exists no sequence with the name '" . $name . "'.", self::SEQUENCE_DOENST_EXIST); } + /** + * @param string $constraintName + * @param string $table + * + * @return SchemaException + */ + public static function uniqueConstraintDoesNotExist($constraintName, $table) + { + return new self( + sprintf('There exists no unique constraint with the name "%s" on table "%s".', $constraintName, $table), + self::CONSTRAINT_DOESNT_EXIST + ); + } + /** * @param string $fkName * @param string $table diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php b/app/vendor/doctrine/dbal/src/Schema/Sequence.php similarity index 90% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php rename to app/vendor/doctrine/dbal/src/Schema/Sequence.php index 1cba86c08..a634e84fa 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php +++ b/app/vendor/doctrine/dbal/src/Schema/Sequence.php @@ -66,7 +66,11 @@ public function getCache() */ public function setAllocationSize($allocationSize) { - $this->allocationSize = (int) $allocationSize ?: 1; + if ($allocationSize > 0) { + $this->allocationSize = $allocationSize; + } else { + $this->allocationSize = 1; + } return $this; } @@ -78,7 +82,11 @@ public function setAllocationSize($allocationSize) */ public function setInitialValue($initialValue) { - $this->initialValue = (int) $initialValue ?: 1; + if ($initialValue > 0) { + $this->initialValue = $initialValue; + } else { + $this->initialValue = 1; + } return $this; } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php b/app/vendor/doctrine/dbal/src/Schema/SqliteSchemaManager.php similarity index 95% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php rename to app/vendor/doctrine/dbal/src/Schema/SqliteSchemaManager.php index 841568ab2..140ebb3c0 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php +++ b/app/vendor/doctrine/dbal/src/Schema/SqliteSchemaManager.php @@ -122,7 +122,7 @@ public function listTableForeignKeys($table, $database = null) $createSql = $this->getCreateTableSQL($table); if ( - $createSql !== null && preg_match_all( + preg_match_all( '# (?:CONSTRAINT\s+([^\s]+)\s+)? (?:FOREIGN\s+KEY[^\)]+\)\s*)? @@ -134,7 +134,7 @@ public function listTableForeignKeys($table, $database = null) )?#isx', $createSql, $match - ) + ) > 0 ) { $names = array_reverse($match[1]); $deferrable = array_reverse($match[2]); @@ -235,21 +235,6 @@ static function (array $a, array $b): int { return parent::_getPortableTableIndexesList($indexBuffer, $tableName); } - /** - * @deprecated - * - * @param array $tableIndex - * - * @return array - */ - protected function _getPortableTableIndexDefinition($tableIndex) - { - return [ - 'name' => $tableIndex['name'], - 'unique' => (bool) $tableIndex['unique'], - ]; - } - /** * {@inheritdoc} */ @@ -285,7 +270,7 @@ protected function _getPortableTableColumnList($table, $database, $tableColumns) } // inspect column collation and comments - $createSql = $this->getCreateTableSQL($table) ?? ''; + $createSql = $this->getCreateTableSQL($table); foreach ($list as $columnName => $column) { $type = $column->getType(); @@ -293,7 +278,7 @@ protected function _getPortableTableColumnList($table, $database, $tableColumns) if ($type instanceof StringType || $type instanceof TextType) { $column->setPlatformOption( 'collation', - $this->parseColumnCollationFromSQL($columnName, $createSql) ?: 'BINARY' + $this->parseColumnCollationFromSQL($columnName, $createSql) ?? 'BINARY' ); } @@ -347,7 +332,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) if ($default !== null) { // SQLite returns the default value as a literal expression, so we need to parse it - if (preg_match('/^\'(.*)\'$/s', $default, $matches)) { + if (preg_match('/^\'(.*)\'$/s', $default, $matches) === 1) { $default = str_replace("''", "'", $matches[1]); } } @@ -535,9 +520,12 @@ private function parseColumnCommentFromSQL(string $column, string $sql): ?string return $comment === '' ? null : $comment; } - private function getCreateTableSQL(string $table): ?string + /** + * @throws Exception + */ + private function getCreateTableSQL(string $table): string { - return $this->_conn->fetchColumn( + $sql = $this->_conn->fetchOne( <<<'SQL' SELECT sql FROM ( @@ -552,17 +540,25 @@ private function getCreateTableSQL(string $table): ?string SQL , [$table] - ) ?: null; + ); + + if ($sql !== false) { + return $sql; + } + + return ''; } /** + * {@inheritDoc} + * * @param string $name */ public function listTableDetails($name): Table { $table = parent::listTableDetails($name); - $tableCreateSql = $this->getCreateTableSQL($name) ?? ''; + $tableCreateSql = $this->getCreateTableSQL($name); $comment = $this->parseTableCommentFromSQL($name, $tableCreateSql); @@ -572,4 +568,13 @@ public function listTableDetails($name): Table return $table; } + + /** + * {@inheritDoc} + */ + public function getSchemaSearchPaths() + { + // SQLite does not support schemas or databases + return []; + } } diff --git a/app/vendor/doctrine/dbal/src/Schema/Table.php b/app/vendor/doctrine/dbal/src/Schema/Table.php new file mode 100644 index 000000000..d330d03f4 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Schema/Table.php @@ -0,0 +1,1005 @@ + [], + ]; + + /** @var SchemaConfig|null */ + protected $_schemaConfig; + + /** @var Index[] */ + private $implicitIndexes = []; + + /** + * @param Column[] $columns + * @param Index[] $indexes + * @param UniqueConstraint[] $uniqueConstraints + * @param ForeignKeyConstraint[] $fkConstraints + * @param mixed[] $options + * + * @throws SchemaException + * @throws Exception + */ + public function __construct( + string $name, + array $columns = [], + array $indexes = [], + array $uniqueConstraints = [], + array $fkConstraints = [], + array $options = [] + ) { + if ($name === '') { + throw InvalidTableName::new($name); + } + + $this->_setName($name); + + foreach ($columns as $column) { + $this->_addColumn($column); + } + + foreach ($indexes as $idx) { + $this->_addIndex($idx); + } + + foreach ($uniqueConstraints as $uniqueConstraint) { + $this->_addUniqueConstraint($uniqueConstraint); + } + + foreach ($fkConstraints as $constraint) { + $this->_addForeignKeyConstraint($constraint); + } + + $this->_options = array_merge($this->_options, $options); + } + + /** + * @return void + */ + public function setSchemaConfig(SchemaConfig $schemaConfig) + { + $this->_schemaConfig = $schemaConfig; + } + + /** + * @return int + */ + protected function _getMaxIdentifierLength() + { + if ($this->_schemaConfig instanceof SchemaConfig) { + return $this->_schemaConfig->getMaxIdentifierLength(); + } + + return 63; + } + + /** + * Sets the Primary Key. + * + * @param string[] $columnNames + * @param string|false $indexName + * + * @return self + * + * @throws SchemaException + */ + public function setPrimaryKey(array $columnNames, $indexName = false) + { + if ($indexName === false) { + $indexName = 'primary'; + } + + $this->_addIndex($this->_createIndex($columnNames, $indexName, true, true)); + + foreach ($columnNames as $columnName) { + $column = $this->getColumn($columnName); + $column->setNotnull(true); + } + + return $this; + } + + /** + * @param string[] $columnNames + * @param string[] $flags + * @param mixed[] $options + * + * @return self + * + * @throws SchemaException + */ + public function addIndex(array $columnNames, ?string $indexName = null, array $flags = [], array $options = []) + { + if ($indexName === null) { + $indexName = $this->_generateIdentifierName( + array_merge([$this->getName()], $columnNames), + 'idx', + $this->_getMaxIdentifierLength() + ); + } + + return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options)); + } + + /** + * @param string[] $columnNames + * @param string[] $flags + * @param mixed[] $options + * + * @return self + */ + public function addUniqueConstraint( + array $columnNames, + ?string $indexName = null, + array $flags = [], + array $options = [] + ): Table { + if ($indexName === null) { + $indexName = $this->_generateIdentifierName( + array_merge([$this->getName()], $columnNames), + 'uniq', + $this->_getMaxIdentifierLength() + ); + } + + return $this->_addUniqueConstraint($this->_createUniqueConstraint($columnNames, $indexName, $flags, $options)); + } + + /** + * Drops the primary key from this table. + * + * @return void + * + * @throws SchemaException + */ + public function dropPrimaryKey() + { + if ($this->_primaryKeyName === null) { + return; + } + + $this->dropIndex($this->_primaryKeyName); + $this->_primaryKeyName = null; + } + + /** + * Drops an index from this table. + * + * @param string $name The index name. + * + * @return void + * + * @throws SchemaException If the index does not exist. + */ + public function dropIndex($name) + { + $name = $this->normalizeIdentifier($name); + + if (! $this->hasIndex($name)) { + throw SchemaException::indexDoesNotExist($name, $this->_name); + } + + unset($this->_indexes[$name]); + } + + /** + * @param string[] $columnNames + * @param string|null $indexName + * @param mixed[] $options + * + * @return self + * + * @throws SchemaException + */ + public function addUniqueIndex(array $columnNames, $indexName = null, array $options = []) + { + if ($indexName === null) { + $indexName = $this->_generateIdentifierName( + array_merge([$this->getName()], $columnNames), + 'uniq', + $this->_getMaxIdentifierLength() + ); + } + + return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options)); + } + + /** + * Renames an index. + * + * @param string $oldName The name of the index to rename from. + * @param string|null $newName The name of the index to rename to. + * If null is given, the index name will be auto-generated. + * + * @return self This table instance. + * + * @throws SchemaException If no index exists for the given current name + * or if an index with the given new name already exists on this table. + */ + public function renameIndex($oldName, $newName = null) + { + $oldName = $this->normalizeIdentifier($oldName); + $normalizedNewName = $this->normalizeIdentifier($newName); + + if ($oldName === $normalizedNewName) { + return $this; + } + + if (! $this->hasIndex($oldName)) { + throw SchemaException::indexDoesNotExist($oldName, $this->_name); + } + + if ($this->hasIndex($normalizedNewName)) { + throw SchemaException::indexAlreadyExists($normalizedNewName, $this->_name); + } + + $oldIndex = $this->_indexes[$oldName]; + + if ($oldIndex->isPrimary()) { + $this->dropPrimaryKey(); + + return $this->setPrimaryKey($oldIndex->getColumns(), $newName ?? false); + } + + unset($this->_indexes[$oldName]); + + if ($oldIndex->isUnique()) { + return $this->addUniqueIndex($oldIndex->getColumns(), $newName, $oldIndex->getOptions()); + } + + return $this->addIndex($oldIndex->getColumns(), $newName, $oldIndex->getFlags(), $oldIndex->getOptions()); + } + + /** + * Checks if an index begins in the order of the given columns. + * + * @param string[] $columnNames + * + * @return bool + */ + public function columnsAreIndexed(array $columnNames) + { + foreach ($this->getIndexes() as $index) { + if ($index->spansColumns($columnNames)) { + return true; + } + } + + return false; + } + + /** + * @param string[] $columnNames + * @param string $indexName + * @param bool $isUnique + * @param bool $isPrimary + * @param string[] $flags + * @param mixed[] $options + * + * @return Index + * + * @throws SchemaException + */ + private function _createIndex( + array $columnNames, + $indexName, + $isUnique, + $isPrimary, + array $flags = [], + array $options = [] + ) { + if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName)) === 1) { + throw SchemaException::indexNameInvalid($indexName); + } + + foreach ($columnNames as $columnName) { + if (! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + + return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options); + } + + /** + * @param string $name + * @param string $typeName + * @param mixed[] $options + * + * @return Column + * + * @throws SchemaException + */ + public function addColumn($name, $typeName, array $options = []) + { + $column = new Column($name, Type::getType($typeName), $options); + + $this->_addColumn($column); + + return $column; + } + + /** + * Change Column Details. + * + * @param string $name + * @param mixed[] $options + * + * @return self + * + * @throws SchemaException + */ + public function changeColumn($name, array $options) + { + $column = $this->getColumn($name); + $column->setOptions($options); + + return $this; + } + + /** + * Drops a Column from the Table. + * + * @param string $name + * + * @return self + */ + public function dropColumn($name) + { + $name = $this->normalizeIdentifier($name); + + unset($this->_columns[$name]); + + return $this; + } + + /** + * Adds a foreign key constraint. + * + * Name is inferred from the local columns. + * + * @param Table|string $foreignTable Table schema instance or table name + * @param string[] $localColumnNames + * @param string[] $foreignColumnNames + * @param mixed[] $options + * @param string|null $name + * + * @return self + * + * @throws SchemaException + */ + public function addForeignKeyConstraint( + $foreignTable, + array $localColumnNames, + array $foreignColumnNames, + array $options = [], + $name = null + ) { + if ($name === null) { + $name = $this->_generateIdentifierName( + array_merge((array) $this->getName(), $localColumnNames), + 'fk', + $this->_getMaxIdentifierLength() + ); + } + + if ($foreignTable instanceof Table) { + foreach ($foreignColumnNames as $columnName) { + if (! $foreignTable->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName()); + } + } + } + + foreach ($localColumnNames as $columnName) { + if (! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + + $constraint = new ForeignKeyConstraint( + $localColumnNames, + $foreignTable, + $foreignColumnNames, + $name, + $options + ); + + return $this->_addForeignKeyConstraint($constraint); + } + + /** + * @param string $name + * @param mixed $value + * + * @return self + */ + public function addOption($name, $value) + { + $this->_options[$name] = $value; + + return $this; + } + + /** + * @return void + * + * @throws SchemaException + */ + protected function _addColumn(Column $column) + { + $columnName = $column->getName(); + $columnName = $this->normalizeIdentifier($columnName); + + if (isset($this->_columns[$columnName])) { + throw SchemaException::columnAlreadyExists($this->getName(), $columnName); + } + + $this->_columns[$columnName] = $column; + } + + /** + * Adds an index to the table. + * + * @return self + * + * @throws SchemaException + */ + protected function _addIndex(Index $indexCandidate) + { + $indexName = $indexCandidate->getName(); + $indexName = $this->normalizeIdentifier($indexName); + $replacedImplicitIndexes = []; + + foreach ($this->implicitIndexes as $name => $implicitIndex) { + if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) { + continue; + } + + $replacedImplicitIndexes[] = $name; + } + + if ( + (isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) || + ($this->_primaryKeyName !== null && $indexCandidate->isPrimary()) + ) { + throw SchemaException::indexAlreadyExists($indexName, $this->_name); + } + + foreach ($replacedImplicitIndexes as $name) { + unset($this->_indexes[$name], $this->implicitIndexes[$name]); + } + + if ($indexCandidate->isPrimary()) { + $this->_primaryKeyName = $indexName; + } + + $this->_indexes[$indexName] = $indexCandidate; + + return $this; + } + + /** + * @return self + */ + protected function _addUniqueConstraint(UniqueConstraint $constraint): Table + { + $mergedNames = array_merge([$this->getName()], $constraint->getColumns()); + $name = strlen($constraint->getName()) > 0 + ? $constraint->getName() + : $this->_generateIdentifierName($mergedNames, 'fk', $this->_getMaxIdentifierLength()); + + $name = $this->normalizeIdentifier($name); + + $this->uniqueConstraints[$name] = $constraint; + + // If there is already an index that fulfills this requirements drop the request. In the case of __construct + // calling this method during hydration from schema-details all the explicitly added indexes lead to duplicates. + // This creates computation overhead in this case, however no duplicate indexes are ever added (column based). + $indexName = $this->_generateIdentifierName($mergedNames, 'idx', $this->_getMaxIdentifierLength()); + + $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, true, false); + + foreach ($this->_indexes as $existingIndex) { + if ($indexCandidate->isFullfilledBy($existingIndex)) { + return $this; + } + } + + $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; + + return $this; + } + + /** + * @return self + */ + protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) + { + $constraint->setLocalTable($this); + + if (strlen($constraint->getName()) > 0) { + $name = $constraint->getName(); + } else { + $name = $this->_generateIdentifierName( + array_merge([$this->getName()], $constraint->getLocalColumns()), + 'fk', + $this->_getMaxIdentifierLength() + ); + } + + $name = $this->normalizeIdentifier($name); + + $this->_fkConstraints[$name] = $constraint; + + /* Add an implicit index (defined by the DBAL) on the foreign key + columns. If there is already a user-defined index that fulfills these + requirements drop the request. In the case of __construct() calling + this method during hydration from schema-details, all the explicitly + added indexes lead to duplicates. This creates computation overhead in + this case, however no duplicate indexes are ever added (based on + columns). */ + $indexName = $this->_generateIdentifierName( + array_merge([$this->getName()], $constraint->getColumns()), + 'idx', + $this->_getMaxIdentifierLength() + ); + + $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false); + + foreach ($this->_indexes as $existingIndex) { + if ($indexCandidate->isFullfilledBy($existingIndex)) { + return $this; + } + } + + $this->_addIndex($indexCandidate); + $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; + + return $this; + } + + /** + * Returns whether this table has a foreign key constraint with the given name. + * + * @param string $name + * + * @return bool + */ + public function hasForeignKey($name) + { + $name = $this->normalizeIdentifier($name); + + return isset($this->_fkConstraints[$name]); + } + + /** + * Returns the foreign key constraint with the given name. + * + * @param string $name The constraint name. + * + * @return ForeignKeyConstraint + * + * @throws SchemaException If the foreign key does not exist. + */ + public function getForeignKey($name) + { + $name = $this->normalizeIdentifier($name); + + if (! $this->hasForeignKey($name)) { + throw SchemaException::foreignKeyDoesNotExist($name, $this->_name); + } + + return $this->_fkConstraints[$name]; + } + + /** + * Removes the foreign key constraint with the given name. + * + * @param string $name The constraint name. + * + * @return void + * + * @throws SchemaException + */ + public function removeForeignKey($name) + { + $name = $this->normalizeIdentifier($name); + + if (! $this->hasForeignKey($name)) { + throw SchemaException::foreignKeyDoesNotExist($name, $this->_name); + } + + unset($this->_fkConstraints[$name]); + } + + /** + * Returns whether this table has a unique constraint with the given name. + */ + public function hasUniqueConstraint(string $name): bool + { + $name = $this->normalizeIdentifier($name); + + return isset($this->uniqueConstraints[$name]); + } + + /** + * Returns the unique constraint with the given name. + * + * @throws SchemaException If the unique constraint does not exist. + */ + public function getUniqueConstraint(string $name): UniqueConstraint + { + $name = $this->normalizeIdentifier($name); + + if (! $this->hasUniqueConstraint($name)) { + throw SchemaException::uniqueConstraintDoesNotExist($name, $this->_name); + } + + return $this->uniqueConstraints[$name]; + } + + /** + * Removes the unique constraint with the given name. + * + * @throws SchemaException If the unique constraint does not exist. + */ + public function removeUniqueConstraint(string $name): void + { + $name = $this->normalizeIdentifier($name); + + if (! $this->hasUniqueConstraint($name)) { + throw SchemaException::uniqueConstraintDoesNotExist($name, $this->_name); + } + + unset($this->uniqueConstraints[$name]); + } + + /** + * Returns ordered list of columns (primary keys are first, then foreign keys, then the rest) + * + * @return Column[] + */ + public function getColumns() + { + $primaryKeyColumns = $this->hasPrimaryKey() ? $this->getPrimaryKeyColumns() : []; + $foreignKeyColumns = $this->getForeignKeyColumns(); + $remainderColumns = $this->filterColumns( + array_merge(array_keys($primaryKeyColumns), array_keys($foreignKeyColumns)), + true + ); + + return array_merge($primaryKeyColumns, $foreignKeyColumns, $remainderColumns); + } + + /** + * Returns the foreign key columns + * + * @return Column[] + */ + public function getForeignKeyColumns() + { + $foreignKeyColumns = []; + + foreach ($this->getForeignKeys() as $foreignKey) { + $foreignKeyColumns = array_merge($foreignKeyColumns, $foreignKey->getLocalColumns()); + } + + return $this->filterColumns($foreignKeyColumns); + } + + /** + * Returns only columns that have specified names + * + * @param string[] $columnNames + * + * @return Column[] + */ + private function filterColumns(array $columnNames, bool $reverse = false): array + { + return array_filter($this->_columns, static function (string $columnName) use ($columnNames, $reverse): bool { + return in_array($columnName, $columnNames, true) !== $reverse; + }, ARRAY_FILTER_USE_KEY); + } + + /** + * Returns whether this table has a Column with the given name. + * + * @param string $name The column name. + * + * @return bool + */ + public function hasColumn($name) + { + $name = $this->normalizeIdentifier($name); + + return isset($this->_columns[$name]); + } + + /** + * Returns the Column with the given name. + * + * @param string $name The column name. + * + * @return Column + * + * @throws SchemaException If the column does not exist. + */ + public function getColumn($name) + { + $name = $this->normalizeIdentifier($name); + + if (! $this->hasColumn($name)) { + throw SchemaException::columnDoesNotExist($name, $this->_name); + } + + return $this->_columns[$name]; + } + + /** + * Returns the primary key. + * + * @return Index|null The primary key, or null if this Table has no primary key. + */ + public function getPrimaryKey() + { + if ($this->_primaryKeyName !== null) { + return $this->getIndex($this->_primaryKeyName); + } + + return null; + } + + /** + * Returns the primary key columns. + * + * @return Column[] + * + * @throws Exception + */ + public function getPrimaryKeyColumns() + { + $primaryKey = $this->getPrimaryKey(); + + if ($primaryKey === null) { + throw new Exception('Table ' . $this->getName() . ' has no primary key.'); + } + + return $this->filterColumns($primaryKey->getColumns()); + } + + /** + * Returns whether this table has a primary key. + * + * @return bool + */ + public function hasPrimaryKey() + { + return $this->_primaryKeyName !== null && $this->hasIndex($this->_primaryKeyName); + } + + /** + * Returns whether this table has an Index with the given name. + * + * @param string $name The index name. + * + * @return bool + */ + public function hasIndex($name) + { + $name = $this->normalizeIdentifier($name); + + return isset($this->_indexes[$name]); + } + + /** + * Returns the Index with the given name. + * + * @param string $name The index name. + * + * @return Index + * + * @throws SchemaException If the index does not exist. + */ + public function getIndex($name) + { + $name = $this->normalizeIdentifier($name); + if (! $this->hasIndex($name)) { + throw SchemaException::indexDoesNotExist($name, $this->_name); + } + + return $this->_indexes[$name]; + } + + /** + * @return Index[] + */ + public function getIndexes() + { + return $this->_indexes; + } + + /** + * Returns the unique constraints. + * + * @return UniqueConstraint[] + */ + public function getUniqueConstraints(): array + { + return $this->uniqueConstraints; + } + + /** + * Returns the foreign key constraints. + * + * @return ForeignKeyConstraint[] + */ + public function getForeignKeys() + { + return $this->_fkConstraints; + } + + /** + * @param string $name + * + * @return bool + */ + public function hasOption($name) + { + return isset($this->_options[$name]); + } + + /** + * @param string $name + * + * @return mixed + */ + public function getOption($name) + { + return $this->_options[$name]; + } + + /** + * @return mixed[] + */ + public function getOptions() + { + return $this->_options; + } + + /** + * @return void + * + * @throws SchemaException + */ + public function visit(Visitor $visitor) + { + $visitor->acceptTable($this); + + foreach ($this->getColumns() as $column) { + $visitor->acceptColumn($this, $column); + } + + foreach ($this->getIndexes() as $index) { + $visitor->acceptIndex($this, $index); + } + + foreach ($this->getForeignKeys() as $constraint) { + $visitor->acceptForeignKey($this, $constraint); + } + } + + /** + * Clone of a Table triggers a deep clone of all affected assets. + * + * @return void + */ + public function __clone() + { + foreach ($this->_columns as $k => $column) { + $this->_columns[$k] = clone $column; + } + + foreach ($this->_indexes as $k => $index) { + $this->_indexes[$k] = clone $index; + } + + foreach ($this->_fkConstraints as $k => $fk) { + $this->_fkConstraints[$k] = clone $fk; + $this->_fkConstraints[$k]->setLocalTable($this); + } + } + + /** + * @param string[] $columnNames + * @param string[] $flags + * @param mixed[] $options + * + * @throws SchemaException + */ + private function _createUniqueConstraint( + array $columnNames, + string $indexName, + array $flags = [], + array $options = [] + ): UniqueConstraint { + if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName)) === 1) { + throw SchemaException::indexNameInvalid($indexName); + } + + foreach ($columnNames as $columnName) { + if (! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + + return new UniqueConstraint($indexName, $columnNames, $flags, $options); + } + + /** + * Normalizes a given identifier. + * + * Trims quotes and lowercases the given identifier. + * + * @return string The normalized identifier. + */ + private function normalizeIdentifier(?string $identifier): string + { + if ($identifier === null) { + return ''; + } + + return $this->trimQuotes(strtolower($identifier)); + } + + public function setComment(?string $comment): self + { + // For keeping backward compatibility with MySQL in previous releases, table comments are stored as options. + $this->addOption('comment', $comment); + + return $this; + } + + public function getComment(): ?string + { + return $this->_options['comment'] ?? null; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php b/app/vendor/doctrine/dbal/src/Schema/TableDiff.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php rename to app/vendor/doctrine/dbal/src/Schema/TableDiff.php diff --git a/app/vendor/doctrine/dbal/src/Schema/UniqueConstraint.php b/app/vendor/doctrine/dbal/src/Schema/UniqueConstraint.php new file mode 100644 index 000000000..cb91becb2 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Schema/UniqueConstraint.php @@ -0,0 +1,160 @@ + Identifier) + * + * @var Identifier[] + */ + protected $columns = []; + + /** + * Platform specific flags. + * array($flagName => true) + * + * @var true[] + */ + protected $flags = []; + + /** + * Platform specific options. + * + * @var mixed[] + */ + private $options; + + /** + * @param string[] $columns + * @param string[] $flags + * @param mixed[] $options + */ + public function __construct(string $name, array $columns, array $flags = [], array $options = []) + { + $this->_setName($name); + + $this->options = $options; + + foreach ($columns as $column) { + $this->addColumn($column); + } + + foreach ($flags as $flag) { + $this->addFlag($flag); + } + } + + /** + * {@inheritdoc} + */ + public function getColumns() + { + return array_keys($this->columns); + } + + /** + * {@inheritdoc} + */ + public function getQuotedColumns(AbstractPlatform $platform) + { + $columns = []; + + foreach ($this->columns as $column) { + $columns[] = $column->getQuotedName($platform); + } + + return $columns; + } + + /** + * @return string[] + */ + public function getUnquotedColumns(): array + { + return array_map([$this, 'trimQuotes'], $this->getColumns()); + } + + /** + * Returns platform specific flags for unique constraint. + * + * @return string[] + */ + public function getFlags(): array + { + return array_keys($this->flags); + } + + /** + * Adds flag for a unique constraint that translates to platform specific handling. + * + * @return $this + * + * @example $uniqueConstraint->addFlag('CLUSTERED') + */ + public function addFlag(string $flag): UniqueConstraint + { + $this->flags[strtolower($flag)] = true; + + return $this; + } + + /** + * Does this unique constraint have a specific flag? + */ + public function hasFlag(string $flag): bool + { + return isset($this->flags[strtolower($flag)]); + } + + /** + * Removes a flag. + */ + public function removeFlag(string $flag): void + { + unset($this->flags[strtolower($flag)]); + } + + /** + * Does this unique constraint have a specific option? + */ + public function hasOption(string $name): bool + { + return isset($this->options[strtolower($name)]); + } + + /** + * @return mixed + */ + public function getOption(string $name) + { + return $this->options[strtolower($name)]; + } + + /** + * @return mixed[] + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * Adds a new column to the unique constraint. + */ + protected function addColumn(string $column): void + { + $this->columns[$column] = new Identifier($column); + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/View.php b/app/vendor/doctrine/dbal/src/Schema/View.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/View.php rename to app/vendor/doctrine/dbal/src/Schema/View.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php b/app/vendor/doctrine/dbal/src/Schema/Visitor/AbstractVisitor.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php rename to app/vendor/doctrine/dbal/src/Schema/Visitor/AbstractVisitor.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php b/app/vendor/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php rename to app/vendor/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php b/app/vendor/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php rename to app/vendor/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php b/app/vendor/doctrine/dbal/src/Schema/Visitor/Graphviz.php similarity index 96% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php rename to app/vendor/doctrine/dbal/src/Schema/Visitor/Graphviz.php index 841b7cbce..299ff0e7f 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php +++ b/app/vendor/doctrine/dbal/src/Schema/Visitor/Graphviz.php @@ -83,13 +83,15 @@ private function createTableLabel(Table $table) . '' . $columnLabel . '' . '' . '' - . '' . strtolower($column->getType()) . '' + . '' + . strtolower($column->getType()->getName()) + . '' . '' . ''; $primaryKey = $table->getPrimaryKey(); - if ($primaryKey !== null && in_array($column->getName(), $primaryKey->getColumns())) { + if ($primaryKey !== null && in_array($column->getName(), $primaryKey->getColumns(), true)) { $label .= "\xe2\x9c\xb7"; } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/NamespaceVisitor.php b/app/vendor/doctrine/dbal/src/Schema/Visitor/NamespaceVisitor.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/NamespaceVisitor.php rename to app/vendor/doctrine/dbal/src/Schema/Visitor/NamespaceVisitor.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php b/app/vendor/doctrine/dbal/src/Schema/Visitor/RemoveNamespacedAssets.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php rename to app/vendor/doctrine/dbal/src/Schema/Visitor/RemoveNamespacedAssets.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php b/app/vendor/doctrine/dbal/src/Schema/Visitor/SchemaDiffVisitor.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php rename to app/vendor/doctrine/dbal/src/Schema/Visitor/SchemaDiffVisitor.php diff --git a/app/vendor/doctrine/dbal/src/Schema/Visitor/Visitor.php b/app/vendor/doctrine/dbal/src/Schema/Visitor/Visitor.php new file mode 100644 index 000000000..0b1c87d54 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Schema/Visitor/Visitor.php @@ -0,0 +1,51 @@ +Statement for the given SQL and Connection. + * + * @internal The statement can be only instantiated by {@link Connection}. + * + * @param string $sql The SQL of the statement. + * @param Connection $conn The connection on which the statement should be executed. + * + * @throws Exception + */ + public function __construct($sql, Connection $conn) + { + $driverConnection = $conn->getWrappedConnection(); + + try { + $stmt = $driverConnection->prepare($sql); + } catch (Exception $ex) { + throw $conn->convertExceptionDuringQuery($ex, $sql); + } + + $this->sql = $sql; + $this->stmt = $stmt; + $this->conn = $conn; + $this->platform = $conn->getDatabasePlatform(); + } + + /** + * Binds a parameter value to the statement. + * + * The value can optionally be bound with a DBAL mapping type. + * If bound with a DBAL mapping type, the binding type is derived from the mapping + * type and the value undergoes the conversion routines of the mapping type before + * being bound. + * + * @param string|int $param The name or position of the parameter. + * @param mixed $value The value of the parameter. + * @param mixed $type Either a PDO binding type or a DBAL mapping type name or instance. + * + * @return bool TRUE on success, FALSE on failure. + * + * @throws Exception + */ + public function bindValue($param, $value, $type = ParameterType::STRING) + { + $this->params[$param] = $value; + $this->types[$param] = $type; + + $bindingType = ParameterType::STRING; + + if ($type !== null) { + if (is_string($type)) { + $type = Type::getType($type); + } + + $bindingType = $type; + + if ($type instanceof Type) { + $value = $type->convertToDatabaseValue($value, $this->platform); + $bindingType = $type->getBindingType(); + } + } + + try { + return $this->stmt->bindValue($param, $value, $bindingType); + } catch (Exception $e) { + throw $this->conn->convertException($e); + } + } + + /** + * Binds a parameter to a value by reference. + * + * Binding a parameter by reference does not support DBAL mapping types. + * + * @param string|int $param The name or position of the parameter. + * @param mixed $variable The reference to the variable to bind. + * @param int $type The binding type. + * @param int|null $length Must be specified when using an OUT bind + * so that PHP allocates enough memory to hold the returned value. + * + * @return bool TRUE on success, FALSE on failure. + * + * @throws Exception + */ + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + { + $this->params[$param] = $variable; + $this->types[$param] = $type; + + try { + if (func_num_args() > 3) { + return $this->stmt->bindParam($param, $variable, $type, $length); + } + + return $this->stmt->bindParam($param, $variable, $type); + } catch (Exception $e) { + throw $this->conn->convertException($e); + } + } + + /** + * Executes the statement with the currently bound parameters. + * + * @deprecated Statement::execute() is deprecated, use Statement::executeQuery() or executeStatement() instead + * + * @param mixed[]|null $params + * + * @throws Exception + */ + public function execute($params = null): Result + { + Deprecation::triggerIfCalledFromOutside( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/4580', + 'Statement::execute() is deprecated, use Statement::executeQuery() or Statement::executeStatement() instead' + ); + + if ($params !== null) { + $this->params = $params; + } + + $logger = $this->conn->getConfiguration()->getSQLLogger(); + if ($logger !== null) { + $logger->startQuery($this->sql, $this->params, $this->types); + } + + try { + return new Result( + $this->stmt->execute($params), + $this->conn + ); + } catch (Exception $ex) { + throw $this->conn->convertExceptionDuringQuery($ex, $this->sql, $this->params, $this->types); + } finally { + if ($logger !== null) { + $logger->stopQuery(); + } + } + } + + /** + * Executes the statement with the currently bound parameters and return result. + * + * @param mixed[] $params + * + * @throws Exception + */ + public function executeQuery(array $params = []): Result + { + if ($params === []) { + $params = null; // Workaround as long execute() exists and used internally. + } + + return $this->execute($params); + } + + /** + * Executes the statement with the currently bound parameters and return affected rows. + * + * @param mixed[] $params + * + * @throws Exception + */ + public function executeStatement(array $params = []): int + { + if ($params === []) { + $params = null; // Workaround as long execute() exists and used internally. + } + + return $this->execute($params)->rowCount(); + } + + /** + * Gets the wrapped driver statement. + * + * @return DriverStatement + */ + public function getWrappedStatement() + { + return $this->stmt; + } +} diff --git a/app/vendor/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php b/app/vendor/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php new file mode 100644 index 000000000..8cff50f95 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php @@ -0,0 +1,206 @@ + */ + private $keywordLists; + + /** @var ConnectionProvider */ + private $connectionProvider; + + public function __construct(ConnectionProvider $connectionProvider) + { + parent::__construct(); + $this->connectionProvider = $connectionProvider; + + $this->keywordLists = [ + 'db2' => new DB2Keywords(), + 'mariadb102' => new MariaDb102Keywords(), + 'mysql' => new MySQLKeywords(), + 'mysql57' => new MySQL57Keywords(), + 'mysql80' => new MySQL80Keywords(), + 'oracle' => new OracleKeywords(), + 'pgsql' => new PostgreSQL94Keywords(), + 'pgsql100' => new PostgreSQL100Keywords(), + 'sqlite' => new SQLiteKeywords(), + 'sqlserver' => new SQLServer2012Keywords(), + ]; + } + + /** + * Add or replace a keyword list. + */ + public function setKeywordList(string $name, KeywordList $keywordList): void + { + $this->keywordLists[$name] = $keywordList; + } + + /** + * If you want to add or replace a keywords list use this command. + * + * @param string $name + * @param class-string $class + * + * @return void + */ + public function setKeywordListClass($name, $class) + { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/issues/4510', + 'ReservedWordsCommand::setKeywordListClass() is deprecated,' + . ' use ReservedWordsCommand::setKeywordList() instead.' + ); + + $this->keywordLists[$name] = new $class(); + } + + /** @return void */ + protected function configure() + { + $this + ->setName('dbal:reserved-words') + ->setDescription('Checks if the current database contains identifiers that are reserved.') + ->setDefinition([ + new InputOption('connection', null, InputOption::VALUE_REQUIRED, 'The named database connection'), + new InputOption( + 'list', + 'l', + InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, + 'Keyword-List name.' + ), + ]) + ->setHelp(<<%command.full_name% + +If you want to check against specific dialects you can +pass them to the command: + + %command.full_name% -l mysql -l pgsql + +The following keyword lists are currently shipped with Doctrine: + + * db2 + * mariadb102 + * mysql + * mysql57 + * mysql80 + * oracle + * pgsql + * pgsql100 + * sqlite + * sqlserver +EOT + ); + } + + /** + * {@inheritdoc} + * + * @throws Exception + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $conn = $this->getConnection($input); + + $keywordLists = $input->getOption('list'); + + if (is_string($keywordLists)) { + $keywordLists = [$keywordLists]; + } elseif (! is_array($keywordLists)) { + $keywordLists = []; + } + + if (count($keywordLists) === 0) { + $keywordLists = array_keys($this->keywordLists); + } + + $keywords = []; + foreach ($keywordLists as $keywordList) { + if (! isset($this->keywordLists[$keywordList])) { + throw new InvalidArgumentException( + "There exists no keyword list with name '" . $keywordList . "'. " . + 'Known lists: ' . implode(', ', array_keys($this->keywordLists)) + ); + } + + $keywords[] = $this->keywordLists[$keywordList]; + } + + $output->write( + 'Checking keyword violations for ' . implode(', ', $keywordLists) . '...', + true + ); + + $schema = $conn->getSchemaManager()->createSchema(); + $visitor = new ReservedKeywordsValidator($keywords); + $schema->visit($visitor); + + $violations = $visitor->getViolations(); + if (count($violations) !== 0) { + $output->write( + 'There are ' . count($violations) . ' reserved keyword violations' + . ' in your database schema:', + true + ); + + foreach ($violations as $violation) { + $output->write(' - ' . $violation, true); + } + + return 1; + } + + $output->write('No reserved keywords violations have been found!', true); + + return 0; + } + + private function getConnection(InputInterface $input): Connection + { + $connectionName = $input->getOption('connection'); + assert(is_string($connectionName) || $connectionName === null); + + if ($connectionName !== null) { + return $this->connectionProvider->getConnection($connectionName); + } + + return $this->connectionProvider->getDefaultConnection(); + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php b/app/vendor/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php similarity index 77% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php rename to app/vendor/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php index 8cc24f300..4ccb32605 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php +++ b/app/vendor/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php @@ -3,10 +3,9 @@ namespace Doctrine\DBAL\Tools\Console\Command; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Tools\Console\ConnectionProvider; use Doctrine\DBAL\Tools\Dumper; -use Doctrine\Deprecations\Deprecation; -use Exception; use LogicException; use RuntimeException; use Symfony\Component\Console\Command\Command; @@ -16,6 +15,7 @@ use Symfony\Component\Console\Output\OutputInterface; use function assert; +use function is_bool; use function is_numeric; use function is_string; use function stripos; @@ -26,22 +26,13 @@ */ class RunSqlCommand extends Command { - /** @var ConnectionProvider|null */ + /** @var ConnectionProvider */ private $connectionProvider; - public function __construct(?ConnectionProvider $connectionProvider = null) + public function __construct(ConnectionProvider $connectionProvider) { parent::__construct(); $this->connectionProvider = $connectionProvider; - if ($connectionProvider !== null) { - return; - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3956', - 'Not passing a connection provider as the first constructor argument is deprecated' - ); } /** @return void */ @@ -67,6 +58,8 @@ protected function configure() /** * {@inheritdoc} + * + * @throws Exception */ protected function execute(InputInterface $input, OutputInterface $output) { @@ -86,7 +79,10 @@ protected function execute(InputInterface $input, OutputInterface $output) throw new LogicException("Option 'depth' must contains an integer value"); } - if (stripos($sql, 'select') === 0 || $input->getOption('force-fetch')) { + $forceFetch = $input->getOption('force-fetch'); + assert(is_bool($forceFetch)); + + if (stripos($sql, 'select') === 0 || $forceFetch) { $resultSet = $conn->fetchAllAssociative($sql); } else { $resultSet = $conn->executeStatement($sql); @@ -102,14 +98,6 @@ private function getConnection(InputInterface $input): Connection $connectionName = $input->getOption('connection'); assert(is_string($connectionName) || $connectionName === null); - if ($this->connectionProvider === null) { - if ($connectionName !== null) { - throw new Exception('Specifying a connection is only supported when a ConnectionProvider is used.'); - } - - return $this->getHelper('db')->getConnection(); - } - if ($connectionName !== null) { return $this->connectionProvider->getConnection($connectionName); } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/ConnectionNotFound.php b/app/vendor/doctrine/dbal/src/Tools/Console/ConnectionNotFound.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/ConnectionNotFound.php rename to app/vendor/doctrine/dbal/src/Tools/Console/ConnectionNotFound.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/ConnectionProvider.php b/app/vendor/doctrine/dbal/src/Tools/Console/ConnectionProvider.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/ConnectionProvider.php rename to app/vendor/doctrine/dbal/src/Tools/Console/ConnectionProvider.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/ConnectionProvider/SingleConnectionProvider.php b/app/vendor/doctrine/dbal/src/Tools/Console/ConnectionProvider/SingleConnectionProvider.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/ConnectionProvider/SingleConnectionProvider.php rename to app/vendor/doctrine/dbal/src/Tools/Console/ConnectionProvider/SingleConnectionProvider.php diff --git a/app/vendor/doctrine/dbal/src/Tools/Console/ConsoleRunner.php b/app/vendor/doctrine/dbal/src/Tools/Console/ConsoleRunner.php new file mode 100644 index 000000000..1cc315dee --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Tools/Console/ConsoleRunner.php @@ -0,0 +1,74 @@ +setCatchExceptions(true); + self::addCommands($cli, $connectionProvider); + $cli->addCommands($commands); + $cli->run(); + } + + /** + * @return void + */ + public static function addCommands(Application $cli, ConnectionProvider $connectionProvider) + { + $cli->addCommands([ + new RunSqlCommand($connectionProvider), + new ReservedWordsCommand($connectionProvider), + ]); + } + + /** + * Prints the instructions to create a configuration file + * + * @return void + */ + public static function printCliConfigTemplate() + { + echo <<<'HELP' +You are missing a "cli-config.php" or "config/cli-config.php" file in your +project, which is required to get the Doctrine-DBAL Console working. You can use the +following sample as a template: + +toArray(); diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/TransactionIsolationLevel.php b/app/vendor/doctrine/dbal/src/TransactionIsolationLevel.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/TransactionIsolationLevel.php rename to app/vendor/doctrine/dbal/src/TransactionIsolationLevel.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php b/app/vendor/doctrine/dbal/src/Types/ArrayType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php rename to app/vendor/doctrine/dbal/src/Types/ArrayType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/AsciiStringType.php b/app/vendor/doctrine/dbal/src/Types/AsciiStringType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/AsciiStringType.php rename to app/vendor/doctrine/dbal/src/Types/AsciiStringType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php b/app/vendor/doctrine/dbal/src/Types/BigIntType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php rename to app/vendor/doctrine/dbal/src/Types/BigIntType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BinaryType.php b/app/vendor/doctrine/dbal/src/Types/BinaryType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BinaryType.php rename to app/vendor/doctrine/dbal/src/Types/BinaryType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php b/app/vendor/doctrine/dbal/src/Types/BlobType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php rename to app/vendor/doctrine/dbal/src/Types/BlobType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php b/app/vendor/doctrine/dbal/src/Types/BooleanType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php rename to app/vendor/doctrine/dbal/src/Types/BooleanType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php b/app/vendor/doctrine/dbal/src/Types/ConversionException.php similarity index 88% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php rename to app/vendor/doctrine/dbal/src/Types/ConversionException.php index 5be4743a5..f1a18742e 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php +++ b/app/vendor/doctrine/dbal/src/Types/ConversionException.php @@ -13,6 +13,7 @@ use function sprintf; use function strlen; use function substr; +use function var_export; /** * Conversion Exception is thrown when the database to PHP conversion fails. @@ -73,21 +74,18 @@ public static function conversionFailedInvalidType( array $possibleTypes, ?Throwable $previous = null ) { - $actualType = is_object($value) ? get_class($value) : gettype($value); - - if (is_scalar($value)) { + if (is_scalar($value) || $value === null) { return new self(sprintf( - "Could not convert PHP value '%s' of type '%s' to type '%s'. Expected one of the following types: %s", - $value, - $actualType, + 'Could not convert PHP value %s to type %s. Expected one of the following types: %s', + var_export($value, true), $toType, implode(', ', $possibleTypes) ), 0, $previous); } return new self(sprintf( - "Could not convert PHP value of type '%s' to type '%s'. Expected one of the following types: %s", - $actualType, + 'Could not convert PHP value of type %s to type %s. Expected one of the following types: %s', + is_object($value) ? get_class($value) : gettype($value), $toType, implode(', ', $possibleTypes) ), 0, $previous); diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateImmutableType.php b/app/vendor/doctrine/dbal/src/Types/DateImmutableType.php similarity index 97% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateImmutableType.php rename to app/vendor/doctrine/dbal/src/Types/DateImmutableType.php index a4c5d266d..4fbe6a472 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateImmutableType.php +++ b/app/vendor/doctrine/dbal/src/Types/DateImmutableType.php @@ -49,7 +49,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) $dateTime = DateTimeImmutable::createFromFormat('!' . $platform->getDateFormatString(), $value); - if (! $dateTime) { + if ($dateTime === false) { throw ConversionException::conversionFailedFormat( $value, $this->getName(), diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateIntervalType.php b/app/vendor/doctrine/dbal/src/Types/DateIntervalType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateIntervalType.php rename to app/vendor/doctrine/dbal/src/Types/DateIntervalType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeImmutableType.php b/app/vendor/doctrine/dbal/src/Types/DateTimeImmutableType.php similarity index 95% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeImmutableType.php rename to app/vendor/doctrine/dbal/src/Types/DateTimeImmutableType.php index 9af4fc349..fd7751977 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeImmutableType.php +++ b/app/vendor/doctrine/dbal/src/Types/DateTimeImmutableType.php @@ -51,11 +51,11 @@ public function convertToPHPValue($value, AbstractPlatform $platform) $dateTime = DateTimeImmutable::createFromFormat($platform->getDateTimeFormatString(), $value); - if (! $dateTime) { + if ($dateTime === false) { $dateTime = date_create_immutable($value); } - if (! $dateTime) { + if ($dateTime === false) { throw ConversionException::conversionFailedFormat( $value, $this->getName(), diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php b/app/vendor/doctrine/dbal/src/Types/DateTimeType.php similarity index 96% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php rename to app/vendor/doctrine/dbal/src/Types/DateTimeType.php index f6f6da0ce..454295d71 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php +++ b/app/vendor/doctrine/dbal/src/Types/DateTimeType.php @@ -56,11 +56,11 @@ public function convertToPHPValue($value, AbstractPlatform $platform) $val = DateTime::createFromFormat($platform->getDateTimeFormatString(), $value); - if (! $val) { + if ($val === false) { $val = date_create($value); } - if (! $val) { + if ($val === false) { throw ConversionException::conversionFailedFormat( $value, $this->getName(), diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzImmutableType.php b/app/vendor/doctrine/dbal/src/Types/DateTimeTzImmutableType.php similarity index 97% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzImmutableType.php rename to app/vendor/doctrine/dbal/src/Types/DateTimeTzImmutableType.php index b88862479..6e707e065 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzImmutableType.php +++ b/app/vendor/doctrine/dbal/src/Types/DateTimeTzImmutableType.php @@ -49,7 +49,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) $dateTime = DateTimeImmutable::createFromFormat($platform->getDateTimeTzFormatString(), $value); - if (! $dateTime) { + if ($dateTime === false) { throw ConversionException::conversionFailedFormat( $value, $this->getName(), diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php b/app/vendor/doctrine/dbal/src/Types/DateTimeTzType.php similarity index 92% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php rename to app/vendor/doctrine/dbal/src/Types/DateTimeTzType.php index bc25890b7..29672397f 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php +++ b/app/vendor/doctrine/dbal/src/Types/DateTimeTzType.php @@ -53,7 +53,11 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) return $value->format($platform->getDateTimeTzFormatString()); } - throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'DateTime']); + throw ConversionException::conversionFailedInvalidType( + $value, + $this->getName(), + ['null', 'DateTime'] + ); } /** @@ -66,7 +70,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } $val = DateTime::createFromFormat($platform->getDateTimeTzFormatString(), $value); - if (! $val) { + if ($val === false) { throw ConversionException::conversionFailedFormat( $value, $this->getName(), diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php b/app/vendor/doctrine/dbal/src/Types/DateType.php similarity index 98% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php rename to app/vendor/doctrine/dbal/src/Types/DateType.php index 81cc86c56..6f86f5436 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php +++ b/app/vendor/doctrine/dbal/src/Types/DateType.php @@ -53,7 +53,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } $val = DateTime::createFromFormat('!' . $platform->getDateFormatString(), $value); - if (! $val) { + if ($val === false) { throw ConversionException::conversionFailedFormat( $value, $this->getName(), diff --git a/app/vendor/doctrine/dbal/src/Types/DecimalType.php b/app/vendor/doctrine/dbal/src/Types/DecimalType.php new file mode 100644 index 000000000..f75d3db2c --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Types/DecimalType.php @@ -0,0 +1,45 @@ +getDecimalTypeDeclarationSQL($column); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + // Some drivers starting from PHP 8.1 can represent decimals as float + // See also: https://github.com/doctrine/dbal/pull/4818 + if (PHP_VERSION_ID >= 80100 && is_float($value)) { + return (string) $value; + } + + return $value; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php b/app/vendor/doctrine/dbal/src/Types/FloatType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php rename to app/vendor/doctrine/dbal/src/Types/FloatType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/GuidType.php b/app/vendor/doctrine/dbal/src/Types/GuidType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/GuidType.php rename to app/vendor/doctrine/dbal/src/Types/GuidType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php b/app/vendor/doctrine/dbal/src/Types/IntegerType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php rename to app/vendor/doctrine/dbal/src/Types/IntegerType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonType.php b/app/vendor/doctrine/dbal/src/Types/JsonType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonType.php rename to app/vendor/doctrine/dbal/src/Types/JsonType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php b/app/vendor/doctrine/dbal/src/Types/ObjectType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php rename to app/vendor/doctrine/dbal/src/Types/ObjectType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/PhpDateTimeMappingType.php b/app/vendor/doctrine/dbal/src/Types/PhpDateTimeMappingType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/PhpDateTimeMappingType.php rename to app/vendor/doctrine/dbal/src/Types/PhpDateTimeMappingType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/PhpIntegerMappingType.php b/app/vendor/doctrine/dbal/src/Types/PhpIntegerMappingType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/PhpIntegerMappingType.php rename to app/vendor/doctrine/dbal/src/Types/PhpIntegerMappingType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php b/app/vendor/doctrine/dbal/src/Types/SimpleArrayType.php similarity index 92% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php rename to app/vendor/doctrine/dbal/src/Types/SimpleArrayType.php index 5e3e73ce7..ee9c7f2df 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php +++ b/app/vendor/doctrine/dbal/src/Types/SimpleArrayType.php @@ -4,8 +4,10 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; +use function count; use function explode; use function implode; +use function is_array; use function is_resource; use function stream_get_contents; @@ -29,7 +31,7 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { - if (! $value) { + if (! is_array($value) || count($value) === 0) { return null; } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php b/app/vendor/doctrine/dbal/src/Types/SmallIntType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php rename to app/vendor/doctrine/dbal/src/Types/SmallIntType.php diff --git a/app/vendor/doctrine/dbal/src/Types/StringType.php b/app/vendor/doctrine/dbal/src/Types/StringType.php new file mode 100644 index 000000000..4e7bd55d0 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Types/StringType.php @@ -0,0 +1,27 @@ +getVarcharTypeDeclarationSQL($column); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Types::STRING; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php b/app/vendor/doctrine/dbal/src/Types/TextType.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php rename to app/vendor/doctrine/dbal/src/Types/TextType.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeImmutableType.php b/app/vendor/doctrine/dbal/src/Types/TimeImmutableType.php similarity index 97% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeImmutableType.php rename to app/vendor/doctrine/dbal/src/Types/TimeImmutableType.php index cc4376955..8d2c1517c 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeImmutableType.php +++ b/app/vendor/doctrine/dbal/src/Types/TimeImmutableType.php @@ -49,7 +49,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) $dateTime = DateTimeImmutable::createFromFormat('!' . $platform->getTimeFormatString(), $value); - if (! $dateTime) { + if ($dateTime === false) { throw ConversionException::conversionFailedFormat( $value, $this->getName(), diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php b/app/vendor/doctrine/dbal/src/Types/TimeType.php similarity index 98% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php rename to app/vendor/doctrine/dbal/src/Types/TimeType.php index 59bd7f75e..4f2c8c631 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php +++ b/app/vendor/doctrine/dbal/src/Types/TimeType.php @@ -53,7 +53,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } $val = DateTime::createFromFormat('!' . $platform->getTimeFormatString(), $value); - if (! $val) { + if ($val === false) { throw ConversionException::conversionFailedFormat( $value, $this->getName(), diff --git a/app/vendor/doctrine/dbal/src/Types/Type.php b/app/vendor/doctrine/dbal/src/Types/Type.php new file mode 100644 index 000000000..03e5b8248 --- /dev/null +++ b/app/vendor/doctrine/dbal/src/Types/Type.php @@ -0,0 +1,279 @@ + ArrayType::class, + Types::ASCII_STRING => AsciiStringType::class, + Types::BIGINT => BigIntType::class, + Types::BINARY => BinaryType::class, + Types::BLOB => BlobType::class, + Types::BOOLEAN => BooleanType::class, + Types::DATE_MUTABLE => DateType::class, + Types::DATE_IMMUTABLE => DateImmutableType::class, + Types::DATEINTERVAL => DateIntervalType::class, + Types::DATETIME_MUTABLE => DateTimeType::class, + Types::DATETIME_IMMUTABLE => DateTimeImmutableType::class, + Types::DATETIMETZ_MUTABLE => DateTimeTzType::class, + Types::DATETIMETZ_IMMUTABLE => DateTimeTzImmutableType::class, + Types::DECIMAL => DecimalType::class, + Types::FLOAT => FloatType::class, + Types::GUID => GuidType::class, + Types::INTEGER => IntegerType::class, + Types::JSON => JsonType::class, + Types::OBJECT => ObjectType::class, + Types::SIMPLE_ARRAY => SimpleArrayType::class, + Types::SMALLINT => SmallIntType::class, + Types::STRING => StringType::class, + Types::TEXT => TextType::class, + Types::TIME_MUTABLE => TimeType::class, + Types::TIME_IMMUTABLE => TimeImmutableType::class, + ]; + + /** @var TypeRegistry|null */ + private static $typeRegistry; + + /** + * @internal Do not instantiate directly - use {@see Type::addType()} method instead. + */ + final public function __construct() + { + } + + /** + * Converts a value from its PHP representation to its database representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * + * @return mixed The database representation of the value. + * + * @throws ConversionException + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return $value; + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * + * @return mixed The PHP representation of the value. + * + * @throws ConversionException + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return $value; + } + + /** + * Gets the SQL declaration snippet for a column of this type. + * + * @param mixed[] $column The column definition + * @param AbstractPlatform $platform The currently used database platform. + * + * @return string + */ + abstract public function getSQLDeclaration(array $column, AbstractPlatform $platform); + + /** + * Gets the name of this type. + * + * @return string + * + * @todo Needed? + */ + abstract public function getName(); + + final public static function getTypeRegistry(): TypeRegistry + { + if (self::$typeRegistry === null) { + self::$typeRegistry = self::createTypeRegistry(); + } + + return self::$typeRegistry; + } + + private static function createTypeRegistry(): TypeRegistry + { + $instances = []; + + foreach (self::BUILTIN_TYPES_MAP as $name => $class) { + $instances[$name] = new $class(); + } + + return new TypeRegistry($instances); + } + + /** + * Factory method to create type instances. + * Type instances are implemented as flyweights. + * + * @param string $name The name of the type (as returned by getName()). + * + * @return Type + * + * @throws Exception + */ + public static function getType($name) + { + return self::getTypeRegistry()->get($name); + } + + /** + * Adds a custom type to the type map. + * + * @param string $name The name of the type. This should correspond to what getName() returns. + * @param class-string $className The class name of the custom type. + * + * @return void + * + * @throws Exception + */ + public static function addType($name, $className) + { + self::getTypeRegistry()->register($name, new $className()); + } + + /** + * Checks if exists support for a type. + * + * @param string $name The name of the type. + * + * @return bool TRUE if type is supported; FALSE otherwise. + */ + public static function hasType($name) + { + return self::getTypeRegistry()->has($name); + } + + /** + * Overrides an already defined type to use a different implementation. + * + * @param string $name + * @param class-string $className + * + * @return void + * + * @throws Exception + */ + public static function overrideType($name, $className) + { + self::getTypeRegistry()->override($name, new $className()); + } + + /** + * Gets the (preferred) binding type for values of this type that + * can be used when binding parameters to prepared statements. + * + * This method should return one of the {@link ParameterType} constants. + * + * @return int + */ + public function getBindingType() + { + return ParameterType::STRING; + } + + /** + * Gets the types array map which holds all registered types and the corresponding + * type class + * + * @return string[] + */ + public static function getTypesMap() + { + return array_map( + static function (Type $type): string { + return get_class($type); + }, + self::getTypeRegistry()->getMap() + ); + } + + /** + * Does working with this column require SQL conversion functions? + * + * This is a metadata function that is required for example in the ORM. + * Usage of {@link convertToDatabaseValueSQL} and + * {@link convertToPHPValueSQL} works for any type and mostly + * does nothing. This method can additionally be used for optimization purposes. + * + * @return bool + */ + public function canRequireSQLConversion() + { + return false; + } + + /** + * Modifies the SQL expression (identifier, parameter) to convert to a database value. + * + * @param string $sqlExpr + * + * @return string + */ + public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) + { + return $sqlExpr; + } + + /** + * Modifies the SQL expression (identifier, parameter) to convert to a PHP value. + * + * @param string $sqlExpr + * @param AbstractPlatform $platform + * + * @return string + */ + public function convertToPHPValueSQL($sqlExpr, $platform) + { + return $sqlExpr; + } + + /** + * Gets an array of database types that map to this Doctrine type. + * + * @return string[] + */ + public function getMappedDatabaseTypes(AbstractPlatform $platform) + { + return []; + } + + /** + * If this Doctrine Type maps to an already mapped database type, + * reverse schema engineering can't tell them apart. You need to mark + * one of those types as commented, which will have Doctrine use an SQL + * comment to typehint the actual Doctrine Type. + * + * @return bool + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return false; + } +} diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TypeRegistry.php b/app/vendor/doctrine/dbal/src/Types/TypeRegistry.php similarity index 100% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TypeRegistry.php rename to app/vendor/doctrine/dbal/src/Types/TypeRegistry.php diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php b/app/vendor/doctrine/dbal/src/Types/Types.php similarity index 92% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php rename to app/vendor/doctrine/dbal/src/Types/Types.php index 3ca677942..56bf3f51c 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php +++ b/app/vendor/doctrine/dbal/src/Types/Types.php @@ -35,9 +35,6 @@ final class Types public const TIME_MUTABLE = 'time'; public const TIME_IMMUTABLE = 'time_immutable'; - /** @deprecated json_array type is deprecated, use {@see self::JSON} instead. */ - public const JSON_ARRAY = 'json_array'; - /** * @codeCoverageIgnore */ diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeImmutableType.php b/app/vendor/doctrine/dbal/src/Types/VarDateTimeImmutableType.php similarity index 97% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeImmutableType.php rename to app/vendor/doctrine/dbal/src/Types/VarDateTimeImmutableType.php index 23f8b572d..f4f5e3ec3 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeImmutableType.php +++ b/app/vendor/doctrine/dbal/src/Types/VarDateTimeImmutableType.php @@ -51,7 +51,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) $dateTime = date_create_immutable($value); - if (! $dateTime) { + if ($dateTime === false) { throw ConversionException::conversionFailed($value, $this->getName()); } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php b/app/vendor/doctrine/dbal/src/Types/VarDateTimeType.php similarity index 96% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php rename to app/vendor/doctrine/dbal/src/Types/VarDateTimeType.php index 20960579a..b11ef2871 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php +++ b/app/vendor/doctrine/dbal/src/Types/VarDateTimeType.php @@ -26,7 +26,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } $val = date_create($value); - if (! $val) { + if ($val === false) { throw ConversionException::conversionFailed($value, $this->getName()); } diff --git a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/VersionAwarePlatformDriver.php b/app/vendor/doctrine/dbal/src/VersionAwarePlatformDriver.php similarity index 94% rename from app/vendor/doctrine/dbal/lib/Doctrine/DBAL/VersionAwarePlatformDriver.php rename to app/vendor/doctrine/dbal/src/VersionAwarePlatformDriver.php index 8bdae9264..b3ec8b843 100644 --- a/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/VersionAwarePlatformDriver.php +++ b/app/vendor/doctrine/dbal/src/VersionAwarePlatformDriver.php @@ -12,7 +12,7 @@ * This interface should be implemented by drivers that are capable to do this * distinction. */ -interface VersionAwarePlatformDriver +interface VersionAwarePlatformDriver extends Driver { /** * Factory method for creating the appropriate platform instance for the given version. diff --git a/app/vendor/doctrine/dbal/static-analysis/driver-manager-retrieves-correct-connection-type.php b/app/vendor/doctrine/dbal/static-analysis/driver-manager-retrieves-correct-connection-type.php new file mode 100644 index 000000000..566db2474 --- /dev/null +++ b/app/vendor/doctrine/dbal/static-analysis/driver-manager-retrieves-correct-connection-type.php @@ -0,0 +1,19 @@ + MyConnection::class, + ]); +} diff --git a/app/vendor/josegonzalez/dotenv/README.markdown b/app/vendor/josegonzalez/dotenv/README.markdown index 9ad5d2b52..53ed0f507 100644 --- a/app/vendor/josegonzalez/dotenv/README.markdown +++ b/app/vendor/josegonzalez/dotenv/README.markdown @@ -13,7 +13,7 @@ A `.env` file parsing and loading library for PHP. ## Requirements -* PHP 5.3+ +* PHP 5.5+ ## Installation @@ -145,6 +145,22 @@ $Loader = (new josegonzalez\Dotenv\Loader('path/to/.env')) Already defined `$_SERVER` entries will result in an immediate `LogicException`, unless `$overwriteSERVER` is set to `true` (default `false`). +### Making available to `apache_getenv()` + +> This should be preferred over `getenv` when using the Apache web server with `mod_php`. + +```php +parse() + ->apacheSetenv($overwriteAPACHE); // Throws LogicException if ->parse() is not called first + // May throw a PHP Error if either apache_setenv() or apache_putenv() are not available +?> +``` + +Already defined `apache_getenv()` entries will result in an immediate `LogicException`, unless `$overwriteAPACHE` is set to `true` (default `false`). + ### Making available to `getenv()` ```php @@ -340,7 +356,7 @@ $expect('FOO'); // Returns false if `env` is missing FOO ## What is it and why should I use it? -When developing and deploying your applications you are typically interacting with various environments - production and development for instance. These environments both execute your code, but will do so using different credentials. You may also wish to distribute your application to developers without accidentally giving them access to important external services. +When developing and deploying your applications you are typically interacting with various environments - production and development for instance. These environments both execute your code, but will do so using different credentials. You may also wish to distribute your application to developers without accidentally giving them access to important external services. Simple examples include authentication keys to your email provider or database connection credentials. You would never want to accidentally send testing emails to all your users, or run a `DROP TABLE` statement against production because you ran your unit tests. diff --git a/app/vendor/josegonzalez/dotenv/composer.json b/app/vendor/josegonzalez/dotenv/composer.json index 8d5b2018d..347e6d449 100644 --- a/app/vendor/josegonzalez/dotenv/composer.json +++ b/app/vendor/josegonzalez/dotenv/composer.json @@ -14,12 +14,13 @@ } ], "require": { - "php": ">=5.3.0", + "php": ">=5.5.0", "m1/env": "2.*" }, "require-dev": { "squizlabs/php_codesniffer": "2.*", - "satooshi/php-coveralls": "1.*" + "satooshi/php-coveralls": "1.*", + "php-mock/php-mock-phpunit": "^1.1" }, "autoload": { "psr-0": { diff --git a/app/vendor/josegonzalez/dotenv/src/josegonzalez/Dotenv/Loader.php b/app/vendor/josegonzalez/dotenv/src/josegonzalez/Dotenv/Loader.php index f91a208fb..a9519e387 100644 --- a/app/vendor/josegonzalez/dotenv/src/josegonzalez/Dotenv/Loader.php +++ b/app/vendor/josegonzalez/dotenv/src/josegonzalez/Dotenv/Loader.php @@ -22,6 +22,7 @@ class Loader protected $raise = true; protected $skip = array( + 'apacheSetenv' => false, 'define' => false, 'putenv' => false, 'toEnv' => false, @@ -65,6 +66,7 @@ public static function load($options = null) 'skipExisting', 'prefix', 'expect', + 'apacheSetenv', 'define', 'putenv', 'toEnv', @@ -230,6 +232,28 @@ public function expect() return $this; } + public function apacheSetenv($overwrite = false) + { + $this->requireParse('apache_setenv'); + foreach ($this->environment as $key => $value) { + $prefixedKey = $this->prefixed($key); + if (apache_getenv($prefixedKey) !== false && !$overwrite) { + if ($this->skip['apacheSetenv']) { + continue; + } + + return $this->raise( + 'LogicException', + sprintf('Key "%s" has already been defined in apache_getenv()', $prefixedKey) + ); + } + + apache_setenv($prefixedKey, $value); + } + + return $this; + } + public function define() { $this->requireParse('define'); @@ -257,7 +281,7 @@ public function putenv($overwrite = false) $this->requireParse('putenv'); foreach ($this->environment as $key => $value) { $prefixedKey = $this->prefixed($key); - if (getenv($prefixedKey) && !$overwrite) { + if (getenv($prefixedKey) !== false && !$overwrite) { if ($this->skip['putenv']) { continue; } @@ -279,7 +303,7 @@ public function toEnv($overwrite = false) $this->requireParse('toEnv'); foreach ($this->environment as $key => $value) { $prefixedKey = $this->prefixed($key); - if (isset($_ENV[$prefixedKey]) && !$overwrite) { + if (array_key_exists($prefixedKey, $_ENV) && !$overwrite) { if ($this->skip['toEnv']) { continue; } @@ -301,7 +325,7 @@ public function toServer($overwrite = false) $this->requireParse('toServer'); foreach ($this->environment as $key => $value) { $prefixedKey = $this->prefixed($key); - if (isset($_SERVER[$prefixedKey]) && !$overwrite) { + if (array_key_exists($prefixedKey, $_SERVER) && !$overwrite) { if ($this->skip['toServer']) { continue; } diff --git a/app/vendor/laminas/laminas-diactoros/README.md b/app/vendor/laminas/laminas-diactoros/README.md index 727873817..9d34c9deb 100644 --- a/app/vendor/laminas/laminas-diactoros/README.md +++ b/app/vendor/laminas/laminas-diactoros/README.md @@ -1,6 +1,6 @@ # laminas-diactoros -[![Build Status](https://github.com/laminas/laminas-diactoros/workflows/continuous-integration.yml/badge.svg)](https://github.com/laminas/laminas-diactoros/actions/workflows/continuous-integration.yml) +[![Build Status](https://github.com/laminas/laminas-diactoros/workflows/Continuous%20Integration/badge.svg)](https://github.com/laminas/laminas-diactoros/actions/workflows/continuous-integration.yml) > Diactoros (pronunciation: `/dɪʌktɒrɒs/`): an epithet for Hermes, meaning literally, "the messenger." @@ -10,8 +10,8 @@ This package supercedes and replaces [phly/http](https://github.com/phly/http). [PSR-7 HTTP message interfaces](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message.md) and [PSR-17 HTTP message factory interfaces](https://www.php-fig.org/psr/psr-17). -* File issues at https://github.com/laminas/laminas-diactoros/issues -* Issue patches to https://github.com/laminas/laminas-diactoros/pulls +- File issues at https://github.com/laminas/laminas-diactoros/issues +- Issue patches to https://github.com/laminas/laminas-diactoros/pulls ## Documentation @@ -20,3 +20,11 @@ Documentation is available at: - https://docs.laminas.dev/laminas-diactoros/ Source files for documentation are [in the docs/ tree](docs/). + +----- + +## Contributing and Support + +- If you need support with the project, read [the support documentation](https://github.com/laminas/.github/blob/main/SUPPORT.md). +- If you wish to contribute to the project, read the [contributing guidelines](https://github.com/laminas/.github/blob/main/CONTRIBUTING.md) as well as the [Code of Conduct](https://github.com/laminas/.github/blob/main/CODE_OF_CONDUCT.md). +- For reporting security issues, please review our [security policy](https://github.com/laminas/.github/blob/main/SECURITY.md). diff --git a/app/vendor/laminas/laminas-diactoros/psalm-baseline.xml b/app/vendor/laminas/laminas-diactoros/psalm-baseline.xml index cfa7df05b..1c9bade42 100644 --- a/app/vendor/laminas/laminas-diactoros/psalm-baseline.xml +++ b/app/vendor/laminas/laminas-diactoros/psalm-baseline.xml @@ -1,5 +1,5 @@ - + $contents @@ -515,60 +515,7 @@ - - $name - $name - $value - $value - $value - $value - $value - $value - $value - $value - $version - - - headersWithInjectionVectors - invalidArrayHeaderValues - invalidGeneralHeaderValues - invalidHeaderValueTypes - invalidHeaderValues - invalidProtocolVersionProvider - numericHeaderValuesProvider - testAddHeaderAppendsToExistingHeader - testBodyMutatorReturnsCloneWithChanges - testCanRemoveHeaders - testDoesNotAllowCRLFInjectionWhenCallingWithAddedHeader - testDoesNotAllowCRLFInjectionWhenCallingWithHeader - testGetHeaderLineReturnsEmptyStringWhenHeaderDoesNotExist - testGetHeaderLineReturnsHeaderValueAsCommaConcatenatedString - testGetHeaderReturnsAnEmptyArrayWhenHeaderDoesNotExist - testGetHeaderReturnsHeaderValueAsArray - testGetHeadersKeepsHeaderCaseSensitivity - testGetHeadersReturnsCaseWithWhichHeaderFirstRegistered - testHasHeaderReturnsFalseIfHeaderIsNotPresent - testHasHeaderReturnsTrueIfHeaderIsPresent - testHeaderRemovalIsCaseInsensitive - testHeadersInitialization - testProtocolHasAcceptableDefault - testProtocolMutatorReturnsCloneWithChanges - testUsesStreamProvidedInConstructorAsBody - testWithAddedHeaderAllowsHeaderContinuations - testWithAddedHeaderRaisesExceptionForNonStringNonArrayValue - testWithHeaderAllowsHeaderContinuations - testWithHeaderRaisesExceptionForInvalidNestedHeaderValue - testWithHeaderRaisesExceptionForInvalidValueType - testWithHeaderReplacesDifferentCapitalization - testWithHeaderShouldAllowIntegersAndFloats - testWithHeaderShouldRaiseExceptionForInvalidHeaderScalarValues - testWithHeaderShouldRaiseExceptionForInvalidHeaderValuesInArrays - testWithProtocolVersionRaisesExceptionForInvalidVersion - testWithoutHeaderDoesNothingIfHeaderDoesNotExist - - - $name - $name + $value $value $value @@ -577,17 +524,11 @@ $value $version - + [ $value ] [ $value ] [ $value ] - - $values['array'] - - - $values - diff --git a/app/vendor/laminas/laminas-diactoros/src/MessageTrait.php b/app/vendor/laminas/laminas-diactoros/src/MessageTrait.php index e490bd3ea..1c240d10a 100644 --- a/app/vendor/laminas/laminas-diactoros/src/MessageTrait.php +++ b/app/vendor/laminas/laminas-diactoros/src/MessageTrait.php @@ -370,7 +370,7 @@ private function validateProtocolVersion($version) : void // HTTP/1 uses a "." numbering scheme to indicate // versions of the protocol, while HTTP/2 does not. - if (! preg_match('#^(1\.[01]|2)$#', $version)) { + if (! preg_match('#^(1\.[01]|2(\.0)?)$#', $version)) { throw new Exception\InvalidArgumentException(sprintf( 'Unsupported HTTP protocol version "%s" provided', $version diff --git a/app/vendor/laminas/laminas-httphandlerrunner/CHANGELOG.md b/app/vendor/laminas/laminas-httphandlerrunner/CHANGELOG.md index ebdf9eeec..1f09be2a4 100644 --- a/app/vendor/laminas/laminas-httphandlerrunner/CHANGELOG.md +++ b/app/vendor/laminas/laminas-httphandlerrunner/CHANGELOG.md @@ -2,6 +2,25 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 1.5.0 - 2021-09-22 + + +----- + +### Release Notes for [1.5.0](https://github.com/laminas/laminas-httphandlerrunner/milestone/6) + +Feature release (minor) + +### 1.5.0 + +- Total issues resolved: **0** +- Total pull requests resolved: **1** +- Total contributors: **1** + +#### Enhancement + + - [24: Support PHP 8.1](https://github.com/laminas/laminas-httphandlerrunner/pull/24) thanks to @Ocramius + ## 1.4.0 - 2021-04-08 diff --git a/app/vendor/laminas/laminas-httphandlerrunner/composer.json b/app/vendor/laminas/laminas-httphandlerrunner/composer.json index 067bc8892..d85d92b79 100644 --- a/app/vendor/laminas/laminas-httphandlerrunner/composer.json +++ b/app/vendor/laminas/laminas-httphandlerrunner/composer.json @@ -27,7 +27,7 @@ } }, "require": { - "php": "^7.3 || ~8.0.0", + "php": "^7.3 || ~8.0.0 || ~8.1.0", "laminas/laminas-zendframework-bridge": "^1.0", "psr/http-message": "^1.0", "psr/http-message-implementation": "^1.0", @@ -35,10 +35,10 @@ }, "require-dev": { "laminas/laminas-coding-standard": "~1.0.0", - "laminas/laminas-diactoros": "^2.1.1", - "phpunit/phpunit": "^9.3", - "psalm/plugin-phpunit": "^0.15.1", - "vimeo/psalm": "^4.6" + "laminas/laminas-diactoros": "^2.8.0", + "phpunit/phpunit": "^9.5.9", + "psalm/plugin-phpunit": "^0.16.1", + "vimeo/psalm": "^4.10.0" }, "autoload": { "psr-4": { diff --git a/app/vendor/laminas/laminas-httphandlerrunner/psalm-baseline.xml b/app/vendor/laminas/laminas-httphandlerrunner/psalm-baseline.xml index cd2cee06b..6889aafd4 100644 --- a/app/vendor/laminas/laminas-httphandlerrunner/psalm-baseline.xml +++ b/app/vendor/laminas/laminas-httphandlerrunner/psalm-baseline.xml @@ -1,5 +1,5 @@ - + $index @@ -10,6 +10,11 @@ emit + + ReturnTypeWillChange + ReturnTypeWillChange + ReturnTypeWillChange + InvalidArgumentException InvalidArgumentException @@ -20,9 +25,6 @@ headers_sent() - - assertNoPreviousOutput - $header @@ -34,19 +36,19 @@ $length $remaining - + $length $remaining + $remaining $last $remaining - - - - $emitter - + + $length + $unit + @@ -66,44 +68,7 @@ $serverRequestFactory() - - - testInvocationReturnsArray - testReturnedArrayContainsDependencies - - - ($this->provider)() - - - $this->provider - - - $this->provider - - - - - testDoesNotInjectContentLengthHeaderIfStreamSizeIsUnknown - testDoesNotLetResponseCodeBeOverriddenByPHP - testEmitsMessageBody - testEmitsResponseHeaders - testMultipleSetCookieHeadersAreNotReplaced - - - - nonEmitterValues - testCannotPushNonEmitterToStack - testCannotSetNonEmitterToSpecificIndex - testCannotUnshiftNonEmitterToStack - testEmitLoopsThroughEmittersUntilOneReturnsTrueValue - testEmitReturnsFalseIfLastEmmitterReturnsFalse - testEmitReturnsFalseIfNoEmittersAreComposed - testIsAnEmitterImplementation - testIsAnSplStack - testOffsetSetReplacesExistingValue - testUnshiftAddsNewEmitter - $value $value @@ -166,6 +131,10 @@ $first $last + + $length + $unit + @@ -182,40 +151,23 @@ function (Throwable $e) use ($response) { function (Throwable $e) { - - testRaisesTypeErrorIfServerErrorResponseGeneratorFactoryDoesNotReturnAResponse - testRaisesTypeErrorIfServerRequestFactoryDoesNotReturnARequestInstance - testRunPassesRequestGeneratedByRequestFactoryToHandleWhenNoRequestPassedToRun - testUsesErrorResponseGeneratorToGenerateResponseWhenRequestFactoryRaisesException - - - - - push - reset - + + $e + - - $contents - $data $remainingContents - + $data $remainingContents - $this->contents - - string - string + string - - $data - $remainingContents + is_callable($this->contents) ? ($this->contents)(0) : $this->contents diff --git a/app/vendor/laminas/laminas-httphandlerrunner/src/Emitter/EmitterStack.php b/app/vendor/laminas/laminas-httphandlerrunner/src/Emitter/EmitterStack.php index 3c0d85fbe..5fddede04 100644 --- a/app/vendor/laminas/laminas-httphandlerrunner/src/Emitter/EmitterStack.php +++ b/app/vendor/laminas/laminas-httphandlerrunner/src/Emitter/EmitterStack.php @@ -12,6 +12,7 @@ use Laminas\HttpHandlerRunner\Exception; use Psr\Http\Message\ResponseInterface; +use ReturnTypeWillChange; use SplStack; /** @@ -53,6 +54,7 @@ public function emit(ResponseInterface $response) : bool * @return void * @throws InvalidArgumentException if not an EmitterInterface instance */ + #[ReturnTypeWillChange] public function offsetSet($index, $emitter) { $this->validateEmitter($emitter); @@ -66,6 +68,7 @@ public function offsetSet($index, $emitter) * @return void * @throws InvalidArgumentException if not an EmitterInterface instance */ + #[ReturnTypeWillChange] public function push($emitter) { $this->validateEmitter($emitter); @@ -79,6 +82,7 @@ public function push($emitter) * @return void * @throws InvalidArgumentException if not an EmitterInterface instance */ + #[ReturnTypeWillChange] public function unshift($emitter) { $this->validateEmitter($emitter); diff --git a/app/vendor/league/container/.github/FUNDING.yml b/app/vendor/league/container/.github/FUNDING.yml new file mode 100644 index 000000000..3b7dd88ba --- /dev/null +++ b/app/vendor/league/container/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [philipobenito] diff --git a/app/vendor/league/container/.github/workflows/test.yml b/app/vendor/league/container/.github/workflows/test.yml new file mode 100644 index 000000000..21e4ea67c --- /dev/null +++ b/app/vendor/league/container/.github/workflows/test.yml @@ -0,0 +1,73 @@ +name: Tests + +on: + push: ~ + pull_request: ~ + +jobs: + phpcs: + name: PHPCS + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - uses: shivammathur/setup-php@v2 + with: + php-version: 7.0 + extensions: curl, mbstring + coverage: none + tools: composer:v2, cs2pr + + - run: composer update --no-progress + - run: vendor/bin/phpcs -q --report=checkstyle | cs2pr + + phpunit: + name: PHPUnit on ${{ matrix.php }} ${{ matrix.composer-flags }} + runs-on: ubuntu-latest + strategy: + matrix: + php: ['7.0', '7.1', '7.2', '7.3', '7.4'] + coverage: [true] + composer-flags: [''] + include: + - php: '8.0' + coverage: false + composer-flags: '--ignore-platform-req=php' + - php: '7.0' + coverage: false + composer-flags: '--prefer-lowest' + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: curl, mbstring, pcov + tools: composer:v2 + + - run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: "Use PHPUnit 9.3+ on PHP 8" + run: composer require --no-update --dev phpunit/phpunit:^9.3 + if: "matrix.php == '8.0'" + + - name: "Setup PCOV" + run: | + composer require pcov/clobber + vendor/bin/pcov clobber + if: "matrix.php != '7.0' && matrix.php != '8.0'" + + - run: composer update --no-progress ${{ matrix.composer-flags }} + + - run: vendor/bin/phpunit --no-coverage + if: ${{ !matrix.coverage }} + + - run: vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover + if: ${{ matrix.coverage }} + + - run: php vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover + if: ${{ matrix.coverage }} diff --git a/app/vendor/league/container/CHANGELOG.md b/app/vendor/league/container/CHANGELOG.md new file mode 100644 index 000000000..4682ca868 --- /dev/null +++ b/app/vendor/league/container/CHANGELOG.md @@ -0,0 +1,228 @@ +# Changelog + +All Notable changes to `League\Container` will be documented in this file + +## 3.4.1 + +### Added +- Way to handle non-public controllers safely (@beryllium) +- PHPUnit ^7.0 for PHP versions that support it (@beryllium) + +## 3.4.0 + +### Removed +- Support for `psr/container` ^2.0.0 as the interface cannot be reconciled between versions + +## 3.3.5 + +### Added +- Support for `psr/container` ^2.0.0 + +## 3.3.4 + +### Fixed +- Fixed an issue that caused a recursive `register` call. @pcoutinho +- Fixed a return type declaration. @orbex + +## 3.3.3 + +### Fixed +- Fixed bug relating to `ReflectionContainer::call` on arrow functions. + +## 3.3.2 + +### Added +- Experimental support for PHP 8. + +### Fixed +- Fix issue when preventing reflection from using default value for arguments. + +## 3.3.1 + +### Fixed +- Respect `$new` argument when getting tagged definitions. + +## 3.3.0 + +### Added +- Support for PHP 7.3 +- `{set,get}LeagueContainer` methods added to ContainerAwareTrait as a temporary measure until next major release when this can be properly addressed, less hinting of `Psr\Container\ContainerInterface` + +### Changed +- Various internal code improvements + +### Fixed +- Fix for `setConcrete` not re-resolving class on when overriding (@jleeothon) +- Fix stack overflow error incase a service provider lies about providing a specific service (@azjezz) +- Fix issue where providers may be aggregated multiple times (@bwg) +- Various documentation fixes + +## 3.2.2 + +### Fixed +- Fixed issue that prevented service providers from registering if a previous one in the aggregate was already registered. + +## 3.2.1 + +### Fixed +- Fixed issue where all service providers were registered regardless of whether they need to be. + +## 3.2.0 + +### Added +- Added ability to add definition as not shared when container is set to default to shared. +- Added `{set|get}Concrete` to definitions to allow for better use of `extend`. + +## 3.1.0 + +### Added +- Re-added the `share` proxy method that was mistakenly removed in previous major release. +- Added ability to set Conatiner to "share" by default using `defaultToShared` method. +- Added ability for `ReflectionContainer` to cache resolutions and pull from cache for following calls. + +## 3.0.1 + +### Added +- Allow definition aggregates to be built outside of container. + +## 3.0.0 + +### Added +- Service providers can now be pulled from the container if they are registered. +- Definition logic now handled by aggregate for better separation. +- Now able to add tags to a definition to return an array of items containing that tag. + +### Changed +- Updated minimum PHP requirements to 7.0. +- Now depend directly on PSR-11 interfaces, including providing PSR-11 exceptions. +- Refactored inflector logic to accept type on construction and use generator to iterate. +- Refactored service provider logic with better separation and performance. +- Merged service provider signature logic in to one interface and abstract. +- Heavily simplified definition logic providing more control to user. + +## 2.4.1 + +### Fixed +- Ensures `ReflectionContainer` converts class name in array callable to object. + +## 2.4.0 + +### Changed +- Can now wrap shared objects as `RawArgument`. +- Ability to override shared items. + +### Fixed +- Booleans now recognised as accepted values. +- Various docblock fixes. +- Unused imports removed. +- Unreachable arguments no longer passed. + +## 2.3.0 + +### Added +- Now implementation of the PSR-11. + +## 2.2.0 + +### Changed +- Service providers can now be added multiple times by giving them a signature. + +## 2.1.0 + +### Added +- Allow resolving of `RawArgument` objects as first class dependencies. + +### Changed +- Unnecessary recursion removed from `Container::get`. + +## 2.0.3 + +### Fixed +- Bug where delegating container was not passed to delegate when needed. +- Bug where `Container::extend` would not return a shared definition to extend. + +## 2.0.2 + +### Fixed +- Bug introduced in 2.0.1 where shared definitions registered via a service provider would never be returned as shared. + +## 2.0.1 + +### Fixed +- Bug where shared definitions were not stored as shared. + +## 2.0.0 + +### Added +- Now implementation of the container-interop project. +- `BootableServiceProviderInterface` for eagerly loaded service providers. +- Delegate container functionality. +- `RawArgument` to ensure scalars are not resolved from the container but seen as an argument. + +### Altered +- Refactor of definition functionality. +- `Container::share` replaces `singleton` functionality to improve understanding. +- Auto wiring is now disabled by default. +- Auto wiring abstracted to be a delegate container `ReflectionContainer` handling all reflection based functionality. +- Inflection functionality abstracted to an aggregate. +- Service provider functionality abstracted to an aggregate. +- Much bloat removed. +- `Container::call` now proxies to `ReflectionContainer::call` and handles argument resolution in a much more efficient way. + +### Removed +- Ability to register invokables, this functionality added a layer of complexity too large for the problem it solved. +- Container no longer accepts a configuration array, this functionality will now be provided by an external service provider package. + +## 1.4.0 + +### Added +- Added `isRegisteredCallable` method to public API. +- Invoking `call` now accepts named arguments at runtime. + +### Fixed +- Container now stores instantiated Service Providers after first instantiation. +- Extending a definition now looks in Service Providers as well as just Definitions. + +## 1.3.1 - 2015-02-21 + +### Fixed +- Fixed bug where arbitrary values were attempted to be resolved as classes. + +## 1.3.0 - 2015-02-09 + +### Added +- Added `ServiceProvider` functionality to allow cleaner resolving of complex dependencies. +- Added `Inflector` functionality to allow for manipulation of resolved objects of a specific type. +- Improvements to DRY throughout the package. + +### Fixed +- Setter in `ContainerAwareTrait` now returns self (`$this`). + +## 1.2.1 - 2015-01-29 + +### Fixed +- Allow arbitrary values to be registered via container config. + +## 1.2.0 - 2015-01-13 + +### Added +- Improvements to `Container::call` functionality. + +### Fixed +- General code tidy. +- Improvements to test suite. + +## 1.1.1 - 2015-01-13 + +### Fixed +- Allow singleton to be passed as method argument. + +## 1.1.0 - 2015-01-12 + +### Added +- Addition of `ContainerAwareTrait` to provide functionality from `ContainerAwareInterface`. + +## 1.0.0 - 2015-01-12 + +### Added +- Migrated from [Orno\Di](https://github.com/orno/di). diff --git a/app/vendor/league/container/CONTRIBUTING.md b/app/vendor/league/container/CONTRIBUTING.md new file mode 100644 index 000000000..0c45c52dd --- /dev/null +++ b/app/vendor/league/container/CONTRIBUTING.md @@ -0,0 +1,29 @@ +# Contributing + +Contributions are **welcome** and will be fully **credited**. + +We accept contributions via Pull Requests on [Github](https://github.com/thephpleague/container). + +## Pull Requests + +- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). + +- **Add tests!** - Your patch won't be accepted if it doesn't have tests. + +- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. + +- **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. + +- **Create feature branches** - Don't ask us to pull from your master branch. + +- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. + +- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting. + +## Running Tests + +``` bash +$ composer test +``` + +**Happy coding**! diff --git a/app/vendor/league/container/LICENSE.md b/app/vendor/league/container/LICENSE.md new file mode 100644 index 000000000..36f37e7f5 --- /dev/null +++ b/app/vendor/league/container/LICENSE.md @@ -0,0 +1,21 @@ +# The MIT License (MIT) + +Copyright (c) 2017 Phil Bennett + +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. diff --git a/app/vendor/league/container/README.md b/app/vendor/league/container/README.md new file mode 100644 index 000000000..854d6708e --- /dev/null +++ b/app/vendor/league/container/README.md @@ -0,0 +1,65 @@ +# Container (Dependency Injection) + +[![Author](http://img.shields.io/badge/author-@philipobenito-blue.svg?style=for-the-badge)](https://twitter.com/philipobenito) +[![Latest Version](https://img.shields.io/github/release/thephpleague/container.svg?style=for-the-badge)](https://github.com/thephpleague/container/releases) +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=for-the-badge)](LICENSE.md) +[![Build Status](https://img.shields.io/github/workflow/status/thephpleague/container/Tests/3.x?style=for-the-badge)](https://github.com/thephpleague/container/actions) +[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/thephpleague/container.svg?style=for-the-badge)](https://scrutinizer-ci.com/g/thephpleague/container/code-structure) +[![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/container.svg?style=for-the-badge)](https://scrutinizer-ci.com/g/thephpleague/container) +[![Total Downloads](https://img.shields.io/packagist/dt/league/container.svg?style=for-the-badge)](https://packagist.org/packages/league/container) + +This package is compliant with [PSR-1], [PSR-2], [PSR-4] and [PSR-11]. If you notice compliance oversights, please send a patch via pull request. + +[PSR-1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md +[PSR-2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md +[PSR-4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md +[PSR-11]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md + +## Install + +Via Composer + +``` bash +$ composer require league/container +``` + +## Requirements + +The following versions of PHP are supported by this version. + +* PHP 7.0 +* PHP 7.1 +* PHP 7.2 +* PHP 7.3 +* PHP 7.4 +* PHP 8.0 + +## Documentation + +Container has [full documentation](http://container.thephpleague.com), powered by [Jekyll](http://jekyllrb.com/). + +Contribute to this documentation in the [docs/](https://github.com/thephpleague/container/tree/master/docs) sub-directory. + +## Testing + +``` bash +$ composer test +``` + +## Contributing + +Please see [CONTRIBUTING](https://github.com/thephpleague/container/blob/master/CONTRIBUTING.md) for details. + +## Security + +If you discover any security related issues, please email philipobenito@gmail.com instead of using the issue tracker. + +## Credits + +- [Phil Bennett](https://github.com/philipobenito) +- [All Contributors](https://github.com/thephpleague/container/contributors) +- `Orno\Di` contributors + +## License + +The MIT License (MIT). Please see [License File](https://github.com/thephpleague/container/blob/master/LICENSE.md) for more information. diff --git a/app/vendor/league/container/composer.json b/app/vendor/league/container/composer.json new file mode 100644 index 000000000..61cedd9be --- /dev/null +++ b/app/vendor/league/container/composer.json @@ -0,0 +1,62 @@ +{ + "name": "league/container", + "description": "A fast and intuitive dependency injection container.", + "keywords": [ + "league", + "container", + "dependency", + "injection", + "di", + "service", + "provider" + ], + "homepage": "https://github.com/thephpleague/container", + "license": "MIT", + "authors": [ + { + "name": "Phil Bennett", + "email": "philipobenito@gmail.com", + "homepage": "http://www.philipobenito.com", + "role": "Developer" + } + ], + "require": { + "php": "^7.0 || ^8.0", + "psr/container": "^1.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0 || ^7.0", + "roave/security-advisories": "dev-latest", + "scrutinizer/ocular": "^1.8", + "squizlabs/php_codesniffer": "^3.5" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "replace": { + "orno/di": "~2.0" + }, + "autoload": { + "psr-4": { + "League\\Container\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "League\\Container\\Test\\": "tests" + } + }, + "extra": { + "branch-alias": { + "dev-master": "3.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "scripts": { + "test": [ + "phpunit" + ] + } +} diff --git a/app/vendor/league/container/phpcs.xml b/app/vendor/league/container/phpcs.xml new file mode 100644 index 000000000..139ce5755 --- /dev/null +++ b/app/vendor/league/container/phpcs.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + src + + + + + + + diff --git a/app/vendor/league/container/phpunit.xml b/app/vendor/league/container/phpunit.xml new file mode 100644 index 000000000..1f0d1c034 --- /dev/null +++ b/app/vendor/league/container/phpunit.xml @@ -0,0 +1,29 @@ + + + + + tests + + + + + src/ + + + + + + + + + + diff --git a/app/vendor/league/container/src/Argument/ArgumentResolverInterface.php b/app/vendor/league/container/src/Argument/ArgumentResolverInterface.php new file mode 100644 index 000000000..54b323605 --- /dev/null +++ b/app/vendor/league/container/src/Argument/ArgumentResolverInterface.php @@ -0,0 +1,28 @@ +getValue(); + } elseif ($argument instanceof ClassNameInterface) { + $id = $argument->getClassName(); + } elseif (!is_string($argument)) { + return $argument; + } else { + $justStringValue = true; + $id = $argument; + } + + $container = null; + + try { + $container = $this->getLeagueContainer(); + } catch (ContainerException $e) { + if ($this instanceof ReflectionContainer) { + $container = $this; + } + } + + if ($container !== null) { + try { + return $container->get($id); + } catch (NotFoundException $exception) { + if ($argument instanceof ClassNameWithOptionalValue) { + return $argument->getOptionalValue(); + } + + if ($justStringValue) { + return $id; + } + + throw $exception; + } + } + + if ($argument instanceof ClassNameWithOptionalValue) { + return $argument->getOptionalValue(); + } + + // Just a string value. + return $id; + }, $arguments); + } + + /** + * {@inheritdoc} + */ + public function reflectArguments(ReflectionFunctionAbstract $method, array $args = []) : array + { + $arguments = array_map(function (ReflectionParameter $param) use ($method, $args) { + $name = $param->getName(); + $type = $param->getType(); + + if (array_key_exists($name, $args)) { + return new RawArgument($args[$name]); + } + + if ($type) { + if (PHP_VERSION_ID >= 70100) { + $typeName = $type->getName(); + } else { + $typeName = (string) $type; + } + + $typeName = ltrim($typeName, '?'); + + if ($param->isDefaultValueAvailable()) { + return new ClassNameWithOptionalValue($typeName, $param->getDefaultValue()); + } + + return new ClassName($typeName); + } + + if ($param->isDefaultValueAvailable()) { + return new RawArgument($param->getDefaultValue()); + } + + throw new NotFoundException(sprintf( + 'Unable to resolve a value for parameter (%s) in the function/method (%s)', + $name, + $method->getName() + )); + }, $method->getParameters()); + + return $this->resolveArguments($arguments); + } + + /** + * @return ContainerInterface + */ + abstract public function getContainer() : ContainerInterface; + + /** + * @return Container + */ + abstract public function getLeagueContainer() : Container; +} diff --git a/app/vendor/league/container/src/Argument/ClassName.php b/app/vendor/league/container/src/Argument/ClassName.php new file mode 100644 index 000000000..335fe9385 --- /dev/null +++ b/app/vendor/league/container/src/Argument/ClassName.php @@ -0,0 +1,29 @@ +value = $value; + } + + /** + * {@inheritdoc} + */ + public function getClassName() : string + { + return $this->value; + } +} diff --git a/app/vendor/league/container/src/Argument/ClassNameInterface.php b/app/vendor/league/container/src/Argument/ClassNameInterface.php new file mode 100644 index 000000000..332f98349 --- /dev/null +++ b/app/vendor/league/container/src/Argument/ClassNameInterface.php @@ -0,0 +1,13 @@ +className = $className; + $this->optionalValue = $optionalValue; + } + + /** + * @inheritDoc + */ + public function getClassName(): string + { + return $this->className; + } + + public function getOptionalValue() + { + return $this->optionalValue; + } +} diff --git a/app/vendor/league/container/src/Argument/RawArgument.php b/app/vendor/league/container/src/Argument/RawArgument.php new file mode 100644 index 000000000..46319cac4 --- /dev/null +++ b/app/vendor/league/container/src/Argument/RawArgument.php @@ -0,0 +1,29 @@ +value = $value; + } + + /** + * {@inheritdoc} + */ + public function getValue() + { + return $this->value; + } +} diff --git a/app/vendor/league/container/src/Argument/RawArgumentInterface.php b/app/vendor/league/container/src/Argument/RawArgumentInterface.php new file mode 100644 index 000000000..14157dee7 --- /dev/null +++ b/app/vendor/league/container/src/Argument/RawArgumentInterface.php @@ -0,0 +1,13 @@ +definitions = $definitions ?? new DefinitionAggregate; + $this->providers = $providers ?? new ServiceProviderAggregate; + $this->inflectors = $inflectors ?? new InflectorAggregate; + + if ($this->definitions instanceof ContainerAwareInterface) { + $this->definitions->setLeagueContainer($this); + } + + if ($this->providers instanceof ContainerAwareInterface) { + $this->providers->setLeagueContainer($this); + } + + if ($this->inflectors instanceof ContainerAwareInterface) { + $this->inflectors->setLeagueContainer($this); + } + } + + /** + * Add an item to the container. + * + * @param string $id + * @param mixed $concrete + * @param boolean $shared + * + * @return DefinitionInterface + */ + public function add(string $id, $concrete = null, bool $shared = null) : DefinitionInterface + { + $concrete = $concrete ?? $id; + $shared = $shared ?? $this->defaultToShared; + + return $this->definitions->add($id, $concrete, $shared); + } + + /** + * Proxy to add with shared as true. + * + * @param string $id + * @param mixed $concrete + * + * @return DefinitionInterface + */ + public function share(string $id, $concrete = null) : DefinitionInterface + { + return $this->add($id, $concrete, true); + } + + /** + * Whether the container should default to defining shared definitions. + * + * @param boolean $shared + * + * @return self + */ + public function defaultToShared(bool $shared = true) : ContainerInterface + { + $this->defaultToShared = $shared; + + return $this; + } + + /** + * Get a definition to extend. + * + * @param string $id [description] + * + * @return DefinitionInterface + */ + public function extend(string $id) : DefinitionInterface + { + if ($this->providers->provides($id)) { + $this->providers->register($id); + } + + if ($this->definitions->has($id)) { + return $this->definitions->getDefinition($id); + } + + throw new NotFoundException( + sprintf('Unable to extend alias (%s) as it is not being managed as a definition', $id) + ); + } + + /** + * Add a service provider. + * + * @param ServiceProviderInterface|string $provider + * + * @return self + */ + public function addServiceProvider($provider) : self + { + $this->providers->add($provider); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function get($id, bool $new = false) + { + if ($this->definitions->has($id)) { + $resolved = $this->definitions->resolve($id, $new); + return $this->inflectors->inflect($resolved); + } + + if ($this->definitions->hasTag($id)) { + $arrayOf = $this->definitions->resolveTagged($id, $new); + + array_walk($arrayOf, function (&$resolved) { + $resolved = $this->inflectors->inflect($resolved); + }); + + return $arrayOf; + } + + if ($this->providers->provides($id)) { + $this->providers->register($id); + + if (!$this->definitions->has($id) && !$this->definitions->hasTag($id)) { + throw new ContainerException(sprintf('Service provider lied about providing (%s) service', $id)); + } + + return $this->get($id, $new); + } + + foreach ($this->delegates as $delegate) { + if ($delegate->has($id)) { + $resolved = $delegate->get($id); + return $this->inflectors->inflect($resolved); + } + } + + throw new NotFoundException(sprintf('Alias (%s) is not being managed by the container or delegates', $id)); + } + + /** + * {@inheritdoc} + */ + public function has($id) + { + if ($this->definitions->has($id)) { + return true; + } + + if ($this->definitions->hasTag($id)) { + return true; + } + + if ($this->providers->provides($id)) { + return true; + } + + foreach ($this->delegates as $delegate) { + if ($delegate->has($id)) { + return true; + } + } + + return false; + } + + /** + * Allows for manipulation of specific types on resolution. + * + * @param string $type + * @param callable|null $callback + * + * @return InflectorInterface + */ + public function inflector(string $type, callable $callback = null) : InflectorInterface + { + return $this->inflectors->add($type, $callback); + } + + /** + * Delegate a backup container to be checked for services if it + * cannot be resolved via this container. + * + * @param ContainerInterface $container + * + * @return self + */ + public function delegate(ContainerInterface $container) : self + { + $this->delegates[] = $container; + + if ($container instanceof ContainerAwareInterface) { + $container->setLeagueContainer($this); + } + + return $this; + } +} diff --git a/app/vendor/league/container/src/ContainerAwareInterface.php b/app/vendor/league/container/src/ContainerAwareInterface.php new file mode 100644 index 000000000..6e980f6ad --- /dev/null +++ b/app/vendor/league/container/src/ContainerAwareInterface.php @@ -0,0 +1,40 @@ +container = $container; + + return $this; + } + + /** + * Get the container. + * + * @return ContainerInterface + */ + public function getContainer() : ContainerInterface + { + if ($this->container instanceof ContainerInterface) { + return $this->container; + } + + throw new ContainerException('No container implementation has been set.'); + } + + /** + * Set a container. + * + * @param Container $container + * + * @return self + */ + public function setLeagueContainer(Container $container) : ContainerAwareInterface + { + $this->container = $container; + $this->leagueContainer = $container; + + return $this; + } + + /** + * Get the container. + * + * @return Container + */ + public function getLeagueContainer() : Container + { + if ($this->leagueContainer instanceof Container) { + return $this->leagueContainer; + } + + throw new ContainerException('No container implementation has been set.'); + } +} diff --git a/app/vendor/league/container/src/Definition/Definition.php b/app/vendor/league/container/src/Definition/Definition.php new file mode 100644 index 000000000..8a3128e9b --- /dev/null +++ b/app/vendor/league/container/src/Definition/Definition.php @@ -0,0 +1,278 @@ +alias = $id; + $this->concrete = $concrete; + } + + /** + * {@inheritdoc} + */ + public function addTag(string $tag) : DefinitionInterface + { + $this->tags[$tag] = true; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function hasTag(string $tag) : bool + { + return isset($this->tags[$tag]); + } + + /** + * {@inheritdoc} + */ + public function setAlias(string $id) : DefinitionInterface + { + $this->alias = $id; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getAlias() : string + { + return $this->alias; + } + + /** + * {@inheritdoc} + */ + public function setShared(bool $shared = true) : DefinitionInterface + { + $this->shared = $shared; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function isShared() : bool + { + return $this->shared; + } + + /** + * {@inheritdoc} + */ + public function getConcrete() + { + return $this->concrete; + } + + /** + * {@inheritdoc} + */ + public function setConcrete($concrete) : DefinitionInterface + { + $this->concrete = $concrete; + $this->resolved = null; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addArgument($arg) : DefinitionInterface + { + $this->arguments[] = $arg; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addArguments(array $args) : DefinitionInterface + { + foreach ($args as $arg) { + $this->addArgument($arg); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addMethodCall(string $method, array $args = []) : DefinitionInterface + { + $this->methods[] = [ + 'method' => $method, + 'arguments' => $args + ]; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function addMethodCalls(array $methods = []) : DefinitionInterface + { + foreach ($methods as $method => $args) { + $this->addMethodCall($method, $args); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function resolve(bool $new = false) + { + $concrete = $this->concrete; + + if ($this->isShared() && $this->resolved !== null && $new === false) { + return $this->resolved; + } + + if (is_callable($concrete)) { + $concrete = $this->resolveCallable($concrete); + } + + if ($concrete instanceof RawArgumentInterface) { + $this->resolved = $concrete->getValue(); + + return $concrete->getValue(); + } + + if ($concrete instanceof ClassNameInterface) { + $concrete = $concrete->getClassName(); + } + + if (is_string($concrete) && class_exists($concrete)) { + $concrete = $this->resolveClass($concrete); + } + + if (is_object($concrete)) { + $concrete = $this->invokeMethods($concrete); + } + + if (is_string($concrete) && $this->getContainer()->has($concrete)) { + $concrete = $this->getContainer()->get($concrete); + } + + $this->resolved = $concrete; + + return $concrete; + } + + /** + * Resolve a callable. + * + * @param callable $concrete + * + * @return mixed + */ + protected function resolveCallable(callable $concrete) + { + $resolved = $this->resolveArguments($this->arguments); + + return call_user_func_array($concrete, $resolved); + } + + /** + * Resolve a class. + * + * @param string $concrete + * + * @return object + * + * @throws ReflectionException + */ + protected function resolveClass(string $concrete) + { + $resolved = $this->resolveArguments($this->arguments); + $reflection = new ReflectionClass($concrete); + + return $reflection->newInstanceArgs($resolved); + } + + /** + * Invoke methods on resolved instance. + * + * @param object $instance + * + * @return object + */ + protected function invokeMethods($instance) + { + foreach ($this->methods as $method) { + $args = $this->resolveArguments($method['arguments']); + + /** @var callable $callable */ + $callable = [$instance, $method['method']]; + call_user_func_array($callable, $args); + } + + return $instance; + } +} diff --git a/app/vendor/league/container/src/Definition/DefinitionAggregate.php b/app/vendor/league/container/src/Definition/DefinitionAggregate.php new file mode 100644 index 000000000..3c4fb8dfb --- /dev/null +++ b/app/vendor/league/container/src/Definition/DefinitionAggregate.php @@ -0,0 +1,124 @@ +definitions = array_filter($definitions, function ($definition) { + return ($definition instanceof DefinitionInterface); + }); + } + + /** + * {@inheritdoc} + */ + public function add(string $id, $definition, bool $shared = false) : DefinitionInterface + { + if (!$definition instanceof DefinitionInterface) { + $definition = new Definition($id, $definition); + } + + $this->definitions[] = $definition + ->setAlias($id) + ->setShared($shared) + ; + + return $definition; + } + + /** + * {@inheritdoc} + */ + public function has(string $id) : bool + { + foreach ($this->getIterator() as $definition) { + if ($id === $definition->getAlias()) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function hasTag(string $tag) : bool + { + foreach ($this->getIterator() as $definition) { + if ($definition->hasTag($tag)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getDefinition(string $id) : DefinitionInterface + { + foreach ($this->getIterator() as $definition) { + if ($id === $definition->getAlias()) { + return $definition->setLeagueContainer($this->getLeagueContainer()); + } + } + + throw new NotFoundException(sprintf('Alias (%s) is not being handled as a definition.', $id)); + } + + /** + * {@inheritdoc} + */ + public function resolve(string $id, bool $new = false) + { + return $this->getDefinition($id)->resolve($new); + } + + /** + * {@inheritdoc} + */ + public function resolveTagged(string $tag, bool $new = false) : array + { + $arrayOf = []; + + foreach ($this->getIterator() as $definition) { + if ($definition->hasTag($tag)) { + $arrayOf[] = $definition->setLeagueContainer($this->getLeagueContainer())->resolve($new); + } + } + + return $arrayOf; + } + + /** + * {@inheritdoc} + */ + public function getIterator() : Generator + { + $count = count($this->definitions); + + for ($i = 0; $i < $count; $i++) { + yield $this->definitions[$i]; + } + } +} diff --git a/app/vendor/league/container/src/Definition/DefinitionAggregateInterface.php b/app/vendor/league/container/src/Definition/DefinitionAggregateInterface.php new file mode 100644 index 000000000..3e9876d3c --- /dev/null +++ b/app/vendor/league/container/src/Definition/DefinitionAggregateInterface.php @@ -0,0 +1,67 @@ +type = $type; + $this->callback = $callback; + } + + /** + * {@inheritdoc} + */ + public function getType() : string + { + return $this->type; + } + + /** + * {@inheritdoc} + */ + public function invokeMethod(string $name, array $args) : InflectorInterface + { + $this->methods[$name] = $args; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function invokeMethods(array $methods) : InflectorInterface + { + foreach ($methods as $name => $args) { + $this->invokeMethod($name, $args); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setProperty(string $property, $value) : InflectorInterface + { + $this->properties[$property] = $this->resolveArguments([$value])[0]; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setProperties(array $properties) : InflectorInterface + { + foreach ($properties as $property => $value) { + $this->setProperty($property, $value); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function inflect($object) + { + $properties = $this->resolveArguments(array_values($this->properties)); + $properties = array_combine(array_keys($this->properties), $properties); + + // array_combine() can technically return false + foreach ($properties ?: [] as $property => $value) { + $object->{$property} = $value; + } + + foreach ($this->methods as $method => $args) { + $args = $this->resolveArguments($args); + + /** @var callable $callable */ + $callable = [$object, $method]; + call_user_func_array($callable, $args); + } + + if ($this->callback !== null) { + call_user_func($this->callback, $object); + } + } +} diff --git a/app/vendor/league/container/src/Inflector/InflectorAggregate.php b/app/vendor/league/container/src/Inflector/InflectorAggregate.php new file mode 100644 index 000000000..d6318a898 --- /dev/null +++ b/app/vendor/league/container/src/Inflector/InflectorAggregate.php @@ -0,0 +1,58 @@ +inflectors[] = $inflector; + + return $inflector; + } + + /** + * {@inheritdoc} + */ + public function getIterator() : Generator + { + $count = count($this->inflectors); + + for ($i = 0; $i < $count; $i++) { + yield $this->inflectors[$i]; + } + } + + /** + * {@inheritdoc} + */ + public function inflect($object) + { + foreach ($this->getIterator() as $inflector) { + $type = $inflector->getType(); + + if (! $object instanceof $type) { + continue; + } + + $inflector->setLeagueContainer($this->getLeagueContainer()); + $inflector->inflect($object); + } + + return $object; + } +} diff --git a/app/vendor/league/container/src/Inflector/InflectorAggregateInterface.php b/app/vendor/league/container/src/Inflector/InflectorAggregateInterface.php new file mode 100644 index 000000000..082e1663f --- /dev/null +++ b/app/vendor/league/container/src/Inflector/InflectorAggregateInterface.php @@ -0,0 +1,27 @@ +cacheResolutions === true && array_key_exists($id, $this->cache)) { + return $this->cache[$id]; + } + + if (! $this->has($id)) { + throw new NotFoundException( + sprintf('Alias (%s) is not an existing class and therefore cannot be resolved', $id) + ); + } + + $reflector = new ReflectionClass($id); + $construct = $reflector->getConstructor(); + + if ($construct && !$construct->isPublic()) { + throw new NotFoundException( + sprintf('Alias (%s) has a non-public constructor and therefore cannot be instantiated', $id) + ); + } + + $resolution = $construct === null + ? new $id + : $resolution = $reflector->newInstanceArgs($this->reflectArguments($construct, $args)) + ; + + if ($this->cacheResolutions === true) { + $this->cache[$id] = $resolution; + } + + return $resolution; + } + + /** + * {@inheritdoc} + */ + public function has($id) + { + return class_exists($id); + } + + /** + * Invoke a callable via the container. + * + * @param callable $callable + * @param array $args + * + * @return mixed + * + * @throws ReflectionException + */ + public function call(callable $callable, array $args = []) + { + if (is_string($callable) && strpos($callable, '::') !== false) { + $callable = explode('::', $callable); + } + + if (is_array($callable)) { + if (is_string($callable[0])) { + $callable[0] = $this->getContainer()->get($callable[0]); + } + + $reflection = new ReflectionMethod($callable[0], $callable[1]); + + if ($reflection->isStatic()) { + $callable[0] = null; + } + + return $reflection->invokeArgs($callable[0], $this->reflectArguments($reflection, $args)); + } + + if (is_object($callable)) { + $reflection = new ReflectionMethod($callable, '__invoke'); + + return $reflection->invokeArgs($callable, $this->reflectArguments($reflection, $args)); + } + + $reflection = new ReflectionFunction(\Closure::fromCallable($callable)); + + return $reflection->invokeArgs($this->reflectArguments($reflection, $args)); + } + + /** + * Whether the container should default to caching resolutions and returning + * the cache on following calls. + * + * @param boolean $option + * + * @return self + */ + public function cacheResolutions(bool $option = true) : ContainerInterface + { + $this->cacheResolutions = $option; + + return $this; + } +} diff --git a/app/vendor/league/container/src/ServiceProvider/AbstractServiceProvider.php b/app/vendor/league/container/src/ServiceProvider/AbstractServiceProvider.php new file mode 100644 index 000000000..433e679a8 --- /dev/null +++ b/app/vendor/league/container/src/ServiceProvider/AbstractServiceProvider.php @@ -0,0 +1,46 @@ +provides, true); + } + + /** + * {@inheritdoc} + */ + public function setIdentifier(string $id) : ServiceProviderInterface + { + $this->identifier = $id; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getIdentifier() : string + { + return $this->identifier ?? get_class($this); + } +} diff --git a/app/vendor/league/container/src/ServiceProvider/BootableServiceProviderInterface.php b/app/vendor/league/container/src/ServiceProvider/BootableServiceProviderInterface.php new file mode 100644 index 000000000..2ed1b2a34 --- /dev/null +++ b/app/vendor/league/container/src/ServiceProvider/BootableServiceProviderInterface.php @@ -0,0 +1,14 @@ +getContainer()->has($provider)) { + $provider = $this->getContainer()->get($provider); + } elseif (is_string($provider) && class_exists($provider)) { + $provider = new $provider; + } + + if (in_array($provider, $this->providers, true)) { + return $this; + } + + if ($provider instanceof ContainerAwareInterface) { + $provider->setLeagueContainer($this->getLeagueContainer()); + } + + if ($provider instanceof BootableServiceProviderInterface) { + $provider->boot(); + } + + if ($provider instanceof ServiceProviderInterface) { + $this->providers[] = $provider; + + return $this; + } + + throw new ContainerException( + 'A service provider must be a fully qualified class name or instance ' . + 'of (\League\Container\ServiceProvider\ServiceProviderInterface)' + ); + } + + /** + * {@inheritdoc} + */ + public function provides(string $service) : bool + { + foreach ($this->getIterator() as $provider) { + if ($provider->provides($service)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getIterator() : Generator + { + $count = count($this->providers); + + for ($i = 0; $i < $count; $i++) { + yield $this->providers[$i]; + } + } + + /** + * {@inheritdoc} + */ + public function register(string $service) + { + if (false === $this->provides($service)) { + throw new ContainerException( + sprintf('(%s) is not provided by a service provider', $service) + ); + } + + foreach ($this->getIterator() as $provider) { + if (in_array($provider->getIdentifier(), $this->registered, true)) { + continue; + } + + if ($provider->provides($service)) { + $this->registered[] = $provider->getIdentifier(); + $provider->register(); + } + } + } +} diff --git a/app/vendor/league/container/src/ServiceProvider/ServiceProviderAggregateInterface.php b/app/vendor/league/container/src/ServiceProvider/ServiceProviderAggregateInterface.php new file mode 100644 index 000000000..f63f06971 --- /dev/null +++ b/app/vendor/league/container/src/ServiceProvider/ServiceProviderAggregateInterface.php @@ -0,0 +1,36 @@ +leagueContainer property or the `getLeagueContainer` method + * from the ContainerAwareTrait. + * + * @return void + */ + public function register(); + + /** + * Set a custom id for the service provider. This enables + * registering the same service provider multiple times. + * + * @param string $id + * + * @return self + */ + public function setIdentifier(string $id) : ServiceProviderInterface; + + /** + * The id of the service provider uniquely identifies it, so + * that we can quickly determine if it has already been registered. + * Defaults to get_class($provider). + * + * @return string + */ + public function getIdentifier() : string; +} diff --git a/app/vendor/nikic/php-parser/grammar/php7.y b/app/vendor/nikic/php-parser/grammar/php7.y index d9a450379..ef0f271c6 100644 --- a/app/vendor/nikic/php-parser/grammar/php7.y +++ b/app/vendor/nikic/php-parser/grammar/php7.y @@ -530,24 +530,29 @@ non_empty_parameter_list: | non_empty_parameter_list ',' parameter { push($1, $3); } ; -optional_visibility_modifier: +optional_property_modifiers: /* empty */ { $$ = 0; } - | T_PUBLIC { $$ = Stmt\Class_::MODIFIER_PUBLIC; } + | optional_property_modifiers property_modifier + { $this->checkModifier($1, $2, #2); $$ = $1 | $2; } +; + +property_modifier: + T_PUBLIC { $$ = Stmt\Class_::MODIFIER_PUBLIC; } | T_PROTECTED { $$ = Stmt\Class_::MODIFIER_PROTECTED; } | T_PRIVATE { $$ = Stmt\Class_::MODIFIER_PRIVATE; } | T_READONLY { $$ = Stmt\Class_::MODIFIER_READONLY; } ; parameter: - optional_attributes optional_visibility_modifier optional_type_without_static + optional_attributes optional_property_modifiers optional_type_without_static optional_arg_ref optional_ellipsis plain_variable { $$ = new Node\Param($6, null, $3, $4, $5, attributes(), $2, $1); $this->checkParam($$); } - | optional_attributes optional_visibility_modifier optional_type_without_static + | optional_attributes optional_property_modifiers optional_type_without_static optional_arg_ref optional_ellipsis plain_variable '=' expr { $$ = new Node\Param($6, $8, $3, $4, $5, attributes(), $2, $1); $this->checkParam($$); } - | optional_attributes optional_visibility_modifier optional_type_without_static + | optional_attributes optional_property_modifiers optional_type_without_static optional_arg_ref optional_ellipsis error { $$ = new Node\Param(Expr\Error[], null, $3, $4, $5, attributes(), $2, $1); } ; @@ -556,6 +561,7 @@ type_expr: type { $$ = $1; } | '?' type { $$ = Node\NullableType[$2]; } | union_type { $$ = Node\UnionType[$1]; } + | intersection_type { $$ = Node\IntersectionType[$1]; } ; type: @@ -579,10 +585,24 @@ union_type_without_static: | union_type_without_static '|' type_without_static { push($1, $3); } ; +intersection_type: + type T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type { init($1, $3); } + | intersection_type T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type + { push($1, $3); } +; + +intersection_type_without_static: + type_without_static T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type_without_static + { init($1, $3); } + | intersection_type_without_static T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type_without_static + { push($1, $3); } +; + type_expr_without_static: type_without_static { $$ = $1; } | '?' type_without_static { $$ = Node\NullableType[$2]; } | union_type_without_static { $$ = Node\UnionType[$1]; } + | intersection_type_without_static { $$ = Node\IntersectionType[$1]; } ; optional_type_without_static: @@ -599,6 +619,11 @@ optional_return_type: argument_list: '(' ')' { $$ = array(); } | '(' non_empty_argument_list optional_comma ')' { $$ = $2; } + | '(' variadic_placeholder ')' { init($2); } +; + +variadic_placeholder: + T_ELLIPSIS { $$ = Node\VariadicPlaceholder[]; } ; non_empty_argument_list: diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php b/app/vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php index 8e7db399d..98ea9d336 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php @@ -61,8 +61,7 @@ public function addParams(array $params) { /** * Sets the return type for PHP 7. * - * @param string|Node\Name|Node\NullableType $type One of array, callable, string, int, float, - * bool, iterable, or a class/interface name. + * @param string|Node\Name|Node\Identifier|Node\ComplexType $type * * @return $this The builder instance (for fluid interface) */ diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php b/app/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php index 0ea91683c..de9aae7e5 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php @@ -47,7 +47,7 @@ public function setDefault($value) { /** * Sets type for the parameter. * - * @param string|Node\Name|Node\NullableType|Node\UnionType $type Parameter type + * @param string|Node\Name|Node\Identifier|Node\ComplexType $type Parameter type * * @return $this The builder instance (for fluid interface) */ @@ -63,7 +63,7 @@ public function setType($type) { /** * Sets type for the parameter. * - * @param string|Node\Name|Node\NullableType|Node\UnionType $type Parameter type + * @param string|Node\Name|Node\Identifier|Node\ComplexType $type Parameter type * * @return $this The builder instance (for fluid interface) * diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php b/app/vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php index 90ee4b0ba..68e318565 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php @@ -7,8 +7,8 @@ use PhpParser\Node; use PhpParser\Node\Identifier; use PhpParser\Node\Name; -use PhpParser\Node\NullableType; use PhpParser\Node\Stmt; +use PhpParser\Node\ComplexType; class Property implements PhpParser\Builder { @@ -119,7 +119,7 @@ public function setDocComment($docComment) { /** * Sets the property type for PHP 7.4+. * - * @param string|Name|NullableType|Identifier $type + * @param string|Name|Identifier|ComplexType $type * * @return $this */ diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php b/app/vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php index c6d8f1613..2f0e91273 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php @@ -2,13 +2,13 @@ namespace PhpParser; +use PhpParser\Node\ComplexType; use PhpParser\Node\Expr; use PhpParser\Node\Identifier; use PhpParser\Node\Name; use PhpParser\Node\NullableType; use PhpParser\Node\Scalar; use PhpParser\Node\Stmt; -use PhpParser\Node\UnionType; /** * This class defines helpers used in the implementation of builders. Don't use it directly. @@ -104,29 +104,6 @@ public static function normalizeIdentifierOrExpr($name) { * @return Name The normalized name */ public static function normalizeName($name) : Name { - return self::normalizeNameCommon($name, false); - } - - /** - * Normalizes a name: Converts string names to Name nodes, while also allowing expressions. - * - * @param Expr|Name|string $name The name to normalize - * - * @return Name|Expr The normalized name or expression - */ - public static function normalizeNameOrExpr($name) { - return self::normalizeNameCommon($name, true); - } - - /** - * Normalizes a name: Converts string names to Name nodes, optionally allowing expressions. - * - * @param Expr|Name|string $name The name to normalize - * @param bool $allowExpr Whether to also allow expressions - * - * @return Name|Expr The normalized name, or expression (if allowed) - */ - private static function normalizeNameCommon($name, bool $allowExpr) { if ($name instanceof Name) { return $name; } @@ -147,16 +124,28 @@ private static function normalizeNameCommon($name, bool $allowExpr) { return new Name($name); } - if ($allowExpr) { - if ($name instanceof Expr) { - return $name; - } + throw new \LogicException('Name must be a string or an instance of Node\Name'); + } + + /** + * Normalizes a name: Converts string names to Name nodes, while also allowing expressions. + * + * @param Expr|Name|string $name The name to normalize + * + * @return Name|Expr The normalized name or expression + */ + public static function normalizeNameOrExpr($name) { + if ($name instanceof Expr) { + return $name; + } + + if (!is_string($name) && !($name instanceof Name)) { throw new \LogicException( 'Name must be a string or an instance of Node\Name or Node\Expr' ); } - throw new \LogicException('Name must be a string or an instance of Node\Name'); + return self::normalizeName($name); } /** @@ -165,18 +154,18 @@ private static function normalizeNameCommon($name, bool $allowExpr) { * In particular, builtin types become Identifiers, custom types become Names and nullables * are wrapped in NullableType nodes. * - * @param string|Name|Identifier|NullableType|UnionType $type The type to normalize + * @param string|Name|Identifier|ComplexType $type The type to normalize * - * @return Name|Identifier|NullableType|UnionType The normalized type + * @return Name|Identifier|ComplexType The normalized type */ public static function normalizeType($type) { if (!is_string($type)) { if ( !$type instanceof Name && !$type instanceof Identifier && - !$type instanceof NullableType && !$type instanceof UnionType + !$type instanceof ComplexType ) { throw new \LogicException( - 'Type must be a string, or an instance of Name, Identifier, NullableType or UnionType' + 'Type must be a string, or an instance of Name, Identifier or ComplexType' ); } return $type; diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php b/app/vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php index a8f4e334a..5086f8ce2 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php @@ -8,6 +8,7 @@ use PhpParser\Lexer\TokenEmulator\AttributeEmulator; use PhpParser\Lexer\TokenEmulator\EnumTokenEmulator; use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator; +use PhpParser\Lexer\TokenEmulator\ExplicitOctalEmulator; use PhpParser\Lexer\TokenEmulator\FlexibleDocStringEmulator; use PhpParser\Lexer\TokenEmulator\FnTokenEmulator; use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator; @@ -55,6 +56,7 @@ public function __construct(array $options = []) new AttributeEmulator(), new EnumTokenEmulator(), new ReadonlyTokenEmulator(), + new ExplicitOctalEmulator(), ]; // Collect emulators that are relevant for the PHP version we're running diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php b/app/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php new file mode 100644 index 000000000..f5f6805b8 --- /dev/null +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php @@ -0,0 +1,44 @@ +resolveIntegerOrFloatToken($tokens[$i + 1][1]); + array_splice($tokens, $i, 2, [ + [$tokenKind, '0' . $tokens[$i + 1][1], $tokens[$i][2]], + ]); + $c--; + } + } + return $tokens; + } + + private function resolveIntegerOrFloatToken(string $str): int + { + $str = substr($str, 1); + $str = str_replace('_', '', $str); + $num = octdec($str); + return is_float($num) ? \T_DNUMBER : \T_LNUMBER; + } + + public function reverseEmulate(string $code, array $tokens): array { + // Explicit octals were not legal code previously, don't bother. + return $tokens; + } +} \ No newline at end of file diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Arg.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Arg.php index b25b0904a..bcf130e68 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Arg.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Arg.php @@ -2,6 +2,7 @@ namespace PhpParser\Node; +use PhpParser\Node\VariadicPlaceholder; use PhpParser\NodeAbstract; class Arg extends NodeAbstract diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/ComplexType.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/ComplexType.php new file mode 100644 index 000000000..9505532ae --- /dev/null +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/ComplexType.php @@ -0,0 +1,14 @@ + + */ + abstract public function getRawArgs(): array; + + /** + * Returns whether this call expression is actually a first class callable. + */ + public function isFirstClassCallable(): bool { + foreach ($this->getRawArgs() as $arg) { + if ($arg instanceof VariadicPlaceholder) { + return true; + } + } + return false; + } + + /** + * Assert that this is not a first-class callable and return only ordinary Args. + * + * @return Arg[] + */ + public function getArgs(): array { + assert(!$this->isFirstClassCallable()); + return $this->getRawArgs(); + } +} \ No newline at end of file diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php index 56e621f25..56ddea6aa 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php @@ -16,7 +16,7 @@ class Closure extends Expr implements FunctionLike public $params; /** @var ClosureUse[] use()s */ public $uses; - /** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */ + /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */ public $returnType; /** @var Node\Stmt[] Statements */ public $stmts; diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php index 1e8afa559..2de4d0dd5 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php @@ -5,19 +5,19 @@ use PhpParser\Node; use PhpParser\Node\Expr; -class FuncCall extends Expr +class FuncCall extends CallLike { /** @var Node\Name|Expr Function name */ public $name; - /** @var Node\Arg[] Arguments */ + /** @var array Arguments */ public $args; /** * Constructs a function call node. * - * @param Node\Name|Expr $name Function name - * @param Node\Arg[] $args Arguments - * @param array $attributes Additional attributes + * @param Node\Name|Expr $name Function name + * @param array $args Arguments + * @param array $attributes Additional attributes */ public function __construct($name, array $args = [], array $attributes = []) { $this->attributes = $attributes; @@ -32,4 +32,8 @@ public function getSubNodeNames() : array { public function getType() : string { return 'Expr_FuncCall'; } + + public function getRawArgs(): array { + return $this->args; + } } diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php index bd81bb43f..49ca48356 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php @@ -5,23 +5,24 @@ use PhpParser\Node\Arg; use PhpParser\Node\Expr; use PhpParser\Node\Identifier; +use PhpParser\Node\VariadicPlaceholder; -class MethodCall extends Expr +class MethodCall extends CallLike { /** @var Expr Variable holding object */ public $var; /** @var Identifier|Expr Method name */ public $name; - /** @var Arg[] Arguments */ + /** @var array Arguments */ public $args; /** * Constructs a function call node. * - * @param Expr $var Variable holding object - * @param string|Identifier|Expr $name Method name - * @param Arg[] $args Arguments - * @param array $attributes Additional attributes + * @param Expr $var Variable holding object + * @param string|Identifier|Expr $name Method name + * @param array $args Arguments + * @param array $attributes Additional attributes */ public function __construct(Expr $var, $name, array $args = [], array $attributes = []) { $this->attributes = $attributes; @@ -37,4 +38,8 @@ public function getSubNodeNames() : array { public function getType() : string { return 'Expr_MethodCall'; } + + public function getRawArgs(): array { + return $this->args; + } } diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/New_.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/New_.php index c86f0c601..e2bb64928 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/New_.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/New_.php @@ -3,20 +3,22 @@ namespace PhpParser\Node\Expr; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr; +use PhpParser\Node\VariadicPlaceholder; -class New_ extends Expr +class New_ extends CallLike { /** @var Node\Name|Expr|Node\Stmt\Class_ Class name */ public $class; - /** @var Node\Arg[] Arguments */ + /** @var array Arguments */ public $args; /** * Constructs a function call node. * * @param Node\Name|Expr|Node\Stmt\Class_ $class Class name (or class node for anonymous classes) - * @param Node\Arg[] $args Arguments + * @param array $args Arguments * @param array $attributes Additional attributes */ public function __construct($class, array $args = [], array $attributes = []) { @@ -32,4 +34,8 @@ public function getSubNodeNames() : array { public function getType() : string { return 'Expr_New'; } + + public function getRawArgs(): array { + return $this->args; + } } diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php index 9883f5af5..d0d099c47 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php @@ -3,25 +3,27 @@ namespace PhpParser\Node\Expr; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr; use PhpParser\Node\Identifier; +use PhpParser\Node\VariadicPlaceholder; -class StaticCall extends Expr +class StaticCall extends CallLike { /** @var Node\Name|Expr Class name */ public $class; /** @var Identifier|Expr Method name */ public $name; - /** @var Node\Arg[] Arguments */ + /** @var array Arguments */ public $args; /** * Constructs a static method call node. * - * @param Node\Name|Expr $class Class name - * @param string|Identifier|Expr $name Method name - * @param Node\Arg[] $args Arguments - * @param array $attributes Additional attributes + * @param Node\Name|Expr $class Class name + * @param string|Identifier|Expr $name Method name + * @param array $args Arguments + * @param array $attributes Additional attributes */ public function __construct($class, $name, array $args = [], array $attributes = []) { $this->attributes = $attributes; @@ -37,4 +39,8 @@ public function getSubNodeNames() : array { public function getType() : string { return 'Expr_StaticCall'; } + + public function getRawArgs(): array { + return $this->args; + } } diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/FunctionLike.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/FunctionLike.php index bbcf53e55..5a825e731 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Node/FunctionLike.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/FunctionLike.php @@ -23,7 +23,7 @@ public function getParams() : array; /** * Get the declared return type or null * - * @return null|Identifier|Name|NullableType|UnionType + * @return null|Identifier|Name|ComplexType */ public function getReturnType(); diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/IntersectionType.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/IntersectionType.php new file mode 100644 index 000000000..9208e1392 --- /dev/null +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/IntersectionType.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->types = $types; + } + + public function getSubNodeNames() : array { + return ['types']; + } + + public function getType() : string { + return 'IntersectionType'; + } +} diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/NullableType.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/NullableType.php index 36463657e..d68e26a38 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Node/NullableType.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/NullableType.php @@ -2,9 +2,7 @@ namespace PhpParser\Node; -use PhpParser\NodeAbstract; - -class NullableType extends NodeAbstract +class NullableType extends ComplexType { /** @var Identifier|Name Type */ public $type; diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Param.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Param.php index 315b5f24f..1e90b7944 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Param.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Param.php @@ -6,7 +6,7 @@ class Param extends NodeAbstract { - /** @var null|Identifier|Name|NullableType|UnionType Type declaration */ + /** @var null|Identifier|Name|ComplexType Type declaration */ public $type; /** @var bool Whether parameter is passed by reference */ public $byRef; @@ -24,14 +24,14 @@ class Param extends NodeAbstract /** * Constructs a parameter node. * - * @param Expr\Variable|Expr\Error $var Parameter variable - * @param null|Expr $default Default value - * @param null|string|Identifier|Name|NullableType|UnionType $type Type declaration - * @param bool $byRef Whether is passed by reference - * @param bool $variadic Whether this is a variadic argument - * @param array $attributes Additional attributes - * @param int $flags Optional visibility flags - * @param AttributeGroup[] $attrGroups PHP attribute groups + * @param Expr\Variable|Expr\Error $var Parameter variable + * @param null|Expr $default Default value + * @param null|string|Identifier|Name|ComplexType $type Type declaration + * @param bool $byRef Whether is passed by reference + * @param bool $variadic Whether this is a variadic argument + * @param array $attributes Additional attributes + * @param int $flags Optional visibility flags + * @param AttributeGroup[] $attrGroups PHP attribute groups */ public function __construct( $var, Expr $default = null, $type = null, diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/LNumber.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/LNumber.php index b33943547..f17dd1f8a 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/LNumber.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/LNumber.php @@ -62,6 +62,11 @@ public static function fromString(string $str, array $attributes = [], bool $all throw new Error('Invalid numeric literal', $attributes); } + // Strip optional explicit octal prefix. + if ('o' === $str[1] || 'O' === $str[1]) { + $str = substr($str, 2); + } + // use intval instead of octdec to get proper cutting behavior with malformed numbers $attributes['kind'] = LNumber::KIND_OCT; return new LNumber(intval($str, 8), $attributes); diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php index 92157fab2..09b877a92 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php @@ -15,7 +15,7 @@ class ClassMethod extends Node\Stmt implements FunctionLike public $name; /** @var Node\Param[] Parameters */ public $params; - /** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */ + /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */ public $returnType; /** @var Node\Stmt[]|null Statements */ public $stmts; diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.php index f08481fae..3fa24f493 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.php @@ -16,7 +16,7 @@ class Function_ extends Node\Stmt implements FunctionLike public $name; /** @var Node\Param[] Parameters */ public $params; - /** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */ + /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */ public $returnType; /** @var Node\Stmt[] Statements */ public $stmts; diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.php index 4f04805d2..bc781bbff 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.php @@ -3,10 +3,9 @@ namespace PhpParser\Node\Stmt; use PhpParser\Node; +use PhpParser\Node\ComplexType; use PhpParser\Node\Identifier; use PhpParser\Node\Name; -use PhpParser\Node\NullableType; -use PhpParser\Node\UnionType; class Property extends Node\Stmt { @@ -14,7 +13,7 @@ class Property extends Node\Stmt public $flags; /** @var PropertyProperty[] Properties */ public $props; - /** @var null|Identifier|Name|NullableType|UnionType Type declaration */ + /** @var null|Identifier|Name|ComplexType Type declaration */ public $type; /** @var Node\AttributeGroup[] PHP attribute groups */ public $attrGroups; @@ -22,11 +21,11 @@ class Property extends Node\Stmt /** * Constructs a class property list node. * - * @param int $flags Modifiers - * @param PropertyProperty[] $props Properties - * @param array $attributes Additional attributes - * @param null|string|Identifier|Name|NullableType|UnionType $type Type declaration - * @param Node\AttributeGroup[] $attrGroups PHP attribute groups + * @param int $flags Modifiers + * @param PropertyProperty[] $props Properties + * @param array $attributes Additional attributes + * @param null|string|Identifier|Name|ComplexType $type Type declaration + * @param Node\AttributeGroup[] $attrGroups PHP attribute groups */ public function __construct(int $flags, array $props, array $attributes = [], $type = null, array $attrGroups = []) { $this->attributes = $attributes; diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/UnionType.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/UnionType.php index c8f45235d..61c2d8106 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Node/UnionType.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/UnionType.php @@ -2,9 +2,7 @@ namespace PhpParser\Node; -use PhpParser\NodeAbstract; - -class UnionType extends NodeAbstract +class UnionType extends ComplexType { /** @var (Identifier|Name)[] Types */ public $types; diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Node/VariadicPlaceholder.php b/app/vendor/nikic/php-parser/lib/PhpParser/Node/VariadicPlaceholder.php new file mode 100644 index 000000000..403a24df2 --- /dev/null +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Node/VariadicPlaceholder.php @@ -0,0 +1,27 @@ +attributes = $attributes; + } + + public function getType(): string { + return 'VariadicPlaceholder'; + } + + public function getSubNodeNames(): array { + return []; + } +} \ No newline at end of file diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php b/app/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php index 79bbc4577..8e259c57b 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php @@ -189,7 +189,7 @@ private function resolveType($node) { $node->type = $this->resolveType($node->type); return $node; } - if ($node instanceof Node\UnionType) { + if ($node instanceof Node\UnionType || $node instanceof Node\IntersectionType) { foreach ($node->types as &$type) { $type = $this->resolveType($type); } diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php b/app/vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php index 75fc06db7..32a3fef4f 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php @@ -18,16 +18,16 @@ class Php7 extends \PhpParser\ParserAbstract { protected $tokenToSymbolMapSize = 396; - protected $actionTableSize = 1187; - protected $gotoTableSize = 579; + protected $actionTableSize = 1196; + protected $gotoTableSize = 585; protected $invalidSymbol = 168; protected $errorSymbol = 1; protected $defaultAction = -32766; protected $unexpectedTokenRule = 32767; - protected $YY2TBLSTATE = 415; - protected $numNonLeafStates = 702; + protected $YY2TBLSTATE = 419; + protected $numNonLeafStates = 710; protected $symbolToName = array( "EOF", @@ -244,253 +244,255 @@ class Php7 extends \PhpParser\ParserAbstract ); protected $action = array( - 131, 132, 133, 563, 134, 135, 0, 714, 715, 716, - 136, 36, 977, 976, 975, 978,-32766,-32766,-32766,-32767, - -32767,-32767,-32767, 100, 101, 102, 103, 104, 1051, 1052, - 1053, 1050, 1049, 1048, 1054, 708, 707,-32766,-32766,-32766, + 131, 132, 133, 569, 134, 135, 0, 722, 723, 724, + 136, 36, 834, 911, 835, 468,-32766,-32766,-32766,-32767, + -32767,-32767,-32767, 100, 101, 102, 103, 104, 1068, 1069, + 1070, 1067, 1066, 1065, 1071, 716, 715,-32766,-32766,-32766, -32766,-32766,-32766,-32766,-32766,-32766,-32767,-32767,-32767,-32767, - -32767, 539, 540, 903, 2, 717,-32766,-32766,-32766, 988, - 989, -88, 914, 440, 441, 442, 365, 366, 462, 265, - 137, 391, 721, 722, 723, 724, 409,-32766, 415,-32766, - -32766,-32766,-32766,-32766, -305, 725, 726, 727, 728, 729, - 730, 731, 732, 733, 734, 735, 755, 564, 756, 757, - 758, 759, 747, 748, 331, 332, 750, 751, 736, 737, - 738, 740, 741, 742, 341, 782, 783, 784, 785, 786, - 787, 743, 744, 565, 566, 776, 767, 765, 766, 779, - 762, 763, 981, 415, 567, 568, 761, 569, 570, 571, - 572, 573, 574, -193, -566, 535, 485, 790, 764, 575, - 576, -566, 138,-32766,-32766,-32766, 131, 132, 133, 563, - 134, 135, 1002, 714, 715, 716, 136, 36, 1043,-32766, - -32766,-32766, 799, -86,-32766, 1276,-32766,-32766,-32766,-32766, - -32766,-32766,-32766, 1051, 1052, 1053, 1050, 1049, 1048, 1054, - -32766, 708, 707,-32766,-32766,-32766, 1241, 238, 463,-32766, - -32766,-32766,-32766,-32766,-32766, 883, 1213, 125, 1176, 1175, - 1177, 717, 801, 689,-32766, 1029,-32766,-32766,-32766,-32766, - -32766, -192,-32766,-32766,-32766, 265, 137, 391, 721, 722, - 723, 724, 883, 945, 415, 680, 12, 34, 247, -86, - -305, 725, 726, 727, 728, 729, 730, 731, 732, 733, - 734, 735, 755, 564, 756, 757, 758, 759, 747, 748, - 331, 332, 750, 751, 736, 737, 738, 740, 741, 742, - 341, 782, 783, 784, 785, 786, 787, 743, 744, 565, - 566, 776, 767, 765, 766, 779, 762, 763, 873, 585, - 567, 568, 761, 569, 570, 571, 572, 573, 574, -193, - 81, 82, 83, -566, 764, 575, 576, -566, 138, 739, - 709, 710, 711, 712, 713, 873, 714, 715, 716, 752, - 753, 35, 33, 84, 85, 86, 87, 88, 89, 90, + -32767, 545, 546,-32766,-32766, 725,-32766,-32766,-32766, 998, + 999, 806, 922, 446, 447, 448, 369, 370, 2, 266, + 137, 395, 729, 730, 731, 732, 413,-32766, 419,-32766, + -32766,-32766,-32766,-32766, 990, 733, 734, 735, 736, 737, + 738, 739, 740, 741, 742, 743, 763, 570, 764, 765, + 766, 767, 755, 756, 335, 336, 758, 759, 744, 745, + 746, 748, 749, 750, 345, 790, 791, 792, 793, 794, + 795, 751, 752, 571, 572, 784, 775, 773, 774, 787, + 770, 771, 282, 419, 573, 574, 769, 575, 576, 577, + 578, 579, 580, 598, -575, 469, 491, 798, 772, 581, + 582, -575, 138,-32766,-32766,-32766, 131, 132, 133, 569, + 134, 135, 1017, 722, 723, 724, 136, 36, 1060,-32766, + -32766,-32766, 1303, 696,-32766, 1304,-32766,-32766,-32766,-32766, + -32766,-32766,-32766, 1068, 1069, 1070, 1067, 1066, 1065, 1071, + -32766, 716, 715, 371, 370, 1258,-32766,-32766,-32766, -572, + 105, 106, 107, 413, 269, 891, -572, 239, 1193, 1192, + 1194, 725,-32766,-32766,-32766, 1046, 108,-32766,-32766,-32766, + -32766, 986, 985, 984, 987, 266, 137, 395, 729, 730, + 731, 732, 12,-32766, 419,-32766,-32766,-32766,-32766, 998, + 999, 733, 734, 735, 736, 737, 738, 739, 740, 741, + 742, 743, 763, 570, 764, 765, 766, 767, 755, 756, + 335, 336, 758, 759, 744, 745, 746, 748, 749, 750, + 345, 790, 791, 792, 793, 794, 795, 751, 752, 571, + 572, 784, 775, 773, 774, 787, 770, 771, 881, 320, + 573, 574, 769, 575, 576, 577, 578, 579, 580,-32766, + 81, 82, 83, -575, 772, 581, 582, -575, 147, 747, + 717, 718, 719, 720, 721, 1278, 722, 723, 724, 760, + 761, 35, 1277, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, - 101, 102, 103, 104, 105, 106, 107, -264, 267,-32766, - -32766,-32766,-32766, 105, 106, 107, 80, 267, 127, 1001, - 108, 946, 314, 885, 717, 675, 367, 366, 143, 108, - 800,-32766, 1027,-32766,-32766, 148, 409, -192, 718, 719, - 720, 721, 722, 723, 724, 237, 1181, 788, 276, -517, - 885, 315, 675, 149, 725, 726, 727, 728, 729, 730, - 731, 732, 733, 734, 735, 755, 778, 756, 757, 758, - 759, 747, 748, 749, 777, 750, 751, 736, 737, 738, - 740, 741, 742, 781, 782, 783, 784, 785, 786, 787, - 743, 744, 745, 746, 776, 767, 765, 766, 779, 762, - 763,-32766,-32766, 754, 760, 761, 768, 769, 771, 770, - 772, 773, 251, -517, -517, 448, 449, 764, 775, 774, - 48, 49, 50, 494, 51, 52, 795, 799, -517, 591, - 53, 54, -111, 55, 986, 708, 707, -111, 792, -111, - -517, 298, -523, 986, 294, 631, 24, -111, -111, -111, - -111, -111, -111, -111, -111, 988, 989, 300, 1286, 1261, - -343, 1287, -343, 1174, 988, 989, 1260, 312, 56, 57, - -32766,-32766,-32766, -111, 58, 1201, 59, 244, 245, 60, - 61, 62, 63, 64, 65, 66, 67, -516, 26, 266, - 68, 429, 495, -319, 647, 648, 1207, 1208, 496, 1172, - 799, 1181, 796, 287, 1205, 40, 23, 497, 73, 498, - 328, 499, 314, 500, 794, 329, 501, 502, 826, 677, - 827, 42, 43, 430, 362, 361, 883, 44, 503, 147, - 394, -16, -557, 353, 327, 355, -557, 1181, 1176, 1175, - 1177, -518, 504, 505, 506, 359, -515, 1257, 47, 363, - 364, -516, -516, 374, 507, 508, 799, 1195, 1196, 1197, - 1198, 1192, 1193, 286, -563, 425, -516, 798, 151, 1199, - 1194, -563, 426, 1176, 1175, 1177, 287, 883, -516, 427, - -522, 69, 799, 310, 311, 314, 30, 109, 110, 111, + 101, 102, 103, 104, 105, 106, 107, 996, 269, 149, + -32766,-32766,-32766, 454, 455, 80, 33, -264, -572, 1016, + 108, 319, -572, 893, 725, 682, 803, 127, 998, 999, + 592,-32766, 1044,-32766,-32766,-32766, 809, 150, 726, 727, + 728, 729, 730, 731, 732, -88, 1198, 796, 277, -526, + 282,-32766,-32766,-32766, 733, 734, 735, 736, 737, 738, + 739, 740, 741, 742, 743, 763, 786, 764, 765, 766, + 767, 755, 756, 757, 785, 758, 759, 744, 745, 746, + 748, 749, 750, 789, 790, 791, 792, 793, 794, 795, + 751, 752, 753, 754, 784, 775, 773, 774, 787, 770, + 771, 143, 804, 762, 768, 769, 776, 777, 779, 778, + 780, 781, -314, -526, -526, -193, -192, 772, 783, 782, + 48, 49, 50, 500, 51, 52, 238, 807, -526, -86, + 53, 54, -111, 55, 996, 252,-32766, -111, 800, -111, + -526, 541, -532, -352, 299, -352, 303, -111, -111, -111, + -111, -111, -111, -111, -111, 998, 999, 998, 999, 152, + -32766,-32766,-32766, 1191, 807, 125, 305, 1293, 56, 57, + 102, 103, 104, -111, 58, 1218, 59, 245, 246, 60, + 61, 62, 63, 64, 65, 66, 67, -525, 26, 267, + 68, 435, 501, -328, 808, -86, 1224, 1225, 502, 1189, + 807, 1198, 1230, 292, 1222, 40, 23, 503, 73, 504, + 953, 505, 319, 506, 802, 153, 507, 508, 278, 684, + 279, 42, 43, 436, 366, 365, 891, 44, 509, 34, + 248, -16, -566, 357, 331, 317, -566, 1198, 1193, 1192, + 1194, -527, 510, 511, 512, 332, -524, 1274, 47, 716, + 715, -525, -525, 333, 513, 514, 807, 1212, 1213, 1214, + 1215, 1209, 1210, 291, 359, 283, -525, 284, -314, 1216, + 1211, -193, -192, 1193, 1192, 1194, 292, 891, -525, 363, + -531, 69, 807, 315, 316, 319, 30, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, - -153, -153, -153, 368, 369, -518, -518, 681, 428, 873, - -515, -515, 290, 291, 883, -153, 805, -153, 799, -153, - -518, -153, 708, 707, 152, -515, 790, 358, -111, 1088, - 1090, 360, -518, 153, 883, 139, 376, -515, 11, 126, - -515, 314, -111, -111, 682, 155, 279, -520, 102, 103, - 104, 31, 859, -111, -111, -111, -111, 46, 287,-32766, - 873, 623, 624, 73, 684, 1174, 826, 314, 827, 1028, - -79, 700,-32766,-32766,-32766, 122,-32766, 123,-32766, 128, - -32766, 708, 707,-32766, 885, 883, 675, -153,-32766,-32766, - -32766, 708, 707, 129,-32766,-32766, 142, 873, 156, 73, - -32766, 406, 157, 314, -515, -515, 158, 140, 159,-32766, - -75, -520, -520, 314, 26, 691, -73, 873, -72, -515, - -71, 288, 289, -563, -70, -69, 799, -563,-32766, -68, - 1205, -515,-32766, -67, 1174, 885, -66, 675, -520, 72, - -47,-32766,-32766,-32766, -18,-32766, 146,-32766, 124,-32766, - 268, 275,-32766, 988, 989, 690, -51,-32766,-32766,-32766, - 693, 882, 145,-32766,-32766, 899, 108, 277, 873,-32766, - 406, 278, 931, 280, 675, 281, 321, 144,-32766, 267, - 507, 508, 799, 1195, 1196, 1197, 1198, 1192, 1193, 655, - 130, 790, 885, 1288, 675, 1199, 1194, 543, 1058,-32766, - 650, 13,-32766, 537, 632, 1174, 424, 71, 621, 915, - 311, 314,-32766,-32766,-32766, 668,-32766, 637,-32766,-32766, - -32766, 293, 1212,-32766, 916, 445, 638, 549,-32766,-32766, - -32766, 473, -481,-32766,-32766,-32766, -4, 883, -551, 1174, - -32766, 406, 651, 885, 589, 675,-32766,-32766,-32766,-32766, - -32766, 295,-32766, 901,-32766, 0, 798,-32766, 0, 0, - 0, 0,-32766,-32766,-32766,-32766, 0, 292,-32766,-32766, - 0, 1174, 0, 0,-32766, 406, 299, 0,-32766,-32766, - -32766, 0,-32766,-32766,-32766, 1214,-32766, 0, 0,-32766, - 0, 287, -471, 468,-32766,-32766,-32766,-32766, 7, 15, - -32766,-32766, 357, 1174, 555, 38,-32766, 406, 1202, 883, - -32766,-32766,-32766, 39,-32766,-32766,-32766, 697,-32766, 698, - 873,-32766, 864, 955, 932, 939,-32766,-32766,-32766, 929, - 940, 862,-32766,-32766, 927, 1032, 1035, 1036,-32766, 406, - 1033, 1034, 360, 1040, 420, 883, 810,-32766, 1227, 285, - 1245, 694, 1279, -111, -111, 626, 860, 32, 309, 356, - 676, 679, 683, 818, -111, -111, -111, -111, 685, 686, - -32766, 687, 688, 692, 678, 1206, 1174, 1283, 1285, 821, - 820, 829, 908,-32766,-32766,-32766, 9,-32766, 947,-32766, - 828,-32766, 873, 1284,-32766, 885, 907, 675, -4,-32766, - -32766,-32766, 909, 906, 1160,-32766,-32766, 892, -242, -242, - -242,-32766, 406, 902, 360, 26, 890, 937, 938, 1282, - -32766, 1239, 1228, 1246, 1252, -111, -111, 799, 873, 1255, - -267, 1205, -549, -523, -522, 859, -111, -111, -111, -111, - -521, 1, 27, 28, -241, -241, -241, 37, 41, 45, - 360, 70, 74, 75, 76, 77, 78, 79, 141, 0, - 150, -111, -111, 154, 243, 316, 342, 885, 343, 675, - -242, 859, -111, -111, -111, -111, 344, 345, 346, 347, - 348, 349, 508, 350, 1195, 1196, 1197, 1198, 1192, 1193, - 351, 352, 354, 421, 0, -265, 1199, 1194, -264, 17, - 18, 19, 20, 885, 22, 675, -241, 393, 71, 314, - 464, 311, 314, 465, 472, 475, 476, 477, 478, 482, - 483, 484, 492, 662, 1185, 1128, 1203, 1003, 1164, -269, - -103, 16, 21, 25, 284, 392, 582, 586, 613, 667, - 1132, 1180, 1129, 1258, 0, -485, 1145 + -153, -153, -153, 638, 24, -527, -527, 687, 378, 881, + -524, -524, 295, 296, 891, -153, 431, -153, 807, -153, + -527, -153, 716, 715, 432, -524, 798, 362, -111, 1105, + 1107, 364, -527, 433, 891, 139, 434, -524, 954, 126, + -524, 319, -111, -111, 688, 813, 380, -529, 11, 834, + 154, 835, 867, -111, -111, -111, -111, 46, 292,-32766, + 881, 654, 655, 73, 689, 1191, 1045, 319, 708, 148, + 398, 156,-32766,-32766,-32766, 31,-32766, -79,-32766, 122, + -32766, 716, 715,-32766, 893, 891, 682, -153,-32766,-32766, + -32766, 716, 715, 891,-32766,-32766, 123, 881, 128, 73, + -32766, 410, 129, 319, -524, -524, 142, 140, -75,-32766, + 157, -529, -529, 319, 26, 691, 158, 881, 159, -524, + 160, 293, 294, 698, 367, 368, 807, -73,-32766, -72, + 1222, -524, 372, 373, 1191, 893, -71, 682, -529, 72, + -70,-32766,-32766,-32766, -69,-32766, -68,-32766, 124,-32766, + 630, 631,-32766, -67, -66, -47, -51,-32766,-32766,-32766, + -18, 146, 270,-32766,-32766, 276, 697, 700, 881,-32766, + 410, 890, 893, 145, 682, 281, 881, 907,-32766, 280, + 513, 514, 285, 1212, 1213, 1214, 1215, 1209, 1210, 325, + 130, 144, 939, 286, 682, 1216, 1211, 108, 269,-32766, + 798, 807,-32766, 662, 639, 1191, 657, 71, 675, 1075, + 316, 319,-32766,-32766,-32766, 1305,-32766, 300,-32766, 628, + -32766, 430, 543,-32766,-32766, 923, 555, 924,-32766,-32766, + -32766, 1229, 549,-32766,-32766,-32766, -4, 891, -490, 1191, + -32766, 410, 644, 893, 298, 682,-32766,-32766,-32766,-32766, + -32766, 893,-32766, 682,-32766, 13, 1231,-32766, 451, 479, + 645, 909,-32766,-32766,-32766,-32766, 658, -480,-32766,-32766, + 0, 1191, 0, 0,-32766, 410, 0, 297,-32766,-32766, + -32766, 304,-32766,-32766,-32766, 0,-32766, 0, 806,-32766, + 0, 0, 0, 474,-32766,-32766,-32766,-32766, 0, 7, + -32766,-32766, 15, 1191, 561, 596,-32766, 410, 1219, 891, + -32766,-32766,-32766, 361,-32766,-32766,-32766, 818,-32766, -267, + 881,-32766, 38, 292, 0, 0,-32766,-32766,-32766, 39, + 705, 706,-32766,-32766, 872, 963, 940, 947,-32766, 410, + 937, 948, 364, 870, 426, 891, 935,-32766, 1049, 290, + 1244, 1052, 1053, -111, -111, 1050, 1051, 1057, -560, 1262, + 1296, 633, 0, 826, -111, -111, -111, -111, 32, 314, + -32766, 360, 683, 686, 690, 692, 1191, 693, 694, 695, + 699, 685, 319,-32766,-32766,-32766, 9,-32766, 702,-32766, + 868,-32766, 881, 1300,-32766, 893, 1302, 682, -4,-32766, + -32766,-32766, 829, 828, 837,-32766,-32766, 916, -242, -242, + -242,-32766, 410, 955, 364, 26, 836, 1301, 915, 917, + -32766, 914, 1177, 900, 910, -111, -111, 807, 881, 898, + 945, 1222, 946, 1299, 1256, 867, -111, -111, -111, -111, + 1245, 1263, 1269, 1272, -241, -241, -241, -558, -532, -531, + 364, -530, 1, 27, 28, 37, 41, 45, 70, 0, + 74, -111, -111, 75, 76, 77, 78, 893, 79, 682, + -242, 867, -111, -111, -111, -111, 141, 151, 155, 244, + 321, 346, 514, 347, 1212, 1213, 1214, 1215, 1209, 1210, + 348, 349, 350, 351, 352, 353, 1216, 1211, 354, 355, + 356, 358, 427, 893, -265, 682, -241, -264, 71, 0, + 17, 316, 319, 18, 19, 20, 22, 397, 470, 471, + 478, 481, 482, 483, 484, 488, 489, 490, 498, 669, + 1202, 1145, 1220, 1019, 1018, 1181, -269, -103, 16, 21, + 25, 289, 396, 589, 593, 620, 674, 1149, 1197, 1146, + 1275, 0, -494, 1162, 0, 1223 ); protected $actionCheck = array( 2, 3, 4, 5, 6, 7, 0, 9, 10, 11, - 12, 13, 119, 120, 121, 122, 9, 10, 11, 44, + 12, 13, 106, 1, 108, 31, 9, 10, 11, 44, 45, 46, 47, 48, 49, 50, 51, 52, 116, 117, 118, 119, 120, 121, 122, 37, 38, 30, 116, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, - 43, 117, 118, 1, 8, 57, 9, 10, 11, 137, - 138, 31, 128, 129, 130, 131, 106, 107, 31, 71, + 43, 117, 118, 9, 10, 57, 9, 10, 11, 137, + 138, 155, 128, 129, 130, 131, 106, 107, 8, 71, 72, 73, 74, 75, 76, 77, 116, 30, 80, 32, - 33, 34, 35, 36, 8, 87, 88, 89, 90, 91, + 33, 34, 35, 36, 1, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, - 132, 133, 1, 80, 136, 137, 138, 139, 140, 141, - 142, 143, 144, 8, 1, 85, 101, 80, 150, 151, + 132, 133, 30, 80, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 51, 1, 161, 101, 80, 150, 151, 152, 8, 154, 9, 10, 11, 2, 3, 4, 5, 6, 7, 164, 9, 10, 11, 12, 13, 123, 9, - 10, 11, 82, 31, 30, 85, 32, 33, 34, 35, + 10, 11, 80, 161, 30, 83, 32, 33, 34, 35, 36, 37, 38, 116, 117, 118, 119, 120, 121, 122, - 30, 37, 38, 9, 10, 11, 1, 14, 161, 9, - 10, 11, 9, 10, 11, 1, 146, 14, 155, 156, - 157, 57, 1, 161, 30, 162, 32, 33, 34, 35, - 30, 8, 32, 33, 34, 71, 72, 73, 74, 75, - 76, 77, 1, 31, 80, 31, 8, 147, 148, 97, - 164, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 30, 37, 38, 106, 107, 1, 9, 10, 11, 1, + 53, 54, 55, 116, 57, 1, 8, 14, 155, 156, + 157, 57, 9, 10, 11, 162, 69, 30, 116, 32, + 33, 119, 120, 121, 122, 71, 72, 73, 74, 75, + 76, 77, 8, 30, 80, 32, 33, 34, 35, 137, + 138, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, - 126, 127, 128, 129, 130, 131, 132, 133, 84, 1, - 136, 137, 138, 139, 140, 141, 142, 143, 144, 164, + 126, 127, 128, 129, 130, 131, 132, 133, 84, 70, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 9, 9, 10, 11, 160, 150, 151, 152, 164, 154, 2, - 3, 4, 5, 6, 7, 84, 9, 10, 11, 12, + 3, 4, 5, 6, 7, 1, 9, 10, 11, 12, 13, 30, 8, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 164, 57, 9, - 9, 10, 11, 53, 54, 55, 161, 57, 8, 1, - 69, 159, 167, 159, 57, 161, 106, 107, 8, 69, - 159, 30, 1, 32, 33, 14, 116, 164, 71, 72, - 73, 74, 75, 76, 77, 97, 1, 80, 30, 70, - 159, 70, 161, 14, 87, 88, 89, 90, 91, 92, + 49, 50, 51, 52, 53, 54, 55, 116, 57, 14, + 9, 10, 11, 134, 135, 161, 8, 164, 160, 1, + 69, 167, 164, 159, 57, 161, 80, 8, 137, 138, + 1, 30, 1, 32, 33, 34, 1, 14, 71, 72, + 73, 74, 75, 76, 77, 31, 1, 80, 30, 70, + 30, 9, 10, 11, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, - 133, 9, 10, 136, 137, 138, 139, 140, 141, 142, - 143, 144, 8, 134, 135, 134, 135, 150, 151, 152, - 2, 3, 4, 5, 6, 7, 80, 82, 149, 51, - 12, 13, 101, 15, 116, 37, 38, 106, 80, 108, - 161, 8, 163, 116, 113, 75, 76, 116, 117, 118, - 119, 120, 121, 122, 123, 137, 138, 8, 80, 1, - 106, 83, 108, 80, 137, 138, 8, 8, 50, 51, - 9, 10, 11, 128, 56, 1, 58, 59, 60, 61, + 133, 8, 156, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 8, 134, 135, 8, 8, 150, 151, 152, + 2, 3, 4, 5, 6, 7, 97, 82, 149, 31, + 12, 13, 101, 15, 116, 8, 116, 106, 80, 108, + 161, 85, 163, 106, 113, 108, 8, 116, 117, 118, + 119, 120, 121, 122, 123, 137, 138, 137, 138, 14, + 9, 10, 11, 80, 82, 14, 8, 85, 50, 51, + 50, 51, 52, 128, 56, 1, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 70, 70, 71, - 72, 73, 74, 162, 75, 76, 78, 79, 80, 116, - 82, 1, 156, 158, 86, 87, 88, 89, 163, 91, - 8, 93, 167, 95, 156, 8, 98, 99, 106, 161, - 108, 103, 104, 105, 106, 107, 1, 109, 110, 101, - 102, 31, 160, 115, 116, 8, 164, 1, 155, 156, - 157, 70, 124, 125, 126, 8, 70, 1, 70, 106, - 107, 134, 135, 8, 136, 137, 82, 139, 140, 141, - 142, 143, 144, 145, 1, 8, 149, 155, 14, 151, - 152, 8, 8, 155, 156, 157, 158, 1, 161, 8, + 72, 73, 74, 162, 159, 97, 78, 79, 80, 116, + 82, 1, 146, 158, 86, 87, 88, 89, 163, 91, + 31, 93, 167, 95, 156, 14, 98, 99, 35, 161, + 37, 103, 104, 105, 106, 107, 1, 109, 110, 147, + 148, 31, 160, 115, 116, 8, 164, 1, 155, 156, + 157, 70, 124, 125, 126, 8, 70, 1, 70, 37, + 38, 134, 135, 8, 136, 137, 82, 139, 140, 141, + 142, 143, 144, 145, 8, 35, 149, 37, 164, 151, + 152, 164, 164, 155, 156, 157, 158, 1, 161, 8, 163, 163, 82, 165, 166, 167, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 75, 76, 77, 106, 107, 134, 135, 31, 8, 84, + 75, 76, 77, 75, 76, 134, 135, 31, 8, 84, 134, 135, 134, 135, 1, 90, 8, 92, 82, 94, - 149, 96, 37, 38, 14, 149, 80, 149, 128, 59, - 60, 106, 161, 14, 1, 161, 106, 161, 108, 161, - 70, 167, 117, 118, 31, 14, 30, 70, 50, 51, - 52, 14, 127, 128, 129, 130, 131, 70, 158, 74, - 84, 111, 112, 163, 31, 80, 106, 167, 108, 159, - 31, 161, 87, 88, 89, 16, 91, 16, 93, 16, + 149, 96, 37, 38, 8, 149, 80, 149, 128, 59, + 60, 106, 161, 8, 1, 161, 8, 161, 159, 161, + 70, 167, 117, 118, 31, 8, 106, 70, 108, 106, + 14, 108, 127, 128, 129, 130, 131, 70, 158, 74, + 84, 75, 76, 163, 31, 80, 159, 167, 161, 101, + 102, 14, 87, 88, 89, 14, 91, 31, 93, 16, 95, 37, 38, 98, 159, 1, 161, 162, 103, 104, - 105, 37, 38, 16, 109, 110, 16, 84, 16, 163, - 115, 116, 16, 167, 134, 135, 16, 161, 16, 124, - 31, 134, 135, 167, 70, 31, 31, 84, 31, 149, - 31, 134, 135, 160, 31, 31, 82, 164, 74, 31, - 86, 161, 116, 31, 80, 159, 31, 161, 161, 154, + 105, 37, 38, 1, 109, 110, 16, 84, 16, 163, + 115, 116, 16, 167, 134, 135, 16, 161, 31, 124, + 16, 134, 135, 167, 70, 31, 16, 84, 16, 149, + 16, 134, 135, 31, 106, 107, 82, 31, 74, 31, + 86, 161, 106, 107, 80, 159, 31, 161, 161, 154, 31, 87, 88, 89, 31, 91, 31, 93, 161, 95, - 31, 31, 98, 137, 138, 31, 31, 103, 104, 105, - 31, 31, 31, 109, 110, 38, 69, 35, 84, 115, - 116, 35, 159, 35, 161, 35, 35, 70, 124, 57, - 136, 137, 82, 139, 140, 141, 142, 143, 144, 77, - 31, 80, 159, 83, 161, 151, 152, 89, 82, 74, - 94, 97, 85, 85, 90, 80, 128, 163, 113, 128, - 166, 167, 87, 88, 89, 92, 91, 96, 93, 116, - 95, 133, 146, 98, 128, 97, 100, 153, 103, 104, - 105, 97, 149, 74, 109, 110, 0, 1, 163, 80, - 115, 116, 100, 159, 153, 161, 87, 88, 89, 124, - 91, 114, 93, 154, 95, -1, 155, 98, -1, -1, - -1, -1, 103, 104, 105, 74, -1, 132, 109, 110, - -1, 80, -1, -1, 115, 116, 132, -1, 87, 88, - 89, -1, 91, 124, 93, 146, 95, -1, -1, 98, - -1, 158, 149, 102, 103, 104, 105, 74, 149, 149, - 109, 110, 149, 80, 81, 159, 115, 116, 160, 1, - 87, 88, 89, 159, 91, 124, 93, 159, 95, 159, - 84, 98, 159, 159, 159, 159, 103, 104, 105, 159, + 111, 112, 98, 31, 31, 31, 31, 103, 104, 105, + 31, 31, 31, 109, 110, 31, 31, 31, 84, 115, + 116, 31, 159, 31, 161, 37, 84, 38, 124, 35, + 136, 137, 35, 139, 140, 141, 142, 143, 144, 35, + 31, 70, 159, 37, 161, 151, 152, 69, 57, 74, + 80, 82, 85, 77, 90, 80, 94, 163, 92, 82, + 166, 167, 87, 88, 89, 83, 91, 114, 93, 113, + 95, 128, 85, 98, 116, 128, 153, 128, 103, 104, + 105, 146, 89, 74, 109, 110, 0, 1, 149, 80, + 115, 116, 96, 159, 133, 161, 87, 88, 89, 124, + 91, 159, 93, 161, 95, 97, 146, 98, 97, 97, + 100, 154, 103, 104, 105, 74, 100, 149, 109, 110, + -1, 80, -1, -1, 115, 116, -1, 132, 87, 88, + 89, 132, 91, 124, 93, -1, 95, -1, 155, 98, + -1, -1, -1, 102, 103, 104, 105, 74, -1, 149, + 109, 110, 149, 80, 81, 153, 115, 116, 160, 1, + 87, 88, 89, 149, 91, 124, 93, 160, 95, 164, + 84, 98, 159, 158, -1, -1, 103, 104, 105, 159, 159, 159, 109, 110, 159, 159, 159, 159, 115, 116, - 159, 159, 106, 159, 108, 1, 160, 124, 160, 113, - 160, 162, 160, 117, 118, 160, 162, 161, 161, 161, - 161, 161, 161, 127, 128, 129, 130, 131, 161, 161, - 74, 161, 161, 161, 161, 166, 80, 162, 162, 162, - 162, 162, 162, 87, 88, 89, 150, 91, 162, 93, + 159, 159, 106, 159, 108, 1, 159, 124, 159, 113, + 160, 159, 159, 117, 118, 159, 159, 159, 163, 160, + 160, 160, -1, 127, 128, 129, 130, 131, 161, 161, + 74, 161, 161, 161, 161, 161, 80, 161, 161, 161, + 161, 161, 167, 87, 88, 89, 150, 91, 162, 93, 162, 95, 84, 162, 98, 159, 162, 161, 162, 103, 104, 105, 162, 162, 162, 109, 110, 162, 100, 101, 102, 115, 116, 162, 106, 70, 162, 162, 162, 162, 124, 162, 162, 162, 162, 117, 118, 82, 84, 162, - 164, 86, 163, 163, 163, 127, 128, 129, 130, 131, - 163, 163, 163, 163, 100, 101, 102, 163, 163, 163, + 162, 86, 162, 162, 162, 127, 128, 129, 130, 131, + 162, 162, 162, 162, 100, 101, 102, 163, 163, 163, 106, 163, 163, 163, 163, 163, 163, 163, 163, -1, 163, 117, 118, 163, 163, 163, 163, 159, 163, 161, 162, 127, 128, 129, 130, 131, 163, 163, 163, 163, 163, 163, 137, 163, 139, 140, 141, 142, 143, 144, - 163, 163, 163, 163, -1, 164, 151, 152, 164, 164, - 164, 164, 164, 159, 164, 161, 162, 164, 163, 167, + 163, 163, 163, 163, 163, 163, 151, 152, 163, 163, + 163, 163, 163, 159, 164, 161, 162, 164, 163, -1, 164, 166, 167, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, - 164, 164, 164, 164, -1, 165, 165 + 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, + 164, -1, 165, 165, -1, 166 ); protected $actionBase = array( 0, -2, 154, 565, 876, 948, 984, 514, 53, 398, - 822, 307, 307, 67, 307, 307, 616, 673, 673, 724, - 673, 204, 653, 231, 231, 231, 625, 625, 625, 625, + 837, 307, 307, 67, 307, 307, 653, 724, 724, 732, + 724, 616, 673, 204, 204, 204, 625, 625, 625, 625, 694, 694, 831, 831, 863, 799, 765, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, @@ -504,62 +506,63 @@ class Php7 extends \PhpParser\ParserAbstract 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, - 211, 202, 288, 677, 1010, 1016, 1012, 1017, 1008, 1007, - 1011, 1013, 1018, 897, 899, 771, 900, 901, 902, 907, - 1014, 835, 1009, 1015, 291, 291, 291, 291, 291, 291, + 936, 375, 519, 369, 701, 1017, 1023, 1019, 1024, 1015, + 1014, 1018, 1020, 1025, 911, 912, 782, 918, 919, 920, + 921, 1021, 841, 1016, 1022, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, - 340, 193, 432, 501, 501, 501, 501, 501, 501, 501, - 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, - 501, 501, 501, 160, 160, 160, 341, 684, 684, 190, - 184, 610, 47, 985, 985, 985, 985, 985, 985, 985, - 985, 985, 985, 144, 144, 7, 7, 7, 7, 7, - 371, -25, -25, -25, -25, 540, 385, 576, 358, 45, - 394, 638, 638, 656, 656, 367, 367, 367, 367, -78, - -78, -78, -66, 319, 457, 452, 60, 423, 586, 586, - 586, 586, 423, 423, 423, 423, 779, 849, 423, 423, - 423, 511, 516, 516, 518, 300, 300, 300, 516, 600, - 758, 90, 600, 90, 195, 418, 743, -40, 260, 412, - -107, 743, 617, 627, 603, 143, 741, 483, 741, 1006, - 757, 749, 719, 824, 853, 1019, 766, 895, 782, 896, - 321, 679, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - 1005, 1005, 1005, 982, 438, 1006, 386, 982, 982, 982, - 438, 438, 438, 438, 438, 438, 438, 438, 438, 438, - 590, 386, 410, 459, 386, 781, 438, 211, 783, 211, - 211, 211, 211, 943, 211, 211, 211, 211, 211, 211, - 956, 753, 37, 211, 202, 52, 52, 550, 131, 52, - 52, 52, 52, 211, 211, 211, 483, 762, 714, 537, - 731, 213, 762, 762, 762, 142, 76, 183, 135, 570, - 751, 751, 756, 918, 918, 751, 740, 751, 756, 926, - 751, 918, 773, 350, 597, 542, 577, 604, 918, 473, - 751, 751, 751, 751, 611, 751, 444, 360, 751, 751, - 775, 760, 784, 46, 918, 918, 918, 784, 567, 728, - 728, 728, 798, 800, 735, 759, 499, 489, 648, 314, - 767, 759, 759, 751, 585, 735, 759, 735, 759, 739, - 759, 759, 759, 735, 759, 751, 740, 547, 759, 722, - 640, 228, 759, 6, 928, 929, 30, 930, 924, 931, - 970, 932, 933, 839, 941, 925, 934, 920, 919, 770, - 699, 701, 789, 723, 917, 737, 737, 737, 910, 737, - 737, 737, 737, 737, 737, 737, 737, 699, 788, 793, - 718, 748, 945, 703, 717, 716, 834, 1020, 1021, 721, - 736, 943, 1000, 935, 786, 720, 980, 953, 829, 837, - 954, 955, 983, 1001, 1002, 855, 747, 856, 857, 826, - 957, 840, 737, 928, 933, 925, 934, 920, 919, 745, - 742, 734, 738, 733, 729, 725, 727, 755, 909, 715, - 828, 956, 911, 699, 830, 975, 836, 986, 989, 838, - 768, 750, 832, 858, 958, 960, 967, 841, 1003, 794, - 976, 906, 990, 774, 859, 991, 992, 993, 994, 860, - 847, 848, 850, 803, 754, 971, 761, 866, 361, 778, - 780, 969, 379, 942, 851, 868, 871, 995, 996, 997, - 874, 937, 804, 977, 746, 978, 974, 805, 806, 594, - 772, 776, 650, 659, 880, 881, 882, 940, 764, 752, - 810, 811, 1004, 885, 671, 812, 726, 891, 999, 730, - 732, 763, 852, 790, 777, 744, 968, 769, 815, 894, - 816, 817, 818, 998, 821, 0, 0, 0, 0, 0, + 291, 290, 491, 44, 382, 382, 382, 382, 382, 382, + 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, + 382, 382, 382, 382, 160, 160, 160, 187, 684, 684, + 341, 203, 610, 47, 985, 985, 985, 985, 985, 985, + 985, 985, 985, 985, 144, 144, 7, 7, 7, 7, + 7, 371, -25, -25, -25, -25, 540, 385, 102, 576, + 358, 45, 377, 460, 460, 360, 231, 231, 231, 231, + 231, 231, -78, -78, -78, -78, -78, -66, 319, 457, + -94, 396, 423, 586, 586, 586, 586, 423, 423, 423, + 423, 750, 1029, 423, 423, 423, 511, 516, 516, 518, + 147, 147, 147, 516, 583, 777, 422, 583, 422, 194, + 92, 748, -40, 87, 412, 748, 617, 627, 198, 143, + 773, 658, 773, 1013, 757, 764, 717, 838, 860, 1026, + 800, 908, 806, 910, 219, 686, 1012, 1012, 1012, 1012, + 1012, 1012, 1012, 1012, 1012, 1012, 1012, 855, 552, 1013, + 286, 855, 855, 855, 552, 552, 552, 552, 552, 552, + 552, 552, 552, 552, 679, 286, 568, 626, 286, 794, + 552, 375, 758, 375, 375, 375, 375, 958, 375, 375, + 375, 375, 375, 375, 970, 769, -16, 375, 519, 12, + 12, 547, 83, 12, 12, 12, 12, 375, 375, 375, + 658, 781, 713, 666, 792, 448, 781, 781, 781, 438, + 444, 193, 447, 570, 523, 580, 760, 760, 767, 929, + 929, 760, 759, 760, 767, 934, 760, 929, 805, 359, + 648, 577, 611, 656, 929, 478, 760, 760, 760, 760, + 665, 760, 467, 433, 760, 760, 785, 774, 789, 60, + 929, 929, 929, 789, 596, 751, 751, 751, 811, 812, + 746, 771, 567, 498, 677, 348, 779, 771, 771, 760, + 640, 746, 771, 746, 771, 747, 771, 771, 771, 746, + 771, 760, 759, 585, 771, 734, 668, 224, 771, 6, + 935, 937, 354, 940, 932, 941, 979, 942, 943, 851, + 956, 933, 945, 931, 930, 780, 703, 720, 790, 729, + 928, 768, 768, 768, 925, 768, 768, 768, 768, 768, + 768, 768, 768, 703, 788, 804, 733, 783, 960, 722, + 726, 725, 868, 1027, 1028, 737, 739, 958, 1006, 953, + 803, 730, 992, 967, 866, 848, 968, 969, 993, 1007, + 1008, 871, 761, 874, 880, 797, 971, 852, 768, 935, + 943, 933, 945, 931, 930, 763, 762, 753, 755, 749, + 745, 736, 738, 770, 1009, 924, 835, 830, 970, 926, + 703, 839, 986, 847, 994, 995, 850, 801, 772, 840, + 881, 972, 975, 976, 853, 1010, 810, 989, 795, 996, + 802, 882, 997, 998, 999, 1000, 885, 854, 856, 857, + 815, 754, 980, 786, 891, 335, 787, 796, 978, 363, + 957, 858, 894, 895, 1001, 1002, 1003, 896, 954, 816, + 990, 752, 991, 983, 817, 818, 485, 784, 778, 541, + 676, 897, 899, 900, 955, 775, 766, 821, 822, 1011, + 901, 697, 824, 740, 902, 1005, 742, 744, 756, 859, + 793, 743, 798, 977, 776, 827, 907, 829, 832, 833, + 1004, 836, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 458, 458, 458, 458, 458, 458, 307, - 307, 307, 307, 0, 0, 307, 0, 0, 458, 458, + 0, 458, 458, 458, 458, 458, 458, 307, 307, 307, + 307, 0, 0, 307, 0, 0, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, @@ -574,41 +577,41 @@ class Php7 extends \PhpParser\ParserAbstract 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, - 458, 458, 291, 291, 291, 291, 291, 291, 291, 291, + 458, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, - 291, 291, 291, 291, 291, 291, 0, 0, 0, 0, + 291, 291, 291, 291, 291, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 291, 291, 291, 291, 291, 291, 291, 291, + 0, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, - 291, 291, 291, 291, 291, 291, 291, 291, 291, 423, - 423, 291, 291, 0, 291, 423, 423, 423, 423, 423, - 423, 423, 423, 423, 423, 291, 291, 291, 291, 291, - 291, 291, 773, 300, 300, 300, 300, 423, 423, 423, - 423, -88, -88, 300, 300, 423, 423, 423, 423, 423, - 423, 423, 423, 423, 0, 0, 0, 386, 90, 0, - 740, 740, 740, 740, 0, 0, 0, 0, 90, 90, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 386, 90, 0, 386, 0, 740, 740, 423, 773, - 773, 498, 0, 423, 0, 0, 0, 0, 386, 740, - 386, 438, 90, 438, 438, 52, 211, 498, 468, 468, - 468, 468, 0, 483, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 740, 0, 773, 0, 740, - 740, 740, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 740, 0, 0, - 918, 0, 0, 0, 0, 751, 0, 0, 0, 0, - 0, 0, 751, 926, 0, 0, 0, 0, 0, 0, - 740, 0, 0, 0, 0, 0, 0, 0, 0, 737, - 768, 0, 768, 0, 737, 737, 737 + 291, 291, 291, 291, 291, 291, 291, 291, 423, 423, + 291, 291, 0, 291, 423, 423, 423, 423, 423, 423, + 423, 423, 423, 423, 291, 291, 291, 291, 291, 291, + 291, 805, 147, 147, 147, 147, 423, 423, 423, 423, + 423, -88, -88, 147, 147, 423, 423, 423, 423, 423, + 423, 423, 423, 423, 423, 423, 423, 0, 0, 0, + 286, 422, 0, 759, 759, 759, 759, 0, 0, 0, + 0, 422, 422, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 286, 422, 0, 286, 0, 759, + 759, 423, 805, 805, 314, 423, 0, 0, 0, 0, + 286, 759, 286, 552, 422, 552, 552, 12, 375, 314, + 608, 608, 608, 608, 0, 658, 805, 805, 805, 805, + 805, 805, 805, 805, 805, 805, 805, 759, 0, 805, + 0, 759, 759, 759, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 759, + 0, 0, 929, 0, 0, 0, 0, 760, 0, 0, + 0, 0, 0, 0, 760, 934, 0, 0, 0, 0, + 0, 0, 759, 0, 0, 0, 0, 0, 0, 0, + 0, 768, 801, 0, 801, 0, 768, 768, 768 ); protected $actionDefault = array( 3,32767, 103,32767,32767,32767,32767,32767,32767,32767, 32767,32767, 101,32767,32767,32767,32767,32767,32767,32767, - 32767,32767,32767,32767,32767,32767, 569, 569, 569, 569, - 32767,32767, 246, 103,32767,32767, 445, 363, 363, 363, - 32767,32767, 513, 513, 513, 513, 513, 513,32767,32767, - 32767,32767,32767,32767, 445,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767, 578, 578, 578, 578, + 32767,32767, 246, 103,32767,32767, 454, 372, 372, 372, + 32767,32767, 522, 522, 522, 522, 522, 522,32767,32767, + 32767,32767,32767,32767, 454,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, @@ -616,131 +619,132 @@ class Php7 extends \PhpParser\ParserAbstract 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767, 101,32767,32767, - 32767, 37, 7, 8, 10, 11, 50, 17,32767,32767, + 32767, 37, 7, 8, 10, 11, 50, 17, 310,32767, 32767,32767,32767, 103,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, - 32767,32767,32767, 562,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767, 571,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767, 458, 437, 438, 440, 441, + 371, 523, 577, 313, 574, 370, 146, 325, 315, 234, + 316, 250, 459, 251, 460, 463, 464, 211, 279, 367, + 150, 401, 455, 403, 453, 457, 402, 377, 382, 383, + 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, + 394, 375, 376, 456, 434, 433, 432, 399,32767,32767, + 400, 404, 374, 407,32767,32767,32767,32767,32767,32767, + 32767,32767, 103,32767, 405, 406, 423, 424, 421, 422, + 425,32767, 426, 427, 428, 429,32767,32767, 302,32767, + 32767, 351, 349, 414, 415, 302,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767, 516, 431, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767, 103,32767, 101, 518, 396, 398, 486, + 409, 410, 408, 378,32767, 493,32767, 103, 495,32767, + 32767,32767, 112,32767,32767,32767, 517,32767, 524, 524, + 32767, 479, 101, 194,32767, 194, 194,32767,32767,32767, + 32767,32767,32767,32767, 585, 479, 111, 111, 111, 111, + 111, 111, 111, 111, 111, 111, 111,32767, 194, 111, + 32767,32767,32767, 101, 194, 194, 194, 194, 194, 194, + 194, 194, 194, 194, 189,32767, 260, 262, 103, 539, + 194,32767, 498,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767, 491,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, - 32767,32767,32767,32767, 449, 428, 429, 431, 432, 362, - 514, 568, 304, 565, 361, 146, 316, 306, 234, 307, - 250, 450, 251, 451, 454, 455, 211, 278, 358, 150, - 392, 446, 394, 444, 448, 393, 368, 373, 374, 375, - 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, - 366, 367, 447, 425, 424, 423, 390,32767,32767, 391, - 395, 365, 398,32767,32767,32767,32767,32767,32767,32767, - 32767, 103,32767, 396, 397, 414, 415, 412, 413, 416, - 32767, 417, 418, 419, 420,32767,32767,32767,32767, 342, - 340, 405, 406, 295, 295,32767,32767,32767,32767,32767, - 32767,32767,32767, 507, 422,32767,32767,32767,32767,32767, - 32767,32767,32767,32767,32767,32767,32767,32767, 103,32767, - 101, 509, 387, 389, 477, 400, 401, 399, 369,32767, - 484,32767, 103, 486,32767,32767,32767, 112,32767,32767, - 272,32767, 508,32767, 515, 515,32767, 470, 101, 194, - 32767, 194, 194,32767,32767,32767,32767,32767,32767,32767, - 576, 470, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111,32767, 194, 111,32767,32767,32767, 101, - 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, - 189,32767, 260, 262, 103, 530, 194,32767, 489,32767, + 479, 419, 139,32767, 139, 524, 411, 412, 413, 481, + 524, 524, 524, 298, 281,32767,32767,32767,32767, 496, + 496, 101, 101, 101, 101, 491,32767,32767, 112, 100, + 100, 100, 100, 100, 104, 102,32767,32767,32767,32767, + 100,32767, 102, 102,32767,32767, 217, 208, 215, 102, + 32767, 543, 544, 215, 102, 219, 219, 219, 239, 239, + 470, 304, 102, 100, 102, 102, 196, 304, 304,32767, + 102, 470, 304, 470, 304, 198, 304, 304, 304, 470, + 304,32767,32767, 102, 304, 210, 100, 100, 304,32767, + 32767,32767, 481,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767, 511,32767, 528, + 541, 417, 418, 420, 526, 442, 443, 444, 445, 446, + 447, 448, 450, 573,32767, 485,32767,32767,32767,32767, + 324, 583,32767, 583,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, - 32767, 482,32767,32767,32767,32767,32767,32767,32767,32767, - 32767,32767,32767,32767,32767,32767, 470, 410, 139,32767, - 139, 515, 402, 403, 404, 472, 515, 515, 515,32767, - 32767,32767,32767, 487, 487, 101, 101, 101, 101, 482, - 32767,32767, 112, 100, 100, 100, 100, 100, 104, 102, - 32767,32767,32767,32767, 100,32767, 102, 102,32767,32767, - 217, 208, 215, 102,32767, 534, 535, 215, 102, 219, - 219, 219, 239, 239, 461, 297, 102, 100, 102, 102, - 196, 297, 297,32767, 102, 461, 297, 461, 297, 198, - 297, 297, 297, 461, 297,32767,32767, 102, 297, 210, - 100, 100, 297,32767,32767,32767, 472,32767,32767,32767, + 32767, 584,32767, 524,32767,32767,32767,32767, 416, 9, + 76, 43, 44, 52, 58, 502, 503, 504, 505, 499, + 500, 506, 501,32767,32767, 507, 549,32767,32767, 525, + 576,32767,32767,32767,32767,32767,32767, 139,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767, 511,32767, + 137,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767, 524,32767,32767,32767, 300, 301,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, - 32767, 502,32767, 519, 532, 408, 409, 411, 517, 433, - 434, 435, 436, 437, 438, 439, 441, 564,32767, 476, - 32767,32767,32767,32767, 315, 574,32767, 574,32767,32767, + 32767,32767,32767, 524,32767,32767,32767, 283, 284,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, - 32767,32767,32767,32767,32767, 575,32767, 515,32767,32767, - 32767,32767, 407, 9, 76, 43, 44, 52, 58, 493, - 494, 495, 496, 490, 491, 497, 492,32767, 498, 540, - 32767,32767, 516, 567,32767,32767,32767,32767,32767,32767, - 139,32767,32767,32767,32767,32767,32767,32767,32767,32767, - 32767, 502,32767, 137,32767,32767,32767,32767,32767,32767, - 32767,32767,32767,32767,32767, 515,32767,32767,32767, 292, - 294,32767,32767,32767,32767,32767,32767,32767,32767,32767, - 32767,32767,32767,32767,32767,32767, 515,32767,32767,32767, - 280, 282,32767,32767,32767,32767,32767,32767,32767,32767, - 32767,32767,32767,32767,32767,32767, 277,32767,32767, 357, - 32767,32767,32767,32767, 336,32767,32767,32767,32767,32767, - 32767,32767,32767,32767,32767, 152, 152, 3, 3, 318, - 152, 152, 152, 318, 152, 318, 318, 318, 152, 152, - 152, 152, 152, 152, 184, 254, 257, 239, 239, 152, - 328, 152 + 32767,32767,32767, 278,32767,32767, 366,32767,32767,32767, + 32767, 345,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767, 152, 152, 3, 3, 327, 152, 152, 152, + 327, 152, 327, 327, 327, 152, 152, 152, 152, 152, + 152, 272, 184, 254, 257, 239, 239, 152, 337, 152 ); protected $goto = array( - 192, 192, 663, 417, 636, 911, 983, 990, 991, 411, - 302, 303, 324, 557, 308, 416, 325, 418, 615, 1005, - 671, 317, 317, 317, 317, 163, 163, 163, 163, 216, - 193, 189, 189, 173, 175, 211, 189, 189, 189, 189, - 189, 190, 190, 190, 190, 190, 190, 184, 185, 186, - 187, 188, 213, 211, 214, 515, 516, 407, 517, 519, - 520, 521, 522, 523, 524, 525, 526, 1074, 164, 165, - 166, 191, 167, 168, 169, 162, 170, 171, 172, 174, - 210, 212, 215, 233, 236, 239, 240, 242, 253, 254, - 255, 256, 257, 258, 259, 261, 262, 263, 264, 271, - 272, 305, 306, 307, 412, 413, 414, 562, 217, 218, - 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, - 229, 230, 231, 176, 232, 177, 194, 195, 196, 234, - 184, 185, 186, 187, 188, 213, 1074, 197, 178, 179, - 180, 198, 194, 181, 235, 199, 161, 200, 201, 182, - 202, 203, 204, 183, 205, 206, 207, 208, 209, 819, - 579, 601, 601, 541, 532, 815, 816, 1204, 1204, 1204, - 1204, 1204, 1204, 1204, 1204, 1204, 1204, 954, 928, 928, - 926, 928, 695, 817, 531, 963, 958, 381, 385, 542, - 580, 584, 383, 532, 541, 550, 551, 390, 560, 581, - 595, 596, 824, 793, 872, 867, 868, 881, 14, 825, - 869, 822, 870, 871, 823, 480, 850, 481, 875, 527, - 527, 527, 527, 488, 583, 1222, 1222, 791, 1026, 1022, - 1023, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, - 1222, 1220, 1220, 812, 812, 599, 633, 1220, 1220, 1220, - 1220, 1220, 1220, 1220, 1220, 1220, 1220, 313, 297, 1173, - 1173, 1173, 987, 282, 282, 282, 282, 987, 987, 987, - 987, 987, 987, 987, 987, 987, 447, 447, 432, 249, - 249, 1173, 554, 432, 432, 447, 1173, 1173, 1173, 1173, - 1046, 1047, 1173, 1173, 1173, 1254, 1254, 1254, 1254, 337, - 797, 1272, 1272, 930, 246, 246, 246, 246, 248, 250, - 888, 335, 876, 340, 877, 889, 1249, 1250, 1272, 518, - 518, 832, 1262, 340, 340, 518, 518, 518, 518, 518, - 518, 518, 518, 518, 518, 1275, 844, 340, 340, 831, - 340, 797, 1289, 797, 630, 1169, 644, 645, 646, 529, - 529, 529, 611, 612, 534, 1273, 1273, 340, 812, 951, - 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, - 432, 438, 1273, 432, 558, 593, 924, 924, 924, 924, - 379, 635, 438, 918, 925, 553, 1123, 1154, 904, 422, - 547, 1155, 1158, 905, 1159, 548, 326, 594, 1170, 696, - 614, 616, 1233, 634, 410, 807, 590, 653, 657, 965, - 661, 669, 961, 456, 1247, 1248, 809, 837, 1244, 1244, - 1244, 1171, 1230, 1231, 5, 656, 6, 533, 545, 389, - 968, 968, 533, 1166, 545, 834, 973, 382, 922, 397, - 670, 1256, 1256, 1256, 1256, 1011, 699, 561, 450, 451, - 452, 846, 842, 534, 457, 1280, 1281, 619, 619, 1015, - 1057, 395, 396, 995, 992, 993, 642, 1240, 643, 935, - 399, 400, 401, 461, 654, 0, 0, 0, 402, 0, - 840, 0, 333, 578, 1039, 0, 674, 660, 660, 0, - 666, 1037, 489, 588, 602, 605, 606, 607, 608, 627, - 628, 629, 673, 0, 0, 0, 1013, 893, 1062, 0, - 1242, 1242, 1013, 1168, 598, 252, 252, 0, 0, 970, - 269, 845, 833, 1010, 1014, 530, 530, 836, 0, 639, - 949, 933, 0, 338, 339, 830, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1165, 0, - 0, 0, 0, 0, 923, 0, 0, 0, 0, 0, + 193, 193, 670, 421, 643, 1022, 1290, 1290, 824, 415, + 307, 308, 328, 563, 313, 420, 329, 422, 622, 801, + 678, 341, 586, 1290, 825, 164, 164, 164, 164, 217, + 194, 190, 190, 174, 176, 212, 190, 190, 190, 190, + 190, 191, 191, 191, 191, 191, 191, 185, 186, 187, + 188, 189, 214, 212, 215, 521, 522, 411, 523, 525, + 526, 527, 528, 529, 530, 531, 532, 1091, 165, 166, + 167, 192, 168, 169, 170, 163, 171, 172, 173, 175, + 211, 213, 216, 234, 237, 240, 241, 243, 254, 255, + 256, 257, 258, 259, 260, 262, 263, 264, 265, 273, + 274, 310, 311, 312, 416, 417, 418, 568, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 177, 233, 178, 195, 196, 197, 235, + 185, 186, 187, 188, 189, 214, 1091, 198, 179, 180, + 181, 199, 195, 182, 236, 200, 198, 162, 201, 202, + 183, 203, 204, 205, 184, 206, 207, 208, 209, 210, + 322, 322, 322, 322, 827, 608, 608, 858, 547, 538, + 1186, 1221, 1221, 1221, 1221, 1221, 1221, 1221, 1221, 1221, + 1221, 1239, 1239, 462, 1264, 1265, 799, 1239, 1239, 1239, + 1239, 1239, 1239, 1239, 1239, 1239, 1239, 387, 538, 547, + 556, 557, 394, 566, 588, 602, 603, 832, 938, 880, + 875, 876, 889, 14, 833, 877, 830, 878, 879, 831, + 453, 453, 884, 883, 885, 1187, 250, 250, 560, 453, + 1237, 1237, 815, 1043, 1039, 1040, 1237, 1237, 1237, 1237, + 1237, 1237, 1237, 1237, 1237, 1237, 820, 820, 1188, 1247, + 1248, 247, 247, 247, 247, 249, 251, 342, 343, 339, + 1190, 1190, 997, 1190, 997, 1279, 930, 401, 677, 997, + 997, 997, 997, 997, 997, 997, 997, 997, 997, 997, + 997, 1261, 1261, 414, 1261, 597, 1190, 287, 287, 287, + 287, 1190, 1190, 1190, 1190, 959, 344, 1190, 1190, 1190, + 1271, 1271, 1271, 1271, 606, 640, 344, 344, 1273, 1273, + 1273, 1273, 1063, 1064, 637, 896, 651, 652, 653, 897, + 344, 344, 383, 344, 486, 1306, 487, 535, 535, 5, + 535, 6, 494, 559, 1257, 1140, 540, 524, 524, 344, + 318, 302, 642, 524, 524, 524, 524, 524, 524, 524, + 524, 524, 524, 444, 1266, 1267, 618, 619, 932, 932, + 932, 932, 820, 428, 444, 926, 933, 330, 533, 533, + 533, 533, 1030, 590, 817, 554, 1259, 1259, 1030, 704, + 621, 623, 845, 641, 1250, 805, 393, 660, 664, 973, + 668, 676, 969, 1183, 553, 842, 823, 1289, 1289, 564, + 600, 601, 385, 389, 548, 587, 591, 663, 962, 936, + 936, 934, 936, 703, 1289, 537, 971, 966, 438, 901, + 1079, 981, 1028, 438, 438, 805, 605, 805, 707, 854, + 1292, 978, 463, 539, 551, 1074, 467, 540, 539, 844, + 551, 646, 957, 386, 1171, 912, 1032, 838, 1172, 1175, + 913, 1176, 943, 567, 456, 457, 458, 0, 850, 0, + 1182, 1297, 1298, 253, 253, 0, 399, 400, 0, 0, + 0, 649, 0, 650, 423, 403, 404, 405, 840, 661, + 0, 423, 0, 406, 0, 0, 848, 337, 1009, 1002, + 1006, 1003, 1007, 852, 0, 0, 839, 1185, 495, 0, + 0, 0, 0, 438, 438, 438, 438, 438, 438, 438, + 438, 438, 438, 438, 0, 0, 438, 595, 609, 612, + 613, 614, 615, 634, 635, 636, 680, 853, 841, 1027, + 1031, 585, 1056, 0, 681, 667, 667, 941, 673, 1054, + 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, + 536, 536, 919, 992, 1000, 1004, 1001, 1005, 0, 0, + 931, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1055, 849 + 976, 976, 0, 1072, 857 ); protected $gotoCheck = array( - 42, 42, 72, 65, 65, 87, 87, 87, 87, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 115, - 9, 23, 23, 23, 23, 42, 42, 42, 42, 42, + 42, 42, 72, 65, 65, 119, 173, 173, 26, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 7, + 9, 93, 122, 173, 27, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, @@ -753,91 +757,92 @@ class Php7 extends \PhpParser\ParserAbstract 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 15, - 118, 104, 104, 75, 75, 25, 26, 104, 104, 104, - 104, 104, 104, 104, 104, 104, 104, 25, 25, 25, - 25, 25, 25, 27, 25, 25, 25, 58, 58, 58, - 58, 58, 75, 75, 75, 75, 75, 75, 75, 75, - 75, 75, 15, 7, 15, 15, 15, 15, 75, 15, - 15, 15, 15, 15, 15, 143, 45, 143, 15, 103, - 103, 103, 103, 143, 103, 156, 156, 6, 15, 15, - 15, 156, 156, 156, 156, 156, 156, 156, 156, 156, - 156, 157, 157, 22, 22, 55, 55, 157, 157, 157, - 157, 157, 157, 157, 157, 157, 157, 155, 155, 72, - 72, 72, 72, 24, 24, 24, 24, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 137, 137, 23, 5, - 5, 72, 158, 23, 23, 137, 72, 72, 72, 72, - 132, 132, 72, 72, 72, 9, 9, 9, 9, 93, - 12, 168, 168, 49, 5, 5, 5, 5, 5, 5, - 72, 165, 64, 14, 64, 72, 164, 164, 168, 159, - 159, 35, 167, 14, 14, 159, 159, 159, 159, 159, - 159, 159, 159, 159, 159, 168, 35, 14, 14, 35, - 14, 12, 14, 12, 84, 20, 84, 84, 84, 19, - 19, 19, 83, 83, 14, 169, 169, 14, 22, 99, - 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 19, 169, 23, 2, 2, 19, 19, 19, 19, - 61, 63, 19, 19, 19, 100, 139, 78, 78, 108, - 9, 78, 78, 78, 78, 48, 29, 9, 20, 48, - 48, 48, 14, 48, 13, 20, 13, 48, 48, 48, - 48, 48, 48, 162, 162, 162, 18, 39, 118, 118, - 118, 20, 20, 20, 46, 14, 46, 9, 9, 28, - 103, 103, 9, 148, 9, 37, 106, 9, 89, 89, - 89, 118, 118, 118, 118, 117, 95, 9, 9, 9, - 9, 41, 9, 14, 145, 9, 9, 111, 111, 120, - 135, 80, 80, 111, 111, 111, 80, 118, 80, 92, - 80, 80, 80, 82, 80, -1, -1, -1, 80, -1, - 9, -1, 80, 8, 8, -1, 8, 8, 8, -1, - 8, 8, 9, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, -1, -1, -1, 118, 17, 17, -1, - 118, 118, 118, 14, 17, 5, 5, -1, -1, 17, - 24, 16, 16, 16, 16, 24, 24, 17, -1, 17, - 17, 16, -1, 93, 93, 17, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 17, -1, - -1, -1, -1, -1, 16, -1, -1, -1, -1, -1, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 23, 23, 23, 23, 15, 104, 104, 45, 75, 75, + 20, 104, 104, 104, 104, 104, 104, 104, 104, 104, + 104, 160, 160, 166, 166, 166, 6, 160, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 15, 49, 15, + 15, 15, 15, 75, 15, 15, 15, 15, 15, 15, + 141, 141, 64, 15, 64, 20, 5, 5, 162, 141, + 161, 161, 20, 15, 15, 15, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 22, 22, 20, 20, + 20, 5, 5, 5, 5, 5, 5, 93, 93, 169, + 72, 72, 72, 72, 72, 171, 89, 89, 89, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 122, 122, 13, 122, 13, 72, 24, 24, 24, + 24, 72, 72, 72, 72, 99, 14, 72, 72, 72, + 9, 9, 9, 9, 55, 55, 14, 14, 122, 122, + 122, 122, 136, 136, 84, 72, 84, 84, 84, 72, + 14, 14, 61, 14, 147, 14, 147, 19, 19, 46, + 19, 46, 147, 100, 122, 143, 14, 163, 163, 14, + 159, 159, 63, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 19, 168, 168, 83, 83, 19, 19, + 19, 19, 22, 109, 19, 19, 19, 29, 103, 103, + 103, 103, 122, 103, 18, 48, 122, 122, 122, 48, + 48, 48, 39, 48, 14, 12, 28, 48, 48, 48, + 48, 48, 48, 152, 9, 37, 25, 172, 172, 2, + 2, 9, 58, 58, 58, 58, 58, 14, 25, 25, + 25, 25, 25, 25, 172, 25, 25, 25, 23, 17, + 17, 106, 121, 23, 23, 12, 17, 12, 95, 41, + 172, 17, 149, 9, 9, 139, 82, 14, 9, 17, + 9, 17, 17, 9, 78, 78, 124, 17, 78, 78, + 78, 78, 92, 9, 9, 9, 9, -1, 9, -1, + 17, 9, 9, 5, 5, -1, 80, 80, -1, -1, + -1, 80, -1, 80, 113, 80, 80, 80, 35, 80, + -1, 113, -1, 80, -1, -1, 9, 80, 113, 113, + 113, 113, 113, 35, -1, -1, 35, 14, 9, -1, + -1, -1, -1, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, -1, -1, 23, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 16, 16, 16, + 16, 8, 8, -1, 8, 8, 8, 16, 8, 8, + -1, -1, -1, -1, -1, 24, -1, -1, -1, -1, + 24, 24, 87, 87, 87, 87, 87, 87, -1, -1, + 16, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 16, 16 + 103, 103, -1, 16, 16 ); protected $gotoBase = array( - 0, 0, -303, 0, 0, 278, 214, 194, 476, 7, - 0, 0, 15, 78, 27, -175, 87, 61, 118, 84, - -33, 0, -74, 18, 260, 161, 162, 179, 103, 111, - 0, 0, 0, 0, 0, -35, 0, 107, 0, 105, - 0, 26, -1, 0, 0, 204, -275, 0, -281, 281, - 0, 0, 0, 0, 0, 207, 0, 0, 144, 0, - 0, 340, 0, 143, 294, -234, 0, 0, 0, 0, - 0, 0, -6, 0, 0, -168, 0, 0, -8, 150, - -10, 0, 16, -108, -339, 0, 0, -270, 0, 145, - 0, 0, 42, -164, 0, 52, 0, 0, 0, 326, - 344, 0, 0, 193, -76, 0, 81, 0, 115, 0, - 0, 184, 0, 0, 0, 17, 0, 86, 153, 0, - 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 21, 0, 0, 32, 0, 244, 0, 119, - 0, 0, 0, -260, 0, 30, 0, 0, 79, 0, - 0, 0, 0, 0, 0, -53, -12, 4, 255, 82, - 0, 0, 124, 0, -41, 283, 0, 293, 5, 59, - 0, 0 + 0, 0, -285, 0, 0, 225, 173, 10, 524, 7, + 0, 0, 95, -47, 5, -174, 87, -33, 71, 61, + -212, 0, -76, 157, 284, 392, 4, 20, 56, 77, + 0, 0, 0, 0, 0, 118, 0, 63, 0, 65, + 0, -2, -1, 0, 0, 155, -378, 0, -308, 186, + 0, 0, 0, 0, 0, 266, 0, 0, 359, 0, + 0, 282, 0, 103, 204, -235, 0, 0, 0, 0, + 0, 0, -6, 0, 0, -167, 0, 0, 45, 170, + -11, 0, -27, -110, -376, 0, 0, 276, 0, -32, + 0, 0, 19, -448, 0, 30, 0, 0, 0, 262, + 292, 0, 0, 342, -73, 0, 62, 0, 0, 88, + 0, 0, 0, 206, 0, 0, 0, 0, 0, 3, + 0, 59, 15, 0, 14, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 41, 0, 0, 1, + 0, 188, 0, 66, 0, 0, 0, -157, 0, 2, + 0, 0, 35, 0, 0, 0, 0, 0, 0, 25, + -57, -8, 201, 99, 0, 0, -111, 0, -7, 231, + 0, 236, 96, -295, 0, 0 ); protected $gotoDefault = array( - -32768, 493, 703, 4, 704, 897, 780, 789, 577, 509, - 672, 334, 603, 408, 1238, 874, 1061, 559, 808, 1182, - 1190, 439, 811, 318, 320, 856, 857, 858, 386, 371, - 377, 384, 625, 604, 474, 843, 435, 835, 466, 838, - 434, 847, 160, 405, 491, 851, 3, 853, 536, 884, - 372, 861, 373, 649, 863, 544, 865, 866, 380, 387, - 388, 1066, 552, 600, 878, 241, 546, 879, 370, 880, - 887, 375, 378, 658, 446, 486, 479, 398, 1041, 587, - 622, 443, 460, 610, 609, 597, 459, 640, 403, 920, - 467, 444, 934, 336, 942, 701, 1073, 617, 469, 950, - 618, 957, 960, 510, 511, 458, 972, 273, 470, 1000, - 641, 985, 620, 998, 453, 1004, 436, 1012, 1226, 437, - 1016, 260, 1019, 274, 404, 419, 1024, 1025, 8, 1031, - 664, 665, 10, 270, 490, 1056, 659, 433, 1072, 423, - 1142, 1144, 538, 471, 1162, 1161, 652, 487, 1167, 1229, - 431, 512, 454, 304, 513, 296, 322, 301, 528, 283, - 323, 514, 455, 1235, 1243, 319, 29, 1263, 1274, 330, - 556, 592 + -32768, 499, 711, 4, 712, 905, 788, 797, 583, 515, + 679, 338, 610, 412, 1255, 882, 1078, 565, 816, 1199, + 1207, 445, 819, 323, 701, 864, 865, 866, 390, 375, + 381, 388, 632, 611, 480, 851, 441, 843, 472, 846, + 440, 855, 161, 409, 497, 859, 3, 861, 542, 892, + 376, 869, 377, 656, 871, 550, 873, 874, 384, 391, + 392, 1083, 558, 607, 886, 242, 552, 887, 374, 888, + 895, 379, 382, 665, 452, 492, 485, 402, 1058, 594, + 629, 449, 466, 617, 616, 604, 465, 424, 407, 928, + 473, 450, 942, 340, 950, 709, 1090, 624, 475, 958, + 625, 965, 968, 516, 517, 464, 980, 268, 983, 476, + 1015, 647, 648, 995, 626, 627, 1013, 459, 584, 1021, + 442, 1029, 1243, 443, 1033, 261, 1036, 275, 408, 425, + 1041, 1042, 8, 1048, 671, 672, 10, 272, 496, 1073, + 666, 439, 1089, 429, 1159, 1161, 544, 477, 1179, 1178, + 659, 493, 1184, 1246, 437, 518, 460, 309, 519, 301, + 326, 306, 534, 288, 327, 520, 461, 1252, 1260, 324, + 29, 1280, 1291, 334, 562, 599 ); protected $ruleToNonTerminal = array( @@ -868,16 +873,17 @@ class Php7 extends \PhpParser\ParserAbstract 93, 94, 94, 95, 95, 96, 97, 97, 98, 98, 99, 99, 54, 54, 50, 50, 101, 52, 52, 102, 51, 51, 53, 53, 63, 63, 63, 63, 79, 79, - 105, 105, 107, 107, 107, 107, 107, 106, 106, 106, - 109, 109, 109, 87, 87, 111, 111, 111, 110, 110, - 112, 112, 113, 113, 113, 108, 108, 80, 80, 80, - 20, 20, 114, 114, 115, 115, 115, 115, 59, 116, - 116, 117, 60, 119, 119, 120, 120, 121, 121, 84, - 122, 122, 122, 122, 122, 122, 127, 127, 128, 128, - 129, 129, 129, 129, 129, 130, 131, 131, 126, 126, - 123, 123, 125, 125, 133, 133, 132, 132, 132, 132, - 132, 132, 132, 124, 134, 134, 136, 135, 135, 61, - 100, 137, 137, 55, 55, 42, 42, 42, 42, 42, + 105, 105, 107, 107, 108, 108, 108, 108, 106, 106, + 106, 110, 110, 110, 110, 87, 87, 113, 113, 113, + 111, 111, 114, 114, 112, 112, 115, 115, 116, 116, + 116, 116, 109, 109, 80, 80, 80, 20, 20, 20, + 118, 117, 117, 119, 119, 119, 119, 59, 120, 120, + 121, 60, 123, 123, 124, 124, 125, 125, 84, 126, + 126, 126, 126, 126, 126, 131, 131, 132, 132, 133, + 133, 133, 133, 133, 134, 135, 135, 130, 130, 127, + 127, 129, 129, 137, 137, 136, 136, 136, 136, 136, + 136, 136, 128, 138, 138, 140, 139, 139, 61, 100, + 141, 141, 55, 55, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, @@ -886,20 +892,20 @@ class Php7 extends \PhpParser\ParserAbstract 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 144, 138, - 138, 143, 143, 146, 147, 147, 148, 149, 149, 149, - 19, 19, 72, 72, 72, 72, 139, 139, 139, 139, - 151, 151, 140, 140, 142, 142, 142, 145, 145, 156, - 156, 156, 156, 156, 156, 156, 156, 156, 157, 157, - 104, 159, 159, 159, 159, 141, 141, 141, 141, 141, - 141, 141, 141, 58, 58, 154, 154, 154, 154, 160, - 160, 150, 150, 150, 161, 161, 161, 161, 161, 161, - 73, 73, 65, 65, 65, 65, 118, 118, 118, 118, - 164, 163, 153, 153, 153, 153, 153, 153, 153, 152, - 152, 152, 162, 162, 162, 162, 103, 158, 166, 166, - 165, 165, 167, 167, 167, 167, 167, 167, 167, 167, - 155, 155, 155, 155, 169, 170, 168, 168, 168, 168, - 168, 168, 168, 168, 171, 171, 171, 171 + 42, 42, 42, 42, 42, 42, 42, 148, 142, 142, + 147, 147, 150, 151, 151, 152, 153, 153, 153, 19, + 19, 72, 72, 72, 72, 143, 143, 143, 143, 155, + 155, 144, 144, 146, 146, 146, 149, 149, 160, 160, + 160, 160, 160, 160, 160, 160, 160, 161, 161, 104, + 163, 163, 163, 163, 145, 145, 145, 145, 145, 145, + 145, 145, 58, 58, 158, 158, 158, 158, 164, 164, + 154, 154, 154, 165, 165, 165, 165, 165, 165, 73, + 73, 65, 65, 65, 65, 122, 122, 122, 122, 168, + 167, 157, 157, 157, 157, 157, 157, 157, 156, 156, + 156, 166, 166, 166, 166, 103, 162, 170, 170, 169, + 169, 171, 171, 171, 171, 171, 171, 171, 171, 159, + 159, 159, 159, 173, 174, 172, 172, 172, 172, 172, + 172, 172, 172, 175, 175, 175, 175 ); protected $ruleToLength = array( @@ -930,38 +936,39 @@ class Php7 extends \PhpParser\ParserAbstract 2, 4, 3, 1, 1, 7, 0, 2, 1, 3, 3, 4, 1, 4, 0, 2, 5, 0, 2, 6, 0, 2, 0, 3, 1, 2, 1, 1, 2, 0, - 1, 3, 0, 1, 1, 1, 1, 6, 8, 6, - 1, 2, 1, 1, 1, 1, 1, 1, 3, 3, - 3, 3, 1, 2, 1, 0, 1, 0, 2, 2, - 2, 4, 1, 3, 1, 2, 2, 3, 2, 3, - 1, 1, 2, 3, 1, 1, 3, 2, 0, 1, - 5, 5, 10, 3, 5, 1, 1, 3, 0, 2, - 4, 5, 4, 4, 4, 3, 1, 1, 1, 1, - 1, 1, 0, 1, 1, 2, 1, 1, 1, 1, - 1, 1, 1, 2, 1, 3, 1, 1, 3, 2, - 2, 3, 1, 0, 1, 1, 3, 3, 3, 4, - 1, 1, 2, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, + 1, 3, 0, 2, 1, 1, 1, 1, 6, 8, + 6, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 3, 3, 3, 3, 3, 3, 3, 3, 1, 2, + 1, 1, 0, 1, 0, 2, 2, 2, 4, 3, + 1, 1, 3, 1, 2, 2, 3, 2, 3, 1, + 1, 2, 3, 1, 1, 3, 2, 0, 1, 5, + 5, 10, 3, 5, 1, 1, 3, 0, 2, 4, + 5, 4, 4, 4, 3, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 2, 1, 1, 1, 1, 1, + 1, 1, 2, 1, 3, 1, 1, 3, 2, 2, + 3, 1, 0, 1, 1, 3, 3, 3, 4, 1, + 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, - 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 5, 4, 3, 4, 4, 2, 2, - 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 1, 3, 2, 1, 2, 4, 2, 2, - 8, 9, 8, 9, 9, 10, 9, 10, 8, 3, - 2, 0, 4, 2, 1, 3, 2, 2, 2, 4, - 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, - 1, 1, 0, 3, 0, 1, 1, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, - 3, 4, 1, 1, 3, 1, 1, 1, 1, 1, - 3, 2, 3, 0, 1, 1, 3, 1, 1, 1, - 1, 1, 3, 1, 1, 4, 4, 1, 4, 4, - 0, 1, 1, 1, 3, 3, 1, 4, 2, 2, - 1, 3, 1, 4, 4, 3, 3, 3, 3, 1, - 3, 1, 1, 3, 1, 1, 4, 1, 1, 1, - 3, 1, 1, 2, 1, 3, 4, 3, 2, 0, - 2, 2, 1, 2, 1, 1, 1, 4, 3, 3, - 3, 3, 6, 3, 1, 1, 2, 1 + 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 5, 4, 3, 4, 4, 2, 2, 4, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 1, 3, 2, 1, 2, 4, 2, 2, 8, + 9, 8, 9, 9, 10, 9, 10, 8, 3, 2, + 0, 4, 2, 1, 3, 2, 2, 2, 4, 1, + 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, + 1, 0, 3, 0, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, + 4, 1, 1, 3, 1, 1, 1, 1, 1, 3, + 2, 3, 0, 1, 1, 3, 1, 1, 1, 1, + 1, 3, 1, 1, 4, 4, 1, 4, 4, 0, + 1, 1, 1, 3, 3, 1, 4, 2, 2, 1, + 3, 1, 4, 4, 3, 3, 3, 3, 1, 3, + 1, 1, 3, 1, 1, 4, 1, 1, 1, 3, + 1, 1, 2, 1, 3, 4, 3, 2, 0, 2, + 2, 1, 2, 1, 1, 1, 4, 3, 3, 3, + 3, 6, 3, 1, 1, 2, 1 ); protected function initReduceCallbacks() { @@ -1814,57 +1821,57 @@ protected function initReduceCallbacks() { $this->semValue = 0; }, 273 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PUBLIC; + $this->checkModifier($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $this->semValue = $this->semStack[$stackPos-(2-1)] | $this->semStack[$stackPos-(2-2)]; }, 274 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PROTECTED; + $this->semValue = Stmt\Class_::MODIFIER_PUBLIC; }, 275 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PRIVATE; + $this->semValue = Stmt\Class_::MODIFIER_PROTECTED; }, 276 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_READONLY; + $this->semValue = Stmt\Class_::MODIFIER_PRIVATE; }, 277 => function ($stackPos) { - $this->semValue = new Node\Param($this->semStack[$stackPos-(6-6)], null, $this->semStack[$stackPos-(6-3)], $this->semStack[$stackPos-(6-4)], $this->semStack[$stackPos-(6-5)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes, $this->semStack[$stackPos-(6-2)], $this->semStack[$stackPos-(6-1)]); - $this->checkParam($this->semValue); + $this->semValue = Stmt\Class_::MODIFIER_READONLY; }, 278 => function ($stackPos) { - $this->semValue = new Node\Param($this->semStack[$stackPos-(8-6)], $this->semStack[$stackPos-(8-8)], $this->semStack[$stackPos-(8-3)], $this->semStack[$stackPos-(8-4)], $this->semStack[$stackPos-(8-5)], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes, $this->semStack[$stackPos-(8-2)], $this->semStack[$stackPos-(8-1)]); + $this->semValue = new Node\Param($this->semStack[$stackPos-(6-6)], null, $this->semStack[$stackPos-(6-3)], $this->semStack[$stackPos-(6-4)], $this->semStack[$stackPos-(6-5)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes, $this->semStack[$stackPos-(6-2)], $this->semStack[$stackPos-(6-1)]); $this->checkParam($this->semValue); }, 279 => function ($stackPos) { - $this->semValue = new Node\Param(new Expr\Error($this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes), null, $this->semStack[$stackPos-(6-3)], $this->semStack[$stackPos-(6-4)], $this->semStack[$stackPos-(6-5)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes, $this->semStack[$stackPos-(6-2)], $this->semStack[$stackPos-(6-1)]); + $this->semValue = new Node\Param($this->semStack[$stackPos-(8-6)], $this->semStack[$stackPos-(8-8)], $this->semStack[$stackPos-(8-3)], $this->semStack[$stackPos-(8-4)], $this->semStack[$stackPos-(8-5)], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes, $this->semStack[$stackPos-(8-2)], $this->semStack[$stackPos-(8-1)]); + $this->checkParam($this->semValue); }, 280 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Node\Param(new Expr\Error($this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes), null, $this->semStack[$stackPos-(6-3)], $this->semStack[$stackPos-(6-4)], $this->semStack[$stackPos-(6-5)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes, $this->semStack[$stackPos-(6-2)], $this->semStack[$stackPos-(6-1)]); }, 281 => function ($stackPos) { - $this->semValue = new Node\NullableType($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 282 => function ($stackPos) { - $this->semValue = new Node\UnionType($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = new Node\NullableType($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 283 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Node\UnionType($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 284 => function ($stackPos) { - $this->semValue = new Node\Name('static', $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = new Node\IntersectionType($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 285 => function ($stackPos) { - $this->semValue = $this->handleBuiltinTypes($this->semStack[$stackPos-(1-1)]); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 286 => function ($stackPos) { - $this->semValue = new Node\Identifier('array', $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = new Node\Name('static', $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 287 => function ($stackPos) { - $this->semValue = new Node\Identifier('callable', $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = $this->handleBuiltinTypes($this->semStack[$stackPos-(1-1)]); }, 288 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)]); + $this->semValue = new Node\Identifier('array', $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 289 => function ($stackPos) { - $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + $this->semValue = new Node\Identifier('callable', $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 290 => function ($stackPos) { $this->semValue = array($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)]); @@ -1873,692 +1880,692 @@ protected function initReduceCallbacks() { $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; }, 292 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = array($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)]); }, 293 => function ($stackPos) { - $this->semValue = new Node\NullableType($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; }, 294 => function ($stackPos) { - $this->semValue = new Node\UnionType($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = array($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)]); }, 295 => function ($stackPos) { - $this->semValue = null; + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; }, 296 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = array($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)]); }, 297 => function ($stackPos) { - $this->semValue = null; + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; }, 298 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(2-2)]; + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 299 => function ($stackPos) { - $this->semValue = null; + $this->semValue = new Node\NullableType($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 300 => function ($stackPos) { - $this->semValue = array(); + $this->semValue = new Node\UnionType($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 301 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(4-2)]; + $this->semValue = new Node\IntersectionType($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 302 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos-(1-1)]); + $this->semValue = null; }, 303 => function ($stackPos) { - $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 304 => function ($stackPos) { - $this->semValue = new Node\Arg($this->semStack[$stackPos-(1-1)], false, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = null; }, 305 => function ($stackPos) { - $this->semValue = new Node\Arg($this->semStack[$stackPos-(2-2)], true, false, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(2-2)]; }, 306 => function ($stackPos) { - $this->semValue = new Node\Arg($this->semStack[$stackPos-(2-2)], false, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = null; }, 307 => function ($stackPos) { - $this->semValue = new Node\Arg($this->semStack[$stackPos-(3-3)], false, false, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes, $this->semStack[$stackPos-(3-1)]); + $this->semValue = array(); }, 308 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(2-1)]; + $this->semValue = $this->semStack[$stackPos-(4-2)]; }, 309 => function ($stackPos) { - $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + $this->semValue = array($this->semStack[$stackPos-(3-2)]); }, 310 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos-(1-1)]); + $this->semValue = new Node\VariadicPlaceholder($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 311 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = array($this->semStack[$stackPos-(1-1)]); }, 312 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(2-1)]; + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; }, 313 => function ($stackPos) { - $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + $this->semValue = new Node\Arg($this->semStack[$stackPos-(1-1)], false, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 314 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos-(1-1)]); + $this->semValue = new Node\Arg($this->semStack[$stackPos-(2-2)], true, false, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 315 => function ($stackPos) { - $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos-(1-1)], null, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = new Node\Arg($this->semStack[$stackPos-(2-2)], false, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 316 => function ($stackPos) { - $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Node\Arg($this->semStack[$stackPos-(3-3)], false, false, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes, $this->semStack[$stackPos-(3-1)]); }, 317 => function ($stackPos) { - if ($this->semStack[$stackPos-(2-2)] !== null) { $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; } + $this->semValue = $this->semStack[$stackPos-(2-1)]; }, 318 => function ($stackPos) { - $this->semValue = array(); + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; }, 319 => function ($stackPos) { - $startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); } else { $nop = null; }; - if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = array($this->semStack[$stackPos-(1-1)]); }, 320 => function ($stackPos) { - $this->semValue = new Stmt\Property($this->semStack[$stackPos-(5-2)], $this->semStack[$stackPos-(5-4)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes, $this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-1)]); - $this->checkProperty($this->semValue, $stackPos-(5-2)); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 321 => function ($stackPos) { - $this->semValue = new Stmt\ClassConst($this->semStack[$stackPos-(5-4)], $this->semStack[$stackPos-(5-2)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes, $this->semStack[$stackPos-(5-1)]); - $this->checkClassConst($this->semValue, $stackPos-(5-2)); + $this->semValue = $this->semStack[$stackPos-(2-1)]; }, 322 => function ($stackPos) { - $this->semValue = new Stmt\ClassMethod($this->semStack[$stackPos-(10-5)], ['type' => $this->semStack[$stackPos-(10-2)], 'byRef' => $this->semStack[$stackPos-(10-4)], 'params' => $this->semStack[$stackPos-(10-7)], 'returnType' => $this->semStack[$stackPos-(10-9)], 'stmts' => $this->semStack[$stackPos-(10-10)], 'attrGroups' => $this->semStack[$stackPos-(10-1)]], $this->startAttributeStack[$stackPos-(10-1)] + $this->endAttributes); - $this->checkClassMethod($this->semValue, $stackPos-(10-2)); + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; }, 323 => function ($stackPos) { - $this->semValue = new Stmt\TraitUse($this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = array($this->semStack[$stackPos-(1-1)]); }, 324 => function ($stackPos) { - $this->semValue = new Stmt\EnumCase($this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-4)], $this->semStack[$stackPos-(5-1)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos-(1-1)], null, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 325 => function ($stackPos) { - $this->semValue = null; /* will be skipped */ + $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 326 => function ($stackPos) { - $this->semValue = array(); + if ($this->semStack[$stackPos-(2-2)] !== null) { $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; } }, 327 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(3-2)]; + $this->semValue = array(); }, 328 => function ($stackPos) { - $this->semValue = array(); + $startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); } else { $nop = null; }; + if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 329 => function ($stackPos) { - $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + $this->semValue = new Stmt\Property($this->semStack[$stackPos-(5-2)], $this->semStack[$stackPos-(5-4)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes, $this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-1)]); + $this->checkProperty($this->semValue, $stackPos-(5-2)); }, 330 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Precedence($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = new Stmt\ClassConst($this->semStack[$stackPos-(5-4)], $this->semStack[$stackPos-(5-2)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes, $this->semStack[$stackPos-(5-1)]); + $this->checkClassConst($this->semValue, $stackPos-(5-2)); }, 331 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(5-1)][0], $this->semStack[$stackPos-(5-1)][1], $this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-4)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + $this->semValue = new Stmt\ClassMethod($this->semStack[$stackPos-(10-5)], ['type' => $this->semStack[$stackPos-(10-2)], 'byRef' => $this->semStack[$stackPos-(10-4)], 'params' => $this->semStack[$stackPos-(10-7)], 'returnType' => $this->semStack[$stackPos-(10-9)], 'stmts' => $this->semStack[$stackPos-(10-10)], 'attrGroups' => $this->semStack[$stackPos-(10-1)]], $this->startAttributeStack[$stackPos-(10-1)] + $this->endAttributes); + $this->checkClassMethod($this->semValue, $stackPos-(10-2)); }, 332 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], $this->semStack[$stackPos-(4-3)], null, $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = new Stmt\TraitUse($this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 333 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], null, $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = new Stmt\EnumCase($this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-4)], $this->semStack[$stackPos-(5-1)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); }, 334 => function ($stackPos) { - $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], null, $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = null; /* will be skipped */ }, 335 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)]); + $this->semValue = array(); }, 336 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = $this->semStack[$stackPos-(3-2)]; }, 337 => function ($stackPos) { - $this->semValue = array(null, $this->semStack[$stackPos-(1-1)]); + $this->semValue = array(); }, 338 => function ($stackPos) { - $this->semValue = null; + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; }, 339 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Stmt\TraitUseAdaptation\Precedence($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 340 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(5-1)][0], $this->semStack[$stackPos-(5-1)][1], $this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-4)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); }, 341 => function ($stackPos) { - $this->semValue = 0; + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], $this->semStack[$stackPos-(4-3)], null, $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 342 => function ($stackPos) { - $this->semValue = 0; + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], null, $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 343 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], null, $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 344 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = array($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)]); }, 345 => function ($stackPos) { - $this->checkModifier($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $this->semValue = $this->semStack[$stackPos-(2-1)] | $this->semStack[$stackPos-(2-2)]; + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 346 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PUBLIC; + $this->semValue = array(null, $this->semStack[$stackPos-(1-1)]); }, 347 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PROTECTED; + $this->semValue = null; }, 348 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_PRIVATE; + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 349 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_STATIC; + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 350 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; + $this->semValue = 0; }, 351 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_FINAL; + $this->semValue = 0; }, 352 => function ($stackPos) { - $this->semValue = Stmt\Class_::MODIFIER_READONLY; + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 353 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(2-1)]; + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 354 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos-(1-1)]); + $this->checkModifier($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $this->semValue = $this->semStack[$stackPos-(2-1)] | $this->semStack[$stackPos-(2-2)]; }, 355 => function ($stackPos) { - $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + $this->semValue = Stmt\Class_::MODIFIER_PUBLIC; }, 356 => function ($stackPos) { - $this->semValue = new Node\VarLikeIdentifier(substr($this->semStack[$stackPos-(1-1)], 1), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = Stmt\Class_::MODIFIER_PROTECTED; }, 357 => function ($stackPos) { - $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos-(1-1)], null, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = Stmt\Class_::MODIFIER_PRIVATE; }, 358 => function ($stackPos) { - $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = Stmt\Class_::MODIFIER_STATIC; }, 359 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(2-1)]; + $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; }, 360 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(2-1)]; + $this->semValue = Stmt\Class_::MODIFIER_FINAL; }, 361 => function ($stackPos) { - $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + $this->semValue = Stmt\Class_::MODIFIER_READONLY; }, 362 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos-(1-1)]); + $this->semValue = $this->semStack[$stackPos-(2-1)]; }, 363 => function ($stackPos) { - $this->semValue = array(); + $this->semValue = array($this->semStack[$stackPos-(1-1)]); }, 364 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; }, 365 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Node\VarLikeIdentifier(substr($this->semStack[$stackPos-(1-1)], 1), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 366 => function ($stackPos) { - $this->semValue = new Expr\Assign($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos-(1-1)], null, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 367 => function ($stackPos) { - $this->semValue = new Expr\Assign($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 368 => function ($stackPos) { - $this->semValue = new Expr\Assign($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(2-1)]; }, 369 => function ($stackPos) { - $this->semValue = new Expr\AssignRef($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(2-1)]; }, 370 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; }, 371 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = array($this->semStack[$stackPos-(1-1)]); }, 372 => function ($stackPos) { - $this->semValue = new Expr\Clone_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = array(); }, 373 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Plus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 374 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Minus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 375 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Mul($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\Assign($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 376 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Div($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\Assign($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 377 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Concat($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\Assign($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 378 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Mod($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\AssignRef($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 379 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\BitwiseAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 380 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\BitwiseOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 381 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\BitwiseXor($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\Clone_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 382 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\ShiftLeft($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\AssignOp\Plus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 383 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\ShiftRight($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\AssignOp\Minus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 384 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Pow($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\AssignOp\Mul($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 385 => function ($stackPos) { - $this->semValue = new Expr\AssignOp\Coalesce($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\AssignOp\Div($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 386 => function ($stackPos) { - $this->semValue = new Expr\PostInc($this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\AssignOp\Concat($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 387 => function ($stackPos) { - $this->semValue = new Expr\PreInc($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\AssignOp\Mod($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 388 => function ($stackPos) { - $this->semValue = new Expr\PostDec($this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\AssignOp\BitwiseAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 389 => function ($stackPos) { - $this->semValue = new Expr\PreDec($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\AssignOp\BitwiseOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 390 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BooleanOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\AssignOp\BitwiseXor($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 391 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BooleanAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\AssignOp\ShiftLeft($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 392 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\LogicalOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\AssignOp\ShiftRight($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 393 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\LogicalAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\AssignOp\Pow($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 394 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\LogicalXor($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\AssignOp\Coalesce($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 395 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\PostInc($this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 396 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\PreInc($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 397 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\PostDec($this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 398 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\BitwiseXor($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\PreDec($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 399 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Concat($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\BooleanOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 400 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Plus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\BooleanAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 401 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Minus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\LogicalOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 402 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Mul($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\LogicalAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 403 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Div($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\LogicalXor($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 404 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Mod($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\BitwiseOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 405 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\ShiftLeft($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 406 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\ShiftRight($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 407 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Pow($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\BitwiseXor($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 408 => function ($stackPos) { - $this->semValue = new Expr\UnaryPlus($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\Concat($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 409 => function ($stackPos) { - $this->semValue = new Expr\UnaryMinus($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\Plus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 410 => function ($stackPos) { - $this->semValue = new Expr\BooleanNot($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\Minus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 411 => function ($stackPos) { - $this->semValue = new Expr\BitwiseNot($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\Mul($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 412 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Identical($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\Div($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 413 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\NotIdentical($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\Mod($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 414 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Equal($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\ShiftLeft($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 415 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\NotEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\ShiftRight($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 416 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Spaceship($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\Pow($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 417 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Smaller($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\UnaryPlus($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 418 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\SmallerOrEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\UnaryMinus($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 419 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Greater($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BooleanNot($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 420 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\GreaterOrEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BitwiseNot($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 421 => function ($stackPos) { - $this->semValue = new Expr\Instanceof_($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\Identical($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 422 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(3-2)]; + $this->semValue = new Expr\BinaryOp\NotIdentical($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 423 => function ($stackPos) { - $this->semValue = new Expr\Ternary($this->semStack[$stackPos-(5-1)], $this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-5)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\Equal($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 424 => function ($stackPos) { - $this->semValue = new Expr\Ternary($this->semStack[$stackPos-(4-1)], null, $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\NotEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 425 => function ($stackPos) { - $this->semValue = new Expr\BinaryOp\Coalesce($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\Spaceship($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 426 => function ($stackPos) { - $this->semValue = new Expr\Isset_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\Smaller($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 427 => function ($stackPos) { - $this->semValue = new Expr\Empty_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\SmallerOrEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 428 => function ($stackPos) { - $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\Greater($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 429 => function ($stackPos) { - $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE_ONCE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\BinaryOp\GreaterOrEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 430 => function ($stackPos) { - $this->semValue = new Expr\Eval_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = new Expr\Instanceof_($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 431 => function ($stackPos) { - $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(3-2)]; }, 432 => function ($stackPos) { - $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE_ONCE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\Ternary($this->semStack[$stackPos-(5-1)], $this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-5)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); }, 433 => function ($stackPos) { - $this->semValue = new Expr\Cast\Int_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\Ternary($this->semStack[$stackPos-(4-1)], null, $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 434 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes; - $attrs['kind'] = $this->getFloatCastKind($this->semStack[$stackPos-(2-1)]); - $this->semValue = new Expr\Cast\Double($this->semStack[$stackPos-(2-2)], $attrs); + $this->semValue = new Expr\BinaryOp\Coalesce($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 435 => function ($stackPos) { - $this->semValue = new Expr\Cast\String_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\Isset_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 436 => function ($stackPos) { - $this->semValue = new Expr\Cast\Array_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\Empty_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 437 => function ($stackPos) { - $this->semValue = new Expr\Cast\Object_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 438 => function ($stackPos) { - $this->semValue = new Expr\Cast\Bool_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE_ONCE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 439 => function ($stackPos) { - $this->semValue = new Expr\Cast\Unset_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\Eval_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 440 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes; - $attrs['kind'] = strtolower($this->semStack[$stackPos-(2-1)]) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE; - $this->semValue = new Expr\Exit_($this->semStack[$stackPos-(2-2)], $attrs); + $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 441 => function ($stackPos) { - $this->semValue = new Expr\ErrorSuppress($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE_ONCE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 442 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Expr\Cast\Int_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 443 => function ($stackPos) { - $this->semValue = new Expr\ShellExec($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $attrs = $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes; + $attrs['kind'] = $this->getFloatCastKind($this->semStack[$stackPos-(2-1)]); + $this->semValue = new Expr\Cast\Double($this->semStack[$stackPos-(2-2)], $attrs); }, 444 => function ($stackPos) { - $this->semValue = new Expr\Print_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\Cast\String_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 445 => function ($stackPos) { - $this->semValue = new Expr\Yield_(null, null, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = new Expr\Cast\Array_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 446 => function ($stackPos) { - $this->semValue = new Expr\Yield_($this->semStack[$stackPos-(2-2)], null, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\Cast\Object_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 447 => function ($stackPos) { - $this->semValue = new Expr\Yield_($this->semStack[$stackPos-(4-4)], $this->semStack[$stackPos-(4-2)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = new Expr\Cast\Bool_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 448 => function ($stackPos) { - $this->semValue = new Expr\YieldFrom($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\Cast\Unset_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 449 => function ($stackPos) { - $this->semValue = new Expr\Throw_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $attrs = $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes; + $attrs['kind'] = strtolower($this->semStack[$stackPos-(2-1)]) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE; + $this->semValue = new Expr\Exit_($this->semStack[$stackPos-(2-2)], $attrs); }, 450 => function ($stackPos) { - $this->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $this->semStack[$stackPos-(8-2)], 'params' => $this->semStack[$stackPos-(8-4)], 'returnType' => $this->semStack[$stackPos-(8-6)], 'expr' => $this->semStack[$stackPos-(8-8)], 'attrGroups' => []], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes); + $this->semValue = new Expr\ErrorSuppress($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 451 => function ($stackPos) { - $this->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $this->semStack[$stackPos-(9-3)], 'params' => $this->semStack[$stackPos-(9-5)], 'returnType' => $this->semStack[$stackPos-(9-7)], 'expr' => $this->semStack[$stackPos-(9-9)], 'attrGroups' => []], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 452 => function ($stackPos) { - $this->semValue = new Expr\Closure(['static' => false, 'byRef' => $this->semStack[$stackPos-(8-2)], 'params' => $this->semStack[$stackPos-(8-4)], 'uses' => $this->semStack[$stackPos-(8-6)], 'returnType' => $this->semStack[$stackPos-(8-7)], 'stmts' => $this->semStack[$stackPos-(8-8)], 'attrGroups' => []], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes); + $this->semValue = new Expr\ShellExec($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 453 => function ($stackPos) { - $this->semValue = new Expr\Closure(['static' => true, 'byRef' => $this->semStack[$stackPos-(9-3)], 'params' => $this->semStack[$stackPos-(9-5)], 'uses' => $this->semStack[$stackPos-(9-7)], 'returnType' => $this->semStack[$stackPos-(9-8)], 'stmts' => $this->semStack[$stackPos-(9-9)], 'attrGroups' => []], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); + $this->semValue = new Expr\Print_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 454 => function ($stackPos) { - $this->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $this->semStack[$stackPos-(9-3)], 'params' => $this->semStack[$stackPos-(9-5)], 'returnType' => $this->semStack[$stackPos-(9-7)], 'expr' => $this->semStack[$stackPos-(9-9)], 'attrGroups' => $this->semStack[$stackPos-(9-1)]], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); + $this->semValue = new Expr\Yield_(null, null, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 455 => function ($stackPos) { - $this->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $this->semStack[$stackPos-(10-4)], 'params' => $this->semStack[$stackPos-(10-6)], 'returnType' => $this->semStack[$stackPos-(10-8)], 'expr' => $this->semStack[$stackPos-(10-10)], 'attrGroups' => $this->semStack[$stackPos-(10-1)]], $this->startAttributeStack[$stackPos-(10-1)] + $this->endAttributes); + $this->semValue = new Expr\Yield_($this->semStack[$stackPos-(2-2)], null, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 456 => function ($stackPos) { - $this->semValue = new Expr\Closure(['static' => false, 'byRef' => $this->semStack[$stackPos-(9-3)], 'params' => $this->semStack[$stackPos-(9-5)], 'uses' => $this->semStack[$stackPos-(9-7)], 'returnType' => $this->semStack[$stackPos-(9-8)], 'stmts' => $this->semStack[$stackPos-(9-9)], 'attrGroups' => $this->semStack[$stackPos-(9-1)]], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); + $this->semValue = new Expr\Yield_($this->semStack[$stackPos-(4-4)], $this->semStack[$stackPos-(4-2)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 457 => function ($stackPos) { - $this->semValue = new Expr\Closure(['static' => true, 'byRef' => $this->semStack[$stackPos-(10-4)], 'params' => $this->semStack[$stackPos-(10-6)], 'uses' => $this->semStack[$stackPos-(10-8)], 'returnType' => $this->semStack[$stackPos-(10-9)], 'stmts' => $this->semStack[$stackPos-(10-10)], 'attrGroups' => $this->semStack[$stackPos-(10-1)]], $this->startAttributeStack[$stackPos-(10-1)] + $this->endAttributes); + $this->semValue = new Expr\YieldFrom($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 458 => function ($stackPos) { - $this->semValue = array(new Stmt\Class_(null, ['type' => 0, 'extends' => $this->semStack[$stackPos-(8-4)], 'implements' => $this->semStack[$stackPos-(8-5)], 'stmts' => $this->semStack[$stackPos-(8-7)], 'attrGroups' => $this->semStack[$stackPos-(8-1)]], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes), $this->semStack[$stackPos-(8-3)]); - $this->checkClass($this->semValue[0], -1); + $this->semValue = new Expr\Throw_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 459 => function ($stackPos) { - $this->semValue = new Expr\New_($this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $this->semStack[$stackPos-(8-2)], 'params' => $this->semStack[$stackPos-(8-4)], 'returnType' => $this->semStack[$stackPos-(8-6)], 'expr' => $this->semStack[$stackPos-(8-8)], 'attrGroups' => []], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes); }, 460 => function ($stackPos) { - list($class, $ctorArgs) = $this->semStack[$stackPos-(2-2)]; $this->semValue = new Expr\New_($class, $ctorArgs, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $this->semStack[$stackPos-(9-3)], 'params' => $this->semStack[$stackPos-(9-5)], 'returnType' => $this->semStack[$stackPos-(9-7)], 'expr' => $this->semStack[$stackPos-(9-9)], 'attrGroups' => []], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); }, 461 => function ($stackPos) { - $this->semValue = array(); + $this->semValue = new Expr\Closure(['static' => false, 'byRef' => $this->semStack[$stackPos-(8-2)], 'params' => $this->semStack[$stackPos-(8-4)], 'uses' => $this->semStack[$stackPos-(8-6)], 'returnType' => $this->semStack[$stackPos-(8-7)], 'stmts' => $this->semStack[$stackPos-(8-8)], 'attrGroups' => []], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes); }, 462 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(4-3)]; + $this->semValue = new Expr\Closure(['static' => true, 'byRef' => $this->semStack[$stackPos-(9-3)], 'params' => $this->semStack[$stackPos-(9-5)], 'uses' => $this->semStack[$stackPos-(9-7)], 'returnType' => $this->semStack[$stackPos-(9-8)], 'stmts' => $this->semStack[$stackPos-(9-9)], 'attrGroups' => []], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); }, 463 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(2-1)]; + $this->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $this->semStack[$stackPos-(9-3)], 'params' => $this->semStack[$stackPos-(9-5)], 'returnType' => $this->semStack[$stackPos-(9-7)], 'expr' => $this->semStack[$stackPos-(9-9)], 'attrGroups' => $this->semStack[$stackPos-(9-1)]], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); }, 464 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos-(1-1)]); + $this->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $this->semStack[$stackPos-(10-4)], 'params' => $this->semStack[$stackPos-(10-6)], 'returnType' => $this->semStack[$stackPos-(10-8)], 'expr' => $this->semStack[$stackPos-(10-10)], 'attrGroups' => $this->semStack[$stackPos-(10-1)]], $this->startAttributeStack[$stackPos-(10-1)] + $this->endAttributes); }, 465 => function ($stackPos) { - $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + $this->semValue = new Expr\Closure(['static' => false, 'byRef' => $this->semStack[$stackPos-(9-3)], 'params' => $this->semStack[$stackPos-(9-5)], 'uses' => $this->semStack[$stackPos-(9-7)], 'returnType' => $this->semStack[$stackPos-(9-8)], 'stmts' => $this->semStack[$stackPos-(9-9)], 'attrGroups' => $this->semStack[$stackPos-(9-1)]], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); }, 466 => function ($stackPos) { - $this->semValue = new Expr\ClosureUse($this->semStack[$stackPos-(2-2)], $this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\Closure(['static' => true, 'byRef' => $this->semStack[$stackPos-(10-4)], 'params' => $this->semStack[$stackPos-(10-6)], 'uses' => $this->semStack[$stackPos-(10-8)], 'returnType' => $this->semStack[$stackPos-(10-9)], 'stmts' => $this->semStack[$stackPos-(10-10)], 'attrGroups' => $this->semStack[$stackPos-(10-1)]], $this->startAttributeStack[$stackPos-(10-1)] + $this->endAttributes); }, 467 => function ($stackPos) { - $this->semValue = new Expr\FuncCall($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = array(new Stmt\Class_(null, ['type' => 0, 'extends' => $this->semStack[$stackPos-(8-4)], 'implements' => $this->semStack[$stackPos-(8-5)], 'stmts' => $this->semStack[$stackPos-(8-7)], 'attrGroups' => $this->semStack[$stackPos-(8-1)]], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes), $this->semStack[$stackPos-(8-3)]); + $this->checkClass($this->semValue[0], -1); }, 468 => function ($stackPos) { - $this->semValue = new Expr\FuncCall($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\New_($this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 469 => function ($stackPos) { - $this->semValue = new Expr\StaticCall($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + list($class, $ctorArgs) = $this->semStack[$stackPos-(2-2)]; $this->semValue = new Expr\New_($class, $ctorArgs, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 470 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = array(); }, 471 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = $this->semStack[$stackPos-(4-3)]; }, 472 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(2-1)]; }, 473 => function ($stackPos) { - $this->semValue = new Name($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = array($this->semStack[$stackPos-(1-1)]); }, 474 => function ($stackPos) { - $this->semValue = new Name\FullyQualified(substr($this->semStack[$stackPos-(1-1)], 1), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; }, 475 => function ($stackPos) { - $this->semValue = new Name\Relative(substr($this->semStack[$stackPos-(1-1)], 10), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = new Expr\ClosureUse($this->semStack[$stackPos-(2-2)], $this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 476 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Expr\FuncCall($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 477 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Expr\FuncCall($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 478 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(3-2)]; + $this->semValue = new Expr\StaticCall($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 479 => function ($stackPos) { - $this->semValue = new Expr\Error($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); $this->errorState = 2; + $this->semValue = new Name($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 480 => function ($stackPos) { $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 481 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Name($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 482 => function ($stackPos) { - $this->semValue = null; + $this->semValue = new Name($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 483 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(3-2)]; + $this->semValue = new Name\FullyQualified(substr($this->semStack[$stackPos-(1-1)], 1), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 484 => function ($stackPos) { - $this->semValue = array(); + $this->semValue = new Name\Relative(substr($this->semStack[$stackPos-(1-1)], 10), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 485 => function ($stackPos) { - $this->semValue = array(new Scalar\EncapsedStringPart(Scalar\String_::parseEscapeSequences($this->semStack[$stackPos-(1-1)], '`'), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes)); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 486 => function ($stackPos) { - foreach ($this->semStack[$stackPos-(1-1)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', true); } }; $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 487 => function ($stackPos) { - $this->semValue = array(); + $this->semValue = $this->semStack[$stackPos-(3-2)]; }, 488 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Expr\Error($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); $this->errorState = 2; }, 489 => function ($stackPos) { - $this->semValue = new Expr\ConstFetch($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 490 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Line($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 491 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\File($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = null; }, 492 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Dir($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(3-2)]; }, 493 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Class_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = array(); }, 494 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Trait_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = array(new Scalar\EncapsedStringPart(Scalar\String_::parseEscapeSequences($this->semStack[$stackPos-(1-1)], '`'), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes)); }, 495 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Method($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + foreach ($this->semStack[$stackPos-(1-1)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', true); } }; $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 496 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Function_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = array(); }, 497 => function ($stackPos) { - $this->semValue = new Scalar\MagicConst\Namespace_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 498 => function ($stackPos) { - $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\ConstFetch($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 499 => function ($stackPos) { - $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos-(3-1)], new Expr\Error($this->startAttributeStack[$stackPos-(3-3)] + $this->endAttributeStack[$stackPos-(3-3)]), $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); $this->errorState = 2; + $this->semValue = new Scalar\MagicConst\Line($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 500 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = Expr\Array_::KIND_SHORT; - $this->semValue = new Expr\Array_($this->semStack[$stackPos-(3-2)], $attrs); + $this->semValue = new Scalar\MagicConst\File($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 501 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes; $attrs['kind'] = Expr\Array_::KIND_LONG; - $this->semValue = new Expr\Array_($this->semStack[$stackPos-(4-3)], $attrs); + $this->semValue = new Scalar\MagicConst\Dir($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 502 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Scalar\MagicConst\Class_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 503 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes; $attrs['kind'] = ($this->semStack[$stackPos-(1-1)][0] === "'" || ($this->semStack[$stackPos-(1-1)][1] === "'" && ($this->semStack[$stackPos-(1-1)][0] === 'b' || $this->semStack[$stackPos-(1-1)][0] === 'B')) ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED); - $this->semValue = new Scalar\String_(Scalar\String_::parse($this->semStack[$stackPos-(1-1)]), $attrs); + $this->semValue = new Scalar\MagicConst\Trait_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 504 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED; - foreach ($this->semStack[$stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', true); } }; $this->semValue = new Scalar\Encapsed($this->semStack[$stackPos-(3-2)], $attrs); + $this->semValue = new Scalar\MagicConst\Method($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 505 => function ($stackPos) { - $this->semValue = $this->parseLNumber($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = new Scalar\MagicConst\Function_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 506 => function ($stackPos) { - $this->semValue = new Scalar\DNumber(Scalar\DNumber::parse($this->semStack[$stackPos-(1-1)]), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = new Scalar\MagicConst\Namespace_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 507 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 508 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos-(3-1)], new Expr\Error($this->startAttributeStack[$stackPos-(3-3)] + $this->endAttributeStack[$stackPos-(3-3)]), $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); $this->errorState = 2; }, 509 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $attrs = $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = Expr\Array_::KIND_SHORT; + $this->semValue = new Expr\Array_($this->semStack[$stackPos-(3-2)], $attrs); }, 510 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes, $this->startAttributeStack[$stackPos-(3-3)] + $this->endAttributeStack[$stackPos-(3-3)], true); + $attrs = $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes; $attrs['kind'] = Expr\Array_::KIND_LONG; + $this->semValue = new Expr\Array_($this->semStack[$stackPos-(4-3)], $attrs); }, 511 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos-(2-1)], '', $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes, $this->startAttributeStack[$stackPos-(2-2)] + $this->endAttributeStack[$stackPos-(2-2)], true); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 512 => function ($stackPos) { - $this->semValue = $this->parseDocString($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes, $this->startAttributeStack[$stackPos-(3-3)] + $this->endAttributeStack[$stackPos-(3-3)], true); + $attrs = $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes; $attrs['kind'] = ($this->semStack[$stackPos-(1-1)][0] === "'" || ($this->semStack[$stackPos-(1-1)][1] === "'" && ($this->semStack[$stackPos-(1-1)][0] === 'b' || $this->semStack[$stackPos-(1-1)][0] === 'B')) ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED); + $this->semValue = new Scalar\String_(Scalar\String_::parse($this->semStack[$stackPos-(1-1)]), $attrs); }, 513 => function ($stackPos) { - $this->semValue = null; + $attrs = $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED; + foreach ($this->semStack[$stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', true); } }; $this->semValue = new Scalar\Encapsed($this->semStack[$stackPos-(3-2)], $attrs); }, 514 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = $this->parseLNumber($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 515 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Scalar\DNumber(Scalar\DNumber::parse($this->semStack[$stackPos-(1-1)]), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 516 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(3-2)]; + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 517 => function ($stackPos) { $this->semValue = $this->semStack[$stackPos-(1-1)]; @@ -2567,16 +2574,16 @@ protected function initReduceCallbacks() { $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 519 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = $this->parseDocString($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes, $this->startAttributeStack[$stackPos-(3-3)] + $this->endAttributeStack[$stackPos-(3-3)], true); }, 520 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = $this->parseDocString($this->semStack[$stackPos-(2-1)], '', $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes, $this->startAttributeStack[$stackPos-(2-2)] + $this->endAttributeStack[$stackPos-(2-2)], true); }, 521 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = $this->parseDocString($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes, $this->startAttributeStack[$stackPos-(3-3)] + $this->endAttributeStack[$stackPos-(3-3)], true); }, 522 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(3-2)]; + $this->semValue = null; }, 523 => function ($stackPos) { $this->semValue = $this->semStack[$stackPos-(1-1)]; @@ -2585,25 +2592,25 @@ protected function initReduceCallbacks() { $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 525 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(3-2)]; }, 526 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 527 => function ($stackPos) { $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 528 => function ($stackPos) { - $this->semValue = new Expr\MethodCall($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 529 => function ($stackPos) { - $this->semValue = new Expr\NullsafeMethodCall($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 530 => function ($stackPos) { - $this->semValue = null; + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 531 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = $this->semStack[$stackPos-(3-2)]; }, 532 => function ($stackPos) { $this->semValue = $this->semStack[$stackPos-(1-1)]; @@ -2612,165 +2619,192 @@ protected function initReduceCallbacks() { $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 534 => function ($stackPos) { - $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 535 => function ($stackPos) { - $this->semValue = new Expr\NullsafePropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 536 => function ($stackPos) { $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 537 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = new Expr\MethodCall($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 538 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\NullsafeMethodCall($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 539 => function ($stackPos) { - $this->semValue = new Expr\Variable(new Expr\Error($this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes), $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); $this->errorState = 2; + $this->semValue = null; }, 540 => function ($stackPos) { - $var = $this->semStack[$stackPos-(1-1)]->name; $this->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes) : $var; + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 541 => function ($stackPos) { - $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 542 => function ($stackPos) { $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 543 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 544 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = new Expr\NullsafePropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 545 => function ($stackPos) { - $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 546 => function ($stackPos) { - $this->semValue = new Expr\NullsafePropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 547 => function ($stackPos) { - $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 548 => function ($stackPos) { - $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\Variable(new Expr\Error($this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes), $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); $this->errorState = 2; }, 549 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $var = $this->semStack[$stackPos-(1-1)]->name; $this->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes) : $var; }, 550 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(3-2)]; + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 551 => function ($stackPos) { $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 552 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 553 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(3-2)]; + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 554 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 555 => function ($stackPos) { - $this->semValue = new Expr\Error($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); $this->errorState = 2; + $this->semValue = new Expr\NullsafePropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 556 => function ($stackPos) { - $this->semValue = new Expr\List_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 557 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; $end = count($this->semValue)-1; if ($this->semValue[$end] === null) array_pop($this->semValue); + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 558 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos]; + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 559 => function ($stackPos) { - /* do nothing -- prevent default action of $$=$this->semStack[$1]. See $551. */ + $this->semValue = $this->semStack[$stackPos-(3-2)]; }, 560 => function ($stackPos) { - $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 561 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos-(1-1)]); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 562 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(1-1)], null, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(3-2)]; }, 563 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(2-2)], null, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 564 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(1-1)], null, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = new Expr\Error($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); $this->errorState = 2; }, 565 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(3-3)], $this->semStack[$stackPos-(3-1)], false, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Expr\List_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 566 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(4-4)], $this->semStack[$stackPos-(4-1)], true, $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; $end = count($this->semValue)-1; if ($this->semValue[$end] === null) array_pop($this->semValue); }, 567 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(3-3)], $this->semStack[$stackPos-(3-1)], false, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos]; }, 568 => function ($stackPos) { - $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(2-2)], null, false, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + /* do nothing -- prevent default action of $$=$this->semStack[$1]. See $551. */ }, 569 => function ($stackPos) { - $this->semValue = null; + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; }, 570 => function ($stackPos) { - $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + $this->semValue = array($this->semStack[$stackPos-(1-1)]); }, 571 => function ($stackPos) { - $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(1-1)], null, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 572 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos-(1-1)]); + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(2-2)], null, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 573 => function ($stackPos) { - $this->semValue = array($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)]); + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(1-1)], null, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 574 => function ($stackPos) { - $this->semValue = new Scalar\EncapsedStringPart($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(3-3)], $this->semStack[$stackPos-(3-1)], false, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 575 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(4-4)], $this->semStack[$stackPos-(4-1)], true, $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 576 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(1-1)]; + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(3-3)], $this->semStack[$stackPos-(3-1)], false, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); }, 577 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(2-2)], null, false, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); }, 578 => function ($stackPos) { - $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = null; }, 579 => function ($stackPos) { - $this->semValue = new Expr\NullsafePropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; }, 580 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; }, 581 => function ($stackPos) { - $this->semValue = new Expr\Variable($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = array($this->semStack[$stackPos-(1-1)]); }, 582 => function ($stackPos) { - $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(6-2)], $this->semStack[$stackPos-(6-4)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); + $this->semValue = array($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)]); }, 583 => function ($stackPos) { - $this->semValue = $this->semStack[$stackPos-(3-2)]; + $this->semValue = new Scalar\EncapsedStringPart($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 584 => function ($stackPos) { - $this->semValue = new Scalar\String_($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 585 => function ($stackPos) { - $this->semValue = $this->parseNumString($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 586 => function ($stackPos) { - $this->semValue = $this->parseNumString('-' . $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 587 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 588 => function ($stackPos) { + $this->semValue = new Expr\NullsafePropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 589 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 590 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 591 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(6-2)], $this->semStack[$stackPos-(6-4)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); + }, + 592 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 593 => function ($stackPos) { + $this->semValue = new Scalar\String_($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 594 => function ($stackPos) { + $this->semValue = $this->parseNumString($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 595 => function ($stackPos) { + $this->semValue = $this->parseNumString('-' . $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 596 => function ($stackPos) { $this->semValue = $this->semStack[$stackPos-(1-1)]; }, ]; diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php b/app/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php index 62d1f34c1..bb70de659 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php @@ -33,6 +33,10 @@ protected function pArg(Node\Arg $node) { . $this->p($node->value); } + protected function pVariadicPlaceholder(Node\VariadicPlaceholder $node) { + return '...'; + } + protected function pConst(Node\Const_ $node) { return $node->name . ' = ' . $this->p($node->value); } @@ -45,6 +49,10 @@ protected function pUnionType(Node\UnionType $node) { return $this->pImplode($node->types, '|'); } + protected function pIntersectionType(Node\IntersectionType $node) { + return $this->pImplode($node->types, '&'); + } + protected function pIdentifier(Node\Identifier $node) { return $node->name; } diff --git a/app/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php b/app/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php index 8a3565f1c..2c7fc3070 100644 --- a/app/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php +++ b/app/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php @@ -824,7 +824,11 @@ protected function pArray( return null; } - if ($insertStr === ', ' && $this->isMultiline($origNodes)) { + // We go multiline if the original code was multiline, + // or if it's an array item with a comment above it. + if ($insertStr === ', ' && + ($this->isMultiline($origNodes) || $arrItem->getComments()) + ) { $insertStr = ','; $insertNewline = true; } @@ -842,11 +846,11 @@ protected function pArray( $this->setIndentLevel($lastElemIndentLevel); if ($insertNewline) { + $result .= $insertStr . $this->nl; $comments = $arrItem->getComments(); if ($comments) { - $result .= $this->nl . $this->pComments($comments); + $result .= $this->pComments($comments) . $this->nl; } - $result .= $insertStr . $this->nl; } else { $result .= $insertStr; } @@ -1343,6 +1347,7 @@ protected function initializeListInsertionMap() { //'Scalar_Encapsed->parts' => '', 'Stmt_Catch->types' => '|', 'UnionType->types' => '|', + 'IntersectionType->types' => '&', 'Stmt_If->elseifs' => ' ', 'Stmt_TryCatch->catches' => ' ', diff --git a/app/vendor/phpdocumentor/type-resolver/src/PseudoTypes/LiteralString.php b/app/vendor/phpdocumentor/type-resolver/src/PseudoTypes/LiteralString.php new file mode 100644 index 000000000..690f782b7 --- /dev/null +++ b/app/vendor/phpdocumentor/type-resolver/src/PseudoTypes/LiteralString.php @@ -0,0 +1,39 @@ + PseudoTypes\CallableString::class, 'false' => PseudoTypes\False_::class, 'true' => PseudoTypes\True_::class, + 'literal-string' => PseudoTypes\LiteralString::class, 'self' => Types\Self_::class, '$this' => Types\This::class, 'static' => Types\Static_::class, @@ -543,6 +545,7 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte // check the key type for an "array" collection. We allow only // strings or integers. if ( + !$keyType instanceof ArrayKey && !$keyType instanceof String_ && !$keyType instanceof Integer && !$keyType instanceof Compound @@ -555,6 +558,7 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte if ($keyType instanceof Compound) { foreach ($keyType->getIterator() as $item) { if ( + !$item instanceof ArrayKey && !$item instanceof String_ && !$item instanceof Integer ) { diff --git a/app/vendor/phpdocumentor/type-resolver/src/Types/ArrayKey.php b/app/vendor/phpdocumentor/type-resolver/src/Types/ArrayKey.php index 60e0b489d..cf86df007 100644 --- a/app/vendor/phpdocumentor/type-resolver/src/Types/ArrayKey.php +++ b/app/vendor/phpdocumentor/type-resolver/src/Types/ArrayKey.php @@ -13,6 +13,9 @@ namespace phpDocumentor\Reflection\Types; +use phpDocumentor\Reflection\PseudoType; +use phpDocumentor\Reflection\Type; + /** * Value Object representing a array-key Type. * @@ -20,13 +23,18 @@ * * @psalm-immutable */ -final class ArrayKey extends AggregatedType +final class ArrayKey extends AggregatedType implements PseudoType { public function __construct() { parent::__construct([new String_(), new Integer()], '|'); } + public function underlyingType(): Type + { + return new Compound([new String_(), new Integer()]); + } + public function __toString(): string { return 'array-key'; diff --git a/app/vendor/phpdocumentor/type-resolver/src/Types/ClassString.php b/app/vendor/phpdocumentor/type-resolver/src/Types/ClassString.php index a0e5e95d6..fbdd879bb 100644 --- a/app/vendor/phpdocumentor/type-resolver/src/Types/ClassString.php +++ b/app/vendor/phpdocumentor/type-resolver/src/Types/ClassString.php @@ -14,6 +14,7 @@ namespace phpDocumentor\Reflection\Types; use phpDocumentor\Reflection\Fqsen; +use phpDocumentor\Reflection\PseudoType; use phpDocumentor\Reflection\Type; /** @@ -21,7 +22,7 @@ * * @psalm-immutable */ -final class ClassString implements Type +final class ClassString extends String_ implements PseudoType { /** @var Fqsen|null */ private $fqsen; @@ -34,6 +35,11 @@ public function __construct(?Fqsen $fqsen = null) $this->fqsen = $fqsen; } + public function underlyingType(): Type + { + return new String_(); + } + /** * Returns the FQSEN associated with this object. */ diff --git a/app/vendor/phpstan/phpdoc-parser/CODE_OF_CONDUCT.md b/app/vendor/phpstan/phpdoc-parser/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..19f30678e --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project maintainer at . All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ \ No newline at end of file diff --git a/app/vendor/phpstan/phpdoc-parser/LICENSE b/app/vendor/phpstan/phpdoc-parser/LICENSE new file mode 100644 index 000000000..98a854e4f --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Ondřej Mirtes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/app/vendor/phpstan/phpdoc-parser/README.md b/app/vendor/phpstan/phpdoc-parser/README.md new file mode 100644 index 000000000..c2605f8fa --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/README.md @@ -0,0 +1,30 @@ +

PHPDoc-Parser for PHPStan

+ +

+ Build Status + Latest Stable Version + License + PHPStan Enabled +

+ +* [PHPStan](https://phpstan.org) + +------ + +Next generation phpDoc parser with support for intersection types and generics. + +## Code of Conduct + +This project adheres to a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project and its community, you are expected to uphold this code. + +## Building + +Initially you need to run `composer install`, or `composer update` in case you aren't working in a folder which was built before. + +Afterwards you can either run the whole build including linting and coding standards using + + vendor/bin/phing + +or run only tests using + + vendor/bin/phing tests \ No newline at end of file diff --git a/app/vendor/phpstan/phpdoc-parser/build-abnfgen.sh b/app/vendor/phpstan/phpdoc-parser/build-abnfgen.sh new file mode 100755 index 000000000..6e8752b47 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/build-abnfgen.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT_DIR="$DIR" + +if [[ ! -d "$ROOT_DIR/tools/abnfgen" ]]; then + rm -rf "$ROOT_DIR/temp/abnfgen" + mkdir -p "$ROOT_DIR/temp/abnfgen" + + tar xf "$ROOT_DIR/tests/abnfgen-0.20.tar.gz" \ + --directory "$ROOT_DIR/temp/abnfgen" \ + --strip-components 1 + + cd "$ROOT_DIR/temp/abnfgen" + ./configure + make + + mkdir -p "$ROOT_DIR/tools/abnfgen" + mv abnfgen "$ROOT_DIR/tools/abnfgen" + rm -rf "$ROOT_DIR/temp/abnfgen" "$ROOT_DIR/temp/abnfgen.tar.gz" +fi diff --git a/app/vendor/phpstan/phpdoc-parser/composer.json b/app/vendor/phpstan/phpdoc-parser/composer.json new file mode 100644 index 000000000..0d539ce65 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/composer.json @@ -0,0 +1,44 @@ +{ + "name": "phpstan/phpdoc-parser", + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "license": "MIT", + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "consistence/coding-standard": "^3.5", + "ergebnis/composer-normalize": "^2.0.2", + "jakub-onderka/php-parallel-lint": "^0.9.2", + "phing/phing": "^2.16.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.26", + "phpstan/phpstan-strict-rules": "^0.12", + "phpunit/phpunit": "^6.3", + "slevomat/coding-standard": "^4.7.2", + "symfony/process": "^4.0" + }, + "config": { + "sort-packages": true + }, + "extra": { + "branch-alias": { + "dev-master": "0.4-dev" + } + }, + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "autoload-dev": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "tests/PHPStan" + ] + } + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/app/vendor/phpstan/phpdoc-parser/doc/grammars/phpdoc-method.peg b/app/vendor/phpstan/phpdoc-parser/doc/grammars/phpdoc-method.peg new file mode 100644 index 000000000..f022779a0 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/doc/grammars/phpdoc-method.peg @@ -0,0 +1,41 @@ +PhpDocMethod + = AnnotationName IsStatic? MethodReturnType? MethodName MethodParameters? Description? + +AnnotationName + = '@method' + +IsStatic + = 'static' + +MethodReturnType + = Type + +MethodName + = [a-zA-Z_\127-\255][a-zA-Z0-9_\127-\255]* + +MethodParameters + = '(' MethodParametersInner? ')' + +MethodParametersInner + = MethodParameter (',' MethodParameter)* + +MethodParameter + = MethodParameterType? IsReference? IsVariaric? MethodParameterName MethodParameterDefaultValue? + +MethodParameterType + = Type + +IsReference + = '&' + +IsVariaric + = '...' + +MethodParameterName + = '$' [a-zA-Z_\127-\255][a-zA-Z0-9_\127-\255]* + +MethodParameterDefaultValue + = '=' PhpConstantExpr + +Description + = .+ # TODO: exclude EOL or another PhpDocTag start diff --git a/app/vendor/phpstan/phpdoc-parser/doc/grammars/phpdoc-param.peg b/app/vendor/phpstan/phpdoc-parser/doc/grammars/phpdoc-param.peg new file mode 100644 index 000000000..5a5c1b346 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/doc/grammars/phpdoc-param.peg @@ -0,0 +1,14 @@ +PhpDocParam + = AnnotationName Type IsVariadic? ParameterName Description? + +AnnotationName + = '@param' + +IsVariaric + = '...' + +ParameterName + = '$' [a-zA-Z_\127-\255][a-zA-Z0-9_\127-\255]* + +Description + = .+ # TODO: exclude EOL or another PhpDocTag start diff --git a/app/vendor/phpstan/phpdoc-parser/doc/grammars/type.abnf b/app/vendor/phpstan/phpdoc-parser/doc/grammars/type.abnf new file mode 100644 index 000000000..dd38d0519 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/doc/grammars/type.abnf @@ -0,0 +1,243 @@ +; ---------------------------------------------------------------------------- ; +; Type ; +; ---------------------------------------------------------------------------- ; + +Type + = Atomic [Union / Intersection] + / Nullable + +Union + = 1*(TokenUnion Atomic) + +Intersection + = 1*(TokenIntersection Atomic) + +Nullable + = TokenNullable TokenIdentifier [Generic] + +Atomic + = TokenIdentifier [Generic / Callable / Array] + / TokenThisVariable + / TokenParenthesesOpen Type TokenParenthesesClose [Array] + +Generic + = TokenAngleBracketOpen Type *(TokenComma Type) TokenAngleBracketClose + +Callable + = TokenParenthesesOpen [CallableParameters] TokenParenthesesClose TokenColon CallableReturnType + +CallableParameters + = CallableParameter *(TokenComma CallableParameter) + +CallableParameter + = Type [CallableParameterIsReference] [CallableParameterIsVariadic] [CallableParameterName] [CallableParameterIsOptional] + +CallableParameterIsReference + = TokenIntersection + +CallableParameterIsVariadic + = TokenVariadic + +CallableParameterName + = TokenVariable + +CallableParameterIsOptional + = TokenEqualSign + +CallableReturnType + = TokenIdentifier [Generic] + / Nullable + / TokenParenthesesOpen Type TokenParenthesesClose + +Array + = 1*(TokenSquareBracketOpen TokenSquareBracketClose) + +ArrayShape + = TokenCurlyBracketOpen ArrayShapeItem *(TokenComma ArrayShapeItem) TokenCurlyBracketClose + +ArrayShapeItem + = (ConstantString / ConstantInt / TokenIdentifier) TokenNullable TokenColon Type + / Type + +; ---------------------------------------------------------------------------- ; +; ConstantExpr ; +; ---------------------------------------------------------------------------- ; + +ConstantExpr + = ConstantFloat *ByteHorizontalWs + / ConstantInt *ByteHorizontalWs + / ConstantTrue *ByteHorizontalWs + / ConstantFalse *ByteHorizontalWs + / ConstantNull *ByteHorizontalWs + / ConstantString *ByteHorizontalWs + / ConstantArray *ByteHorizontalWs + / ConstantFetch *ByteHorizontalWs + +ConstantFloat + = ["-"] 1*ByteDecDigit "." *ByteDecDigit [ConstantFloatExp] + / ["-"] 1*ByteDecDigit ConstantFloatExp + / ["-"] "." 1*ByteDecDigit [ConstantFloatExp] + +ConstantFloatExp + = "e" ["-"] 1*ByteDecDigit + +ConstantInt + = ["-"] "0b" 1*ByteBinDigit + / ["-"] "0o" 1*ByteOctDigit + / ["-"] "0x" 1*ByteHexDigit + / ["-"] 1*ByteDecDigit + +ConstantTrue + = "true" + +ConstantFalse + = "false" + +ConstantNull + = "null" + +ConstantString + = ByteSingleQuote *(ByteBackslash ByteNotEol / ByteNotEolAndNotBackslashAndNotSingleQuote) ByteSingleQuote + / ByteDoubleQuote *(ByteBackslash ByteNotEol / ByteNotEolAndNotBackslashAndNotDoubleQuote) ByteDoubleQuote + +ConstantArray + = TokenSquareBracketOpen [ConstantArrayItems] TokenSquareBracketClose + / "array" TokenParenthesesOpen [ConstantArrayItems] TokenParenthesesClose + +ConstantArrayItems + = ConstantArrayItem *(TokenComma ConstantArrayItem) [TokenComma] + +ConstantArrayItem + = ConstantExpr [TokenDoubleArrow ConstantExpr] + +ConstantFetch + = TokenIdentifier [TokenDoubleColon ByteIdentifierFirst *ByteIdentifierSecond *ByteHorizontalWs] + + +; ---------------------------------------------------------------------------- ; +; Tokens ; +; ---------------------------------------------------------------------------- ; + +TokenUnion + = "|" *ByteHorizontalWs + +TokenIntersection + = "&" *ByteHorizontalWs + +TokenNullable + = "?" *ByteHorizontalWs + +TokenParenthesesOpen + = "(" *ByteHorizontalWs + +TokenParenthesesClose + = ")" *ByteHorizontalWs + +TokenAngleBracketOpen + = "<" *ByteHorizontalWs + +TokenAngleBracketClose + = ">" *ByteHorizontalWs + +TokenSquareBracketOpen + = "[" *ByteHorizontalWs + +TokenSquareBracketClose + = "]" *ByteHorizontalWs + +TokenCurlyBracketOpen + = "{" *ByteHorizontalWs + +TokenCurlyBracketClose + = "}" *ByteHorizontalWs + +TokenComma + = "," *ByteHorizontalWs + +TokenColon + = ":" *ByteHorizontalWs + +TokenVariadic + = "..." *ByteHorizontalWs + +TokenEqualSign + = "=" *ByteHorizontalWs + +TokenVariable + = "$" ByteIdentifierFirst *ByteIdentifierSecond *ByteHorizontalWs + +TokenDoubleArrow + = "=>" *ByteHorizontalWs + +TokenDoubleColon + = "::" *ByteHorizontalWs + +TokenThisVariable + = %x24.74.68.69.73 *ByteHorizontalWs + +TokenIdentifier + = [ByteBackslash] ByteIdentifierFirst *ByteIdentifierSecond *(ByteBackslash ByteIdentifierFirst *ByteIdentifierSecond) *ByteHorizontalWs + + +; ---------------------------------------------------------------------------- ; +; Bytes ; +; ---------------------------------------------------------------------------- ; + +ByteHorizontalWs + = %x09 ; horizontal tab + / %x20 ; space + +ByteBinDigit + = %x30-31 ; 0-1 + +ByteOctDigit + = %x30-37 ; 0-7 + +ByteDecDigit + = %x30-39 ; 0-9 + +ByteHexDigit + = %x30-39 ; 0-9 + / %x41-46 ; A-F + / %x61-66 ; a-f + +ByteIdentifierFirst + = %x41-5A ; A-Z + / %x5F ; _ + / %x61-7A ; a-z + / %x80-FF + +ByteIdentifierSecond + = %x30-39 ; 0-9 + / %x41-5A ; A-Z + / %x5F ; _ + / %x61-7A ; a-z + / %x80-FF + +ByteSingleQuote + = %x27 ; ' + +ByteDoubleQuote + = %x22 ; " + +ByteBackslash + = %x5C ; \ + +ByteNotEol + = %x00-09 ; skip LF + / %x0B-0C ; skip CR + / %x0E-FF + +ByteNotEolAndNotBackslashAndNotSingleQuote + = %x00-09 ; skip LF + / %x0B-0C ; skip CR + / %x0E-26 ; skip single quote + / %x28-5B ; skip backslash + / %x5D-FF + +ByteNotEolAndNotBackslashAndNotDoubleQuote + = %x00-09 ; skip LF + / %x0B-0C ; skip CR + / %x0E-21 ; skip double quote + / %x23-5B ; skip backslash + / %x5D-FF diff --git a/app/vendor/phpstan/phpdoc-parser/phpstan.neon b/app/vendor/phpstan/phpdoc-parser/phpstan.neon new file mode 100644 index 000000000..412a42f54 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/phpstan.neon @@ -0,0 +1,3 @@ +parameters: + ignoreErrors: + - '#^Dynamic call to static method PHPUnit\\Framework\\Assert#' diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayItemNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayItemNode.php new file mode 100644 index 000000000..9456e95df --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayItemNode.php @@ -0,0 +1,31 @@ +key = $key; + $this->value = $value; + } + + + public function __toString(): string + { + if ($this->key !== null) { + return "{$this->key} => {$this->value}"; + + } + + return "{$this->value}"; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayNode.php new file mode 100644 index 000000000..215c5a37d --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayNode.php @@ -0,0 +1,25 @@ +items = $items; + } + + + public function __toString(): string + { + return '[' . implode(', ', $this->items) . ']'; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFalseNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFalseNode.php new file mode 100644 index 000000000..fae8af1b1 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFalseNode.php @@ -0,0 +1,13 @@ +value = $value; + } + + + public function __toString(): string + { + return $this->value; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprIntegerNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprIntegerNode.php new file mode 100644 index 000000000..949ba5cba --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprIntegerNode.php @@ -0,0 +1,22 @@ +value = $value; + } + + + public function __toString(): string + { + return $this->value; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprNode.php new file mode 100644 index 000000000..1859f03e5 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprNode.php @@ -0,0 +1,10 @@ +value = $value; + } + + + public function __toString(): string + { + return $this->value; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprTrueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprTrueNode.php new file mode 100644 index 000000000..b0e3c003e --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprTrueNode.php @@ -0,0 +1,13 @@ +className = $className; + $this->name = $name; + } + + + public function __toString(): string + { + if ($this->className === '') { + return $this->name; + + } + + return "{$this->className}::{$this->name}"; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/Node.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/Node.php new file mode 100644 index 000000000..8c4ccb578 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/Node.php @@ -0,0 +1,10 @@ +description = $description; + } + + + public function __toString(): string + { + return trim($this->description); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ExtendsTagValueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ExtendsTagValueNode.php new file mode 100644 index 000000000..513f2975c --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ExtendsTagValueNode.php @@ -0,0 +1,28 @@ +type = $type; + $this->description = $description; + } + + + public function __toString(): string + { + return trim("{$this->type} {$this->description}"); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/GenericTagValueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/GenericTagValueNode.php new file mode 100644 index 000000000..97518ab65 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/GenericTagValueNode.php @@ -0,0 +1,22 @@ +value = $value; + } + + + public function __toString(): string + { + return $this->value; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ImplementsTagValueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ImplementsTagValueNode.php new file mode 100644 index 000000000..7691d93a2 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ImplementsTagValueNode.php @@ -0,0 +1,28 @@ +type = $type; + $this->description = $description; + } + + + public function __toString(): string + { + return trim("{$this->type} {$this->description}"); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/InvalidTagValueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/InvalidTagValueNode.php new file mode 100644 index 000000000..150acae55 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/InvalidTagValueNode.php @@ -0,0 +1,26 @@ +value = $value; + $this->exception = $exception; + } + + + public function __toString(): string + { + return $this->value; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueNode.php new file mode 100644 index 000000000..8f5035c3a --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueNode.php @@ -0,0 +1,44 @@ +isStatic = $isStatic; + $this->returnType = $returnType; + $this->methodName = $methodName; + $this->parameters = $parameters; + $this->description = $description; + } + + + public function __toString(): string + { + $static = $this->isStatic ? 'static ' : ''; + $returnType = $this->returnType !== null ? "{$this->returnType} " : ''; + $parameters = implode(', ', $this->parameters); + $description = $this->description !== '' ? " {$this->description}" : ''; + return "{$static}{$returnType}{$this->methodName}({$parameters}){$description}"; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueParameterNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueParameterNode.php new file mode 100644 index 000000000..f7409f3ef --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueParameterNode.php @@ -0,0 +1,46 @@ +type = $type; + $this->isReference = $isReference; + $this->isVariadic = $isVariadic; + $this->parameterName = $parameterName; + $this->defaultValue = $defaultValue; + } + + + public function __toString(): string + { + $type = $this->type !== null ? "{$this->type} " : ''; + $isReference = $this->isReference ? '&' : ''; + $isVariadic = $this->isVariadic ? '...' : ''; + $default = $this->defaultValue !== null ? " = {$this->defaultValue}" : ''; + return "{$type}{$isReference}{$isVariadic}{$this->parameterName}{$default}"; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MixinTagValueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MixinTagValueNode.php new file mode 100644 index 000000000..8290181de --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MixinTagValueNode.php @@ -0,0 +1,28 @@ +type = $type; + $this->description = $description; + } + + + public function __toString(): string + { + return trim("{$this->type} {$this->description}"); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamTagValueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamTagValueNode.php new file mode 100644 index 000000000..04710a7b4 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamTagValueNode.php @@ -0,0 +1,37 @@ +type = $type; + $this->isVariadic = $isVariadic; + $this->parameterName = $parameterName; + $this->description = $description; + } + + + public function __toString(): string + { + $variadic = $this->isVariadic ? '...' : ''; + return trim("{$this->type} {$variadic}{$this->parameterName} {$this->description}"); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocChildNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocChildNode.php new file mode 100644 index 000000000..6162f92df --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocChildNode.php @@ -0,0 +1,10 @@ +children = $children; + } + + + /** + * @return PhpDocTagNode[] + */ + public function getTags(): array + { + return array_filter($this->children, static function (PhpDocChildNode $child): bool { + return $child instanceof PhpDocTagNode; + }); + } + + + /** + * @param string $tagName + * @return PhpDocTagNode[] + */ + public function getTagsByName(string $tagName): array + { + return array_filter($this->getTags(), static function (PhpDocTagNode $tag) use ($tagName): bool { + return $tag->name === $tagName; + }); + } + + + /** + * @return VarTagValueNode[] + */ + public function getVarTagValues(string $tagName = '@var'): array + { + return array_column( + array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool { + return $tag->value instanceof VarTagValueNode; + }), + 'value' + ); + } + + + /** + * @return ParamTagValueNode[] + */ + public function getParamTagValues(string $tagName = '@param'): array + { + return array_column( + array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool { + return $tag->value instanceof ParamTagValueNode; + }), + 'value' + ); + } + + + /** + * @return TemplateTagValueNode[] + */ + public function getTemplateTagValues(string $tagName = '@template'): array + { + return array_column( + array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool { + return $tag->value instanceof TemplateTagValueNode; + }), + 'value' + ); + } + + + /** + * @return ExtendsTagValueNode[] + */ + public function getExtendsTagValues(string $tagName = '@extends'): array + { + return array_column( + array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool { + return $tag->value instanceof ExtendsTagValueNode; + }), + 'value' + ); + } + + + /** + * @return ImplementsTagValueNode[] + */ + public function getImplementsTagValues(string $tagName = '@implements'): array + { + return array_column( + array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool { + return $tag->value instanceof ImplementsTagValueNode; + }), + 'value' + ); + } + + + /** + * @return UsesTagValueNode[] + */ + public function getUsesTagValues(string $tagName = '@use'): array + { + return array_column( + array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool { + return $tag->value instanceof UsesTagValueNode; + }), + 'value' + ); + } + + + /** + * @return ReturnTagValueNode[] + */ + public function getReturnTagValues(string $tagName = '@return'): array + { + return array_column( + array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool { + return $tag->value instanceof ReturnTagValueNode; + }), + 'value' + ); + } + + + /** + * @return ThrowsTagValueNode[] + */ + public function getThrowsTagValues(string $tagName = '@throws'): array + { + return array_column( + array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool { + return $tag->value instanceof ThrowsTagValueNode; + }), + 'value' + ); + } + + + /** + * @return MixinTagValueNode[] + */ + public function getMixinTagValues(string $tagName = '@mixin'): array + { + return array_column( + array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool { + return $tag->value instanceof MixinTagValueNode; + }), + 'value' + ); + } + + + /** + * @return \PHPStan\PhpDocParser\Ast\PhpDoc\DeprecatedTagValueNode[] + */ + public function getDeprecatedTagValues(): array + { + return array_column( + array_filter($this->getTagsByName('@deprecated'), static function (PhpDocTagNode $tag): bool { + return $tag->value instanceof DeprecatedTagValueNode; + }), + 'value' + ); + } + + + /** + * @return PropertyTagValueNode[] + */ + public function getPropertyTagValues(string $tagName = '@property'): array + { + return array_column( + array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool { + return $tag->value instanceof PropertyTagValueNode; + }), + 'value' + ); + } + + + /** + * @return PropertyTagValueNode[] + */ + public function getPropertyReadTagValues(string $tagName = '@property-read'): array + { + return array_column( + array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool { + return $tag->value instanceof PropertyTagValueNode; + }), + 'value' + ); + } + + + /** + * @return PropertyTagValueNode[] + */ + public function getPropertyWriteTagValues(string $tagName = '@property-write'): array + { + return array_column( + array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool { + return $tag->value instanceof PropertyTagValueNode; + }), + 'value' + ); + } + + + /** + * @return MethodTagValueNode[] + */ + public function getMethodTagValues(string $tagName = '@method'): array + { + return array_column( + array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool { + return $tag->value instanceof MethodTagValueNode; + }), + 'value' + ); + } + + + public function __toString(): string + { + return "/**\n * " . implode("\n * ", $this->children) . '*/'; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagNode.php new file mode 100644 index 000000000..be3043bfd --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagNode.php @@ -0,0 +1,26 @@ +name = $name; + $this->value = $value; + } + + + public function __toString(): string + { + return trim("{$this->name} {$this->value}"); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagValueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagValueNode.php new file mode 100644 index 000000000..7723fa0c4 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagValueNode.php @@ -0,0 +1,10 @@ +text = $text; + } + + + public function __toString(): string + { + return $this->text; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PropertyTagValueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PropertyTagValueNode.php new file mode 100644 index 000000000..bca55a7e3 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PropertyTagValueNode.php @@ -0,0 +1,32 @@ +type = $type; + $this->propertyName = $propertyName; + $this->description = $description; + } + + + public function __toString(): string + { + return trim("{$this->type} {$this->propertyName} {$this->description}"); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ReturnTagValueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ReturnTagValueNode.php new file mode 100644 index 000000000..254d3f878 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ReturnTagValueNode.php @@ -0,0 +1,28 @@ +type = $type; + $this->description = $description; + } + + + public function __toString(): string + { + return trim("{$this->type} {$this->description}"); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TemplateTagValueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TemplateTagValueNode.php new file mode 100644 index 000000000..5847df226 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TemplateTagValueNode.php @@ -0,0 +1,33 @@ +name = $name; + $this->bound = $bound; + $this->description = $description; + } + + + public function __toString(): string + { + $bound = $this->bound !== null ? " of {$this->bound}" : ''; + return trim("{$this->name}{$bound} {$this->description}"); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ThrowsTagValueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ThrowsTagValueNode.php new file mode 100644 index 000000000..32ae12d2d --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ThrowsTagValueNode.php @@ -0,0 +1,28 @@ +type = $type; + $this->description = $description; + } + + + public function __toString(): string + { + return trim("{$this->type} {$this->description}"); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/UsesTagValueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/UsesTagValueNode.php new file mode 100644 index 000000000..20b193909 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/UsesTagValueNode.php @@ -0,0 +1,28 @@ +type = $type; + $this->description = $description; + } + + + public function __toString(): string + { + return trim("{$this->type} {$this->description}"); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/VarTagValueNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/VarTagValueNode.php new file mode 100644 index 000000000..2965ce7ca --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/VarTagValueNode.php @@ -0,0 +1,32 @@ +type = $type; + $this->variableName = $variableName; + $this->description = $description; + } + + + public function __toString(): string + { + return trim("$this->type " . trim("{$this->variableName} {$this->description}")); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeItemNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeItemNode.php new file mode 100644 index 000000000..b4996e90d --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeItemNode.php @@ -0,0 +1,45 @@ +keyName = $keyName; + $this->optional = $optional; + $this->valueType = $valueType; + } + + + public function __toString(): string + { + if ($this->keyName !== null) { + return sprintf( + '%s%s: %s', + (string) $this->keyName, + $this->optional ? '?' : '', + (string) $this->valueType + ); + } + + return (string) $this->valueType; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeNode.php new file mode 100644 index 000000000..74df4ab37 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeNode.php @@ -0,0 +1,22 @@ +items = $items; + } + + + public function __toString(): string + { + return 'array{' . implode(', ', $this->items) . '}'; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayTypeNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayTypeNode.php new file mode 100644 index 000000000..b01d083c0 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayTypeNode.php @@ -0,0 +1,22 @@ +type = $type; + } + + + public function __toString(): string + { + return $this->type . '[]'; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeNode.php new file mode 100644 index 000000000..2f4bf7c53 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeNode.php @@ -0,0 +1,31 @@ +identifier = $identifier; + $this->parameters = $parameters; + $this->returnType = $returnType; + } + + + public function __toString(): string + { + $parameters = implode(', ', $this->parameters); + return "{$this->identifier}({$parameters}): {$this->returnType}"; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeParameterNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeParameterNode.php new file mode 100644 index 000000000..3503318d4 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeParameterNode.php @@ -0,0 +1,44 @@ +type = $type; + $this->isReference = $isReference; + $this->isVariadic = $isVariadic; + $this->parameterName = $parameterName; + $this->isOptional = $isOptional; + } + + + public function __toString(): string + { + $type = "{$this->type} "; + $isReference = $this->isReference ? '&' : ''; + $isVariadic = $this->isVariadic ? '...' : ''; + $default = $this->isOptional ? ' = default' : ''; + return "{$type}{$isReference}{$isVariadic}{$this->parameterName}{$default}"; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConstTypeNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConstTypeNode.php new file mode 100644 index 000000000..ee9f14da9 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConstTypeNode.php @@ -0,0 +1,23 @@ +constExpr = $constExpr; + } + + public function __toString(): string + { + return $this->constExpr->__toString(); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/GenericTypeNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/GenericTypeNode.php new file mode 100644 index 000000000..4dcc9c8f9 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/GenericTypeNode.php @@ -0,0 +1,26 @@ +type = $type; + $this->genericTypes = $genericTypes; + } + + + public function __toString(): string + { + return $this->type . '<' . implode(', ', $this->genericTypes) . '>'; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/IdentifierTypeNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/IdentifierTypeNode.php new file mode 100644 index 000000000..9ca389cc6 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/IdentifierTypeNode.php @@ -0,0 +1,22 @@ +name = $name; + } + + + public function __toString(): string + { + return $this->name; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/IntersectionTypeNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/IntersectionTypeNode.php new file mode 100644 index 000000000..60341187a --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/IntersectionTypeNode.php @@ -0,0 +1,22 @@ +types = $types; + } + + + public function __toString(): string + { + return '(' . implode(' & ', $this->types) . ')'; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/NullableTypeNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/NullableTypeNode.php new file mode 100644 index 000000000..98c736475 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/NullableTypeNode.php @@ -0,0 +1,22 @@ +type = $type; + } + + + public function __toString(): string + { + return '?' . $this->type; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ThisTypeNode.php b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ThisTypeNode.php new file mode 100644 index 000000000..06a3537e5 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Ast/Type/ThisTypeNode.php @@ -0,0 +1,13 @@ +types = $types; + } + + + public function __toString(): string + { + return '(' . implode(' | ', $this->types) . ')'; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Lexer/Lexer.php b/app/vendor/phpstan/phpdoc-parser/src/Lexer/Lexer.php new file mode 100644 index 000000000..f03f0db8d --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Lexer/Lexer.php @@ -0,0 +1,168 @@ + '\'&\'', + self::TOKEN_UNION => '\'|\'', + self::TOKEN_INTERSECTION => '\'&\'', + self::TOKEN_NULLABLE => '\'?\'', + self::TOKEN_OPEN_PARENTHESES => '\'(\'', + self::TOKEN_CLOSE_PARENTHESES => '\')\'', + self::TOKEN_OPEN_ANGLE_BRACKET => '\'<\'', + self::TOKEN_CLOSE_ANGLE_BRACKET => '\'>\'', + self::TOKEN_OPEN_SQUARE_BRACKET => '\'[\'', + self::TOKEN_CLOSE_SQUARE_BRACKET => '\']\'', + self::TOKEN_OPEN_CURLY_BRACKET => '\'{\'', + self::TOKEN_CLOSE_CURLY_BRACKET => '\'}\'', + self::TOKEN_COMMA => '\',\'', + self::TOKEN_COLON => '\':\'', + self::TOKEN_VARIADIC => '\'...\'', + self::TOKEN_DOUBLE_COLON => '\'::\'', + self::TOKEN_DOUBLE_ARROW => '\'=>\'', + self::TOKEN_EQUAL => '\'=\'', + self::TOKEN_OPEN_PHPDOC => '\'/**\'', + self::TOKEN_CLOSE_PHPDOC => '\'*/\'', + self::TOKEN_PHPDOC_TAG => 'TOKEN_PHPDOC_TAG', + self::TOKEN_PHPDOC_EOL => 'TOKEN_PHPDOC_EOL', + self::TOKEN_FLOAT => 'TOKEN_FLOAT', + self::TOKEN_INTEGER => 'TOKEN_INTEGER', + self::TOKEN_SINGLE_QUOTED_STRING => 'TOKEN_SINGLE_QUOTED_STRING', + self::TOKEN_DOUBLE_QUOTED_STRING => 'TOKEN_DOUBLE_QUOTED_STRING', + self::TOKEN_IDENTIFIER => 'type', + self::TOKEN_THIS_VARIABLE => '\'$this\'', + self::TOKEN_VARIABLE => 'variable', + self::TOKEN_HORIZONTAL_WS => 'TOKEN_HORIZONTAL_WS', + self::TOKEN_OTHER => 'TOKEN_OTHER', + self::TOKEN_END => 'TOKEN_END', + self::TOKEN_WILDCARD => '*', + ]; + + public const VALUE_OFFSET = 0; + public const TYPE_OFFSET = 1; + + /** @var string|null */ + private $regexp; + + /** @var int[]|null */ + private $types; + + public function tokenize(string $s): array + { + if ($this->regexp === null || $this->types === null) { + $this->initialize(); + } + + assert($this->regexp !== null); + assert($this->types !== null); + + preg_match_all($this->regexp, $s, $tokens, PREG_SET_ORDER); + + $count = count($this->types); + foreach ($tokens as &$match) { + for ($i = 1; $i <= $count; $i++) { + if ($match[$i] !== null && $match[$i] !== '') { + $match = [$match[0], $this->types[$i - 1]]; + break; + } + } + } + + $tokens[] = ['', self::TOKEN_END]; + + return $tokens; + } + + + private function initialize(): void + { + $patterns = [ + // '&' followed by TOKEN_VARIADIC, TOKEN_VARIABLE, TOKEN_EQUAL, TOKEN_EQUAL or TOKEN_CLOSE_PARENTHESES + self::TOKEN_REFERENCE => '&(?=\\s*+(?:[.,=)]|(?:\\$(?!this(?![0-9a-z_\\x80-\\xFF])))))', + self::TOKEN_UNION => '\\|', + self::TOKEN_INTERSECTION => '&', + self::TOKEN_NULLABLE => '\\?', + + self::TOKEN_OPEN_PARENTHESES => '\\(', + self::TOKEN_CLOSE_PARENTHESES => '\\)', + self::TOKEN_OPEN_ANGLE_BRACKET => '<', + self::TOKEN_CLOSE_ANGLE_BRACKET => '>', + self::TOKEN_OPEN_SQUARE_BRACKET => '\\[', + self::TOKEN_CLOSE_SQUARE_BRACKET => '\\]', + self::TOKEN_OPEN_CURLY_BRACKET => '\\{', + self::TOKEN_CLOSE_CURLY_BRACKET => '\\}', + + self::TOKEN_COMMA => ',', + self::TOKEN_VARIADIC => '\\.\\.\\.', + self::TOKEN_DOUBLE_COLON => '::', + self::TOKEN_DOUBLE_ARROW => '=>', + self::TOKEN_EQUAL => '=', + self::TOKEN_COLON => ':', + + self::TOKEN_OPEN_PHPDOC => '/\\*\\*(?=\\s)', + self::TOKEN_CLOSE_PHPDOC => '\\*/', + self::TOKEN_PHPDOC_TAG => '@[a-z-]++', + self::TOKEN_PHPDOC_EOL => '\\r?+\\n[\\x09\\x20]*+(?:\\*(?!/))?', + + self::TOKEN_FLOAT => '(?:-?[0-9]++\\.[0-9]*+(?:e-?[0-9]++)?)|(?:-?[0-9]*+\\.[0-9]++(?:e-?[0-9]++)?)|(?:-?[0-9]++e-?[0-9]++)', + self::TOKEN_INTEGER => '-?(?:(?:0b[0-1]++)|(?:0o[0-7]++)|(?:0x[0-9a-f]++)|(?:[0-9]++))', + self::TOKEN_SINGLE_QUOTED_STRING => '\'(?:\\\\[^\\r\\n]|[^\'\\r\\n\\\\])*+\'', + self::TOKEN_DOUBLE_QUOTED_STRING => '"(?:\\\\[^\\r\\n]|[^"\\r\\n\\\\])*+"', + + self::TOKEN_IDENTIFIER => '(?:[\\\\]?+[a-z_\\x80-\\xFF][0-9a-z_\\x80-\\xFF-]*+)++', + self::TOKEN_THIS_VARIABLE => '\\$this(?![0-9a-z_\\x80-\\xFF])', + self::TOKEN_VARIABLE => '\\$[a-z_\\x80-\\xFF][0-9a-z_\\x80-\\xFF]*+', + + self::TOKEN_HORIZONTAL_WS => '[\\x09\\x20]++', + + self::TOKEN_WILDCARD => '\\*', + + // anything but TOKEN_CLOSE_PHPDOC or TOKEN_HORIZONTAL_WS or TOKEN_EOL + self::TOKEN_OTHER => '(?:(?!\\*/)[^\\s])++', + ]; + + $this->regexp = '~(' . implode(')|(', $patterns) . ')~Asi'; + $this->types = array_keys($patterns); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Parser/ConstExprParser.php b/app/vendor/phpstan/phpdoc-parser/src/Parser/ConstExprParser.php new file mode 100644 index 000000000..012a0cbe0 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Parser/ConstExprParser.php @@ -0,0 +1,113 @@ +isCurrentTokenType(Lexer::TOKEN_FLOAT)) { + $value = $tokens->currentTokenValue(); + $tokens->next(); + return new Ast\ConstExpr\ConstExprFloatNode($value); + + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) { + $value = $tokens->currentTokenValue(); + $tokens->next(); + return new Ast\ConstExpr\ConstExprIntegerNode($value); + + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) { + $value = $tokens->currentTokenValue(); + if ($trimStrings) { + $value = trim($tokens->currentTokenValue(), "'"); + } + $tokens->next(); + return new Ast\ConstExpr\ConstExprStringNode($value); + + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) { + $value = $tokens->currentTokenValue(); + if ($trimStrings) { + $value = trim($tokens->currentTokenValue(), '"'); + } + $tokens->next(); + return new Ast\ConstExpr\ConstExprStringNode($value); + + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { + $identifier = $tokens->currentTokenValue(); + $tokens->next(); + + switch (strtolower($identifier)) { + case 'true': + return new Ast\ConstExpr\ConstExprTrueNode(); + case 'false': + return new Ast\ConstExpr\ConstExprFalseNode(); + case 'null': + return new Ast\ConstExpr\ConstExprNullNode(); + case 'array': + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); + return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_PARENTHESES); + } + + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_COLON)) { + $classConstantName = ''; + if ($tokens->currentTokenType() === Lexer::TOKEN_IDENTIFIER) { + $classConstantName .= $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) { + $classConstantName .= '*'; + } + } else { + $tokens->consumeTokenType(Lexer::TOKEN_WILDCARD); + $classConstantName .= '*'; + } + + return new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName); + + } + + return new Ast\ConstExpr\ConstFetchNode('', $identifier); + + } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_SQUARE_BRACKET); + } + + throw new \LogicException($tokens->currentTokenValue()); + } + + + private function parseArray(TokenIterator $tokens, int $endToken): Ast\ConstExpr\ConstExprArrayNode + { + $items = []; + + if (!$tokens->tryConsumeTokenType($endToken)) { + do { + $items[] = $this->parseArrayItem($tokens); + } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA) && !$tokens->isCurrentTokenType($endToken)); + $tokens->consumeTokenType($endToken); + } + + return new Ast\ConstExpr\ConstExprArrayNode($items); + } + + + private function parseArrayItem(TokenIterator $tokens): Ast\ConstExpr\ConstExprArrayItemNode + { + $expr = $this->parse($tokens); + + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_ARROW)) { + $key = $expr; + $value = $this->parse($tokens); + + } else { + $key = null; + $value = $expr; + } + + return new Ast\ConstExpr\ConstExprArrayItemNode($key, $value); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Parser/ParserException.php b/app/vendor/phpstan/phpdoc-parser/src/Parser/ParserException.php new file mode 100644 index 000000000..d6fd6feb8 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Parser/ParserException.php @@ -0,0 +1,69 @@ +currentTokenValue = $currentTokenValue; + $this->currentTokenType = $currentTokenType; + $this->currentOffset = $currentOffset; + $this->expectedTokenType = $expectedTokenType; + + $json = json_encode($currentTokenValue, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + assert($json !== false); + + parent::__construct(sprintf( + 'Unexpected token %s, expected %s at offset %d', + $json, + Lexer::TOKEN_LABELS[$expectedTokenType], + $currentOffset + )); + } + + + public function getCurrentTokenValue(): string + { + return $this->currentTokenValue; + } + + + public function getCurrentTokenType(): int + { + return $this->currentTokenType; + } + + + public function getCurrentOffset(): int + { + return $this->currentOffset; + } + + + public function getExpectedTokenType(): int + { + return $this->expectedTokenType; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Parser/PhpDocParser.php b/app/vendor/phpstan/phpdoc-parser/src/Parser/PhpDocParser.php new file mode 100644 index 000000000..a148da661 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Parser/PhpDocParser.php @@ -0,0 +1,408 @@ +typeParser = $typeParser; + $this->constantExprParser = $constantExprParser; + } + + + public function parse(TokenIterator $tokens): Ast\PhpDoc\PhpDocNode + { + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PHPDOC); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + + $children = []; + + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { + $children[] = $this->parseChild($tokens); + while ($tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL) && !$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { + $children[] = $this->parseChild($tokens); + } + } + + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PHPDOC); + + return new Ast\PhpDoc\PhpDocNode(array_values($children)); + } + + + private function parseChild(TokenIterator $tokens): Ast\PhpDoc\PhpDocChildNode + { + if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG)) { + return $this->parseTag($tokens); + + } + + return $this->parseText($tokens); + } + + + private function parseText(TokenIterator $tokens): Ast\PhpDoc\PhpDocTextNode + { + $text = ''; + while (true) { + // If we received a Lexer::TOKEN_PHPDOC_EOL, exit early to prevent + // them from being processed. + $currentTokenType = $tokens->currentTokenType(); + if ($currentTokenType === Lexer::TOKEN_PHPDOC_EOL) { + break; + } + $text .= $tokens->joinUntil(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END); + $text = rtrim($text, " \t"); + + // If we joined until TOKEN_PHPDOC_EOL, peak at the next tokens to see + // if we have a multiline string to join. + $currentTokenType = $tokens->currentTokenType(); + if ($currentTokenType !== Lexer::TOKEN_PHPDOC_EOL) { + break; + } + + // Peek at the next token to determine if it is more text that needs + // to be combined. + $tokens->pushSavePoint(); + $tokens->next(); + $currentTokenType = $tokens->currentTokenType(); + if ($currentTokenType !== Lexer::TOKEN_IDENTIFIER) { + $tokens->rollback(); + break; + } + + // There's more text on a new line, ensure spacing. + $text .= "\n"; + } + $text = trim($text, " \t"); + + return new Ast\PhpDoc\PhpDocTextNode($text); + } + + + public function parseTag(TokenIterator $tokens): Ast\PhpDoc\PhpDocTagNode + { + $tag = $tokens->currentTokenValue(); + $tokens->next(); + $value = $this->parseTagValue($tokens, $tag); + + return new Ast\PhpDoc\PhpDocTagNode($tag, $value); + } + + + public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\PhpDocTagValueNode + { + try { + $tokens->pushSavePoint(); + + switch ($tag) { + case '@param': + case '@phpstan-param': + case '@psalm-param': + $tagValue = $this->parseParamTagValue($tokens); + break; + + case '@var': + case '@phpstan-var': + case '@psalm-var': + $tagValue = $this->parseVarTagValue($tokens); + break; + + case '@return': + case '@phpstan-return': + case '@psalm-return': + $tagValue = $this->parseReturnTagValue($tokens); + break; + + case '@throws': + case '@phpstan-throws': + $tagValue = $this->parseThrowsTagValue($tokens); + break; + + case '@mixin': + $tagValue = $this->parseMixinTagValue($tokens); + break; + + case '@deprecated': + $tagValue = $this->parseDeprecatedTagValue($tokens); + break; + + case '@property': + case '@property-read': + case '@property-write': + case '@phpstan-property': + case '@phpstan-property-read': + case '@phpstan-property-write': + case '@psalm-property': + case '@psalm-property-read': + case '@psalm-property-write': + $tagValue = $this->parsePropertyTagValue($tokens); + break; + + case '@method': + case '@phpstan-method': + case '@psalm-method': + $tagValue = $this->parseMethodTagValue($tokens); + break; + + case '@template': + case '@phpstan-template': + case '@psalm-template': + case '@template-covariant': + case '@phpstan-template-covariant': + case '@psalm-template-covariant': + $tagValue = $this->parseTemplateTagValue($tokens); + break; + + case '@extends': + case '@phpstan-extends': + case '@template-extends': + $tagValue = $this->parseExtendsTagValue('@extends', $tokens); + break; + + case '@implements': + case '@phpstan-implements': + case '@template-implements': + $tagValue = $this->parseExtendsTagValue('@implements', $tokens); + break; + + case '@use': + case '@phpstan-use': + case '@template-use': + $tagValue = $this->parseExtendsTagValue('@use', $tokens); + break; + + default: + $tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescription($tokens)); + break; + } + + $tokens->dropSavePoint(); + + } catch (\PHPStan\PhpDocParser\Parser\ParserException $e) { + $tokens->rollback(); + $tagValue = new Ast\PhpDoc\InvalidTagValueNode($this->parseOptionalDescription($tokens), $e); + } + + return $tagValue; + } + + + private function parseParamTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamTagValueNode + { + $type = $this->typeParser->parse($tokens); + $isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC); + $parameterName = $this->parseRequiredVariableName($tokens); + $description = $this->parseOptionalDescription($tokens); + return new Ast\PhpDoc\ParamTagValueNode($type, $isVariadic, $parameterName, $description); + } + + + private function parseVarTagValue(TokenIterator $tokens): Ast\PhpDoc\VarTagValueNode + { + $type = $this->typeParser->parse($tokens); + $variableName = $this->parseOptionalVariableName($tokens); + $description = $this->parseOptionalDescription($tokens, $variableName === ''); + return new Ast\PhpDoc\VarTagValueNode($type, $variableName, $description); + } + + + private function parseReturnTagValue(TokenIterator $tokens): Ast\PhpDoc\ReturnTagValueNode + { + $type = $this->typeParser->parse($tokens); + $description = $this->parseOptionalDescription($tokens, true); + return new Ast\PhpDoc\ReturnTagValueNode($type, $description); + } + + + private function parseThrowsTagValue(TokenIterator $tokens): Ast\PhpDoc\ThrowsTagValueNode + { + $type = $this->typeParser->parse($tokens); + $description = $this->parseOptionalDescription($tokens, true); + return new Ast\PhpDoc\ThrowsTagValueNode($type, $description); + } + + private function parseMixinTagValue(TokenIterator $tokens): Ast\PhpDoc\MixinTagValueNode + { + $type = $this->typeParser->parse($tokens); + $description = $this->parseOptionalDescription($tokens, true); + return new Ast\PhpDoc\MixinTagValueNode($type, $description); + } + + private function parseDeprecatedTagValue(TokenIterator $tokens): Ast\PhpDoc\DeprecatedTagValueNode + { + $description = $this->parseOptionalDescription($tokens); + return new Ast\PhpDoc\DeprecatedTagValueNode($description); + } + + + private function parsePropertyTagValue(TokenIterator $tokens): Ast\PhpDoc\PropertyTagValueNode + { + $type = $this->typeParser->parse($tokens); + $parameterName = $this->parseRequiredVariableName($tokens); + $description = $this->parseOptionalDescription($tokens); + return new Ast\PhpDoc\PropertyTagValueNode($type, $parameterName, $description); + } + + + private function parseMethodTagValue(TokenIterator $tokens): Ast\PhpDoc\MethodTagValueNode + { + $isStatic = $tokens->tryConsumeTokenValue('static'); + $returnTypeOrMethodName = $this->typeParser->parse($tokens); + + if ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { + $returnType = $returnTypeOrMethodName; + $methodName = $tokens->currentTokenValue(); + $tokens->next(); + + } elseif ($returnTypeOrMethodName instanceof Ast\Type\IdentifierTypeNode) { + $returnType = $isStatic ? new Ast\Type\IdentifierTypeNode('static') : null; + $methodName = $returnTypeOrMethodName->name; + $isStatic = false; + + } else { + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); // will throw exception + exit; + } + + $parameters = []; + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { + $parameters[] = $this->parseMethodTagValueParameter($tokens); + while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { + $parameters[] = $this->parseMethodTagValueParameter($tokens); + } + } + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); + + $description = $this->parseOptionalDescription($tokens); + return new Ast\PhpDoc\MethodTagValueNode($isStatic, $returnType, $methodName, $parameters, $description); + } + + + private function parseMethodTagValueParameter(TokenIterator $tokens): Ast\PhpDoc\MethodTagValueParameterNode + { + switch ($tokens->currentTokenType()) { + case Lexer::TOKEN_IDENTIFIER: + case Lexer::TOKEN_OPEN_PARENTHESES: + case Lexer::TOKEN_NULLABLE: + $parameterType = $this->typeParser->parse($tokens); + break; + + default: + $parameterType = null; + } + + $isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE); + $isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC); + + $parameterName = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_VARIABLE); + + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL)) { + $defaultValue = $this->constantExprParser->parse($tokens); + + } else { + $defaultValue = null; + } + + return new Ast\PhpDoc\MethodTagValueParameterNode($parameterType, $isReference, $isVariadic, $parameterName, $defaultValue); + } + + private function parseTemplateTagValue(TokenIterator $tokens): Ast\PhpDoc\TemplateTagValueNode + { + $name = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + + if ($tokens->tryConsumeTokenValue('of') || $tokens->tryConsumeTokenValue('as')) { + $bound = $this->typeParser->parse($tokens); + + } else { + $bound = null; + } + + $description = $this->parseOptionalDescription($tokens); + + return new Ast\PhpDoc\TemplateTagValueNode($name, $bound, $description); + } + + private function parseExtendsTagValue(string $tagName, TokenIterator $tokens): Ast\PhpDoc\PhpDocTagValueNode + { + $baseType = new IdentifierTypeNode($tokens->currentTokenValue()); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + + $type = $this->typeParser->parseGeneric($tokens, $baseType); + + $description = $this->parseOptionalDescription($tokens); + + switch ($tagName) { + case '@extends': + return new Ast\PhpDoc\ExtendsTagValueNode($type, $description); + case '@implements': + return new Ast\PhpDoc\ImplementsTagValueNode($type, $description); + case '@use': + return new Ast\PhpDoc\UsesTagValueNode($type, $description); + } + + throw new \PHPStan\ShouldNotHappenException(); + } + + private function parseOptionalVariableName(TokenIterator $tokens): string + { + if ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) { + $parameterName = $tokens->currentTokenValue(); + $tokens->next(); + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_THIS_VARIABLE)) { + $parameterName = '$this'; + $tokens->next(); + + } else { + $parameterName = ''; + } + + return $parameterName; + } + + + private function parseRequiredVariableName(TokenIterator $tokens): string + { + $parameterName = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_VARIABLE); + + return $parameterName; + } + + + private function parseOptionalDescription(TokenIterator $tokens, bool $limitStartToken = false): string + { + if ($limitStartToken) { + foreach (self::DISALLOWED_DESCRIPTION_START_TOKENS as $disallowedStartToken) { + if (!$tokens->isCurrentTokenType($disallowedStartToken)) { + continue; + } + + $tokens->consumeTokenType(Lexer::TOKEN_OTHER); // will throw exception + } + } + + return $this->parseText($tokens)->text; + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php b/app/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php new file mode 100644 index 000000000..05fe5d7fd --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php @@ -0,0 +1,191 @@ +tokens = $tokens; + $this->index = $index; + + if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== Lexer::TOKEN_HORIZONTAL_WS) { + return; + } + + $this->index++; + } + + + public function currentTokenValue(): string + { + return $this->tokens[$this->index][Lexer::VALUE_OFFSET]; + } + + + public function currentTokenType(): int + { + return $this->tokens[$this->index][Lexer::TYPE_OFFSET]; + } + + + public function currentTokenOffset(): int + { + $offset = 0; + for ($i = 0; $i < $this->index; $i++) { + $offset += strlen($this->tokens[$i][Lexer::VALUE_OFFSET]); + } + + return $offset; + } + + + public function isCurrentTokenValue(string $tokenValue): bool + { + return $this->tokens[$this->index][Lexer::VALUE_OFFSET] === $tokenValue; + } + + + public function isCurrentTokenType(int $tokenType): bool + { + return $this->tokens[$this->index][Lexer::TYPE_OFFSET] === $tokenType; + } + + + public function isPrecededByHorizontalWhitespace(): bool + { + return ($this->tokens[$this->index - 1][Lexer::TYPE_OFFSET] ?? -1) === Lexer::TOKEN_HORIZONTAL_WS; + } + + + /** + * @param int $tokenType + * @throws \PHPStan\PhpDocParser\Parser\ParserException + */ + public function consumeTokenType(int $tokenType): void + { + if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType) { + $this->throwError($tokenType); + } + + $this->index++; + + if (($this->tokens[$this->index][Lexer::TYPE_OFFSET] ?? -1) !== Lexer::TOKEN_HORIZONTAL_WS) { + return; + } + + $this->index++; + } + + + public function tryConsumeTokenValue(string $tokenValue): bool + { + if ($this->tokens[$this->index][Lexer::VALUE_OFFSET] !== $tokenValue) { + return false; + } + + $this->index++; + + if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) { + $this->index++; + } + + return true; + } + + + public function tryConsumeTokenType(int $tokenType): bool + { + if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType) { + return false; + } + + $this->index++; + + if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) { + $this->index++; + } + + return true; + } + + + public function getSkippedHorizontalWhiteSpaceIfAny(): string + { + if ($this->tokens[$this->index - 1][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) { + return $this->tokens[$this->index - 1][Lexer::VALUE_OFFSET]; + } + + return ''; + } + + + public function joinUntil(int ...$tokenType): string + { + $s = ''; + while (!in_array($this->tokens[$this->index][Lexer::TYPE_OFFSET], $tokenType, true)) { + $s .= $this->tokens[$this->index++][Lexer::VALUE_OFFSET]; + } + return $s; + } + + + public function next(): void + { + $this->index++; + + if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== Lexer::TOKEN_HORIZONTAL_WS) { + return; + } + + $this->index++; + } + + + public function pushSavePoint(): void + { + $this->savePoints[] = $this->index; + } + + + public function dropSavePoint(): void + { + array_pop($this->savePoints); + } + + + public function rollback(): void + { + $index = array_pop($this->savePoints); + assert($index !== null); + $this->index = $index; + } + + + /** + * @param int $expectedTokenType + * @throws \PHPStan\PhpDocParser\Parser\ParserException + */ + private function throwError(int $expectedTokenType): void + { + throw new \PHPStan\PhpDocParser\Parser\ParserException( + $this->currentTokenValue(), + $this->currentTokenType(), + $this->currentTokenOffset(), + $expectedTokenType + ); + } + +} diff --git a/app/vendor/phpstan/phpdoc-parser/src/Parser/TypeParser.php b/app/vendor/phpstan/phpdoc-parser/src/Parser/TypeParser.php new file mode 100644 index 000000000..c123ee3f5 --- /dev/null +++ b/app/vendor/phpstan/phpdoc-parser/src/Parser/TypeParser.php @@ -0,0 +1,399 @@ +constExprParser = $constExprParser; + } + + public function parse(TokenIterator $tokens): Ast\Type\TypeNode + { + if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) { + $type = $this->parseNullable($tokens); + + } else { + $type = $this->parseAtomic($tokens); + + if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) { + $type = $this->parseUnion($tokens, $type); + + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) { + $type = $this->parseIntersection($tokens, $type); + } + } + + return $type; + } + + + private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode + { + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { + $type = $this->parse($tokens); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); + + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + return $this->tryParseArray($tokens, $type); + } + + return $type; + } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) { + $type = new Ast\Type\ThisTypeNode(); + + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + return $this->tryParseArray($tokens, $type); + } + + return $type; + } + + $currentTokenValue = $tokens->currentTokenValue(); + $tokens->pushSavePoint(); // because of ConstFetchNode + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) { + $type = new Ast\Type\IdentifierTypeNode($currentTokenValue); + + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) { + $tokens->dropSavePoint(); // because of ConstFetchNode + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) { + $tokens->pushSavePoint(); + + $isHtml = $this->isHtml($tokens); + $tokens->rollback(); + if ($isHtml) { + return $type; + } + + $type = $this->parseGeneric($tokens, $type); + + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArray($tokens, $type); + } + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { + $type = $this->tryParseCallable($tokens, $type); + + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArray($tokens, $type); + + } elseif ($type->name === 'array' && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) { + $type = $this->parseArrayShape($tokens, $type); + + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArray($tokens, $type); + } + } + + return $type; + } else { + $tokens->rollback(); // because of ConstFetchNode + } + } else { + $tokens->dropSavePoint(); // because of ConstFetchNode + } + + $exception = new \PHPStan\PhpDocParser\Parser\ParserException( + $tokens->currentTokenValue(), + $tokens->currentTokenType(), + $tokens->currentTokenOffset(), + Lexer::TOKEN_IDENTIFIER + ); + + if ($this->constExprParser === null) { + throw $exception; + } + + try { + $constExpr = $this->constExprParser->parse($tokens, true); + if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) { + throw $exception; + } + + return new Ast\Type\ConstTypeNode($constExpr); + } catch (\LogicException $e) { + throw $exception; + } + } + + + private function parseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode + { + $types = [$type]; + + while ($tokens->tryConsumeTokenType(Lexer::TOKEN_UNION)) { + $types[] = $this->parseAtomic($tokens); + } + + return new Ast\Type\UnionTypeNode($types); + } + + + private function parseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode + { + $types = [$type]; + + while ($tokens->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) { + $types[] = $this->parseAtomic($tokens); + } + + return new Ast\Type\IntersectionTypeNode($types); + } + + + private function parseNullable(TokenIterator $tokens): Ast\Type\TypeNode + { + $tokens->consumeTokenType(Lexer::TOKEN_NULLABLE); + + $type = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue()); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) { + $type = $this->parseGeneric($tokens, $type); + + } elseif ($type->name === 'array' && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) { + $type = $this->parseArrayShape($tokens, $type); + } + + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArray($tokens, $type); + } + + return new Ast\Type\NullableTypeNode($type); + } + + public function isHtml(TokenIterator $tokens): bool + { + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET); + + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { + return false; + } + + $htmlTagName = $tokens->currentTokenValue(); + + $tokens->next(); + + if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) { + return false; + } + + while (!$tokens->isCurrentTokenType(Lexer::TOKEN_END)) { + if ( + $tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET) + && strpos($tokens->currentTokenValue(), '/' . $htmlTagName . '>') !== false + ) { + return true; + } + + $tokens->next(); + } + + return false; + } + + public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $baseType): Ast\Type\GenericTypeNode + { + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $genericTypes = [$this->parse($tokens)]; + + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + + while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) { + // trailing comma case + return new Ast\Type\GenericTypeNode($baseType, $genericTypes); + } + $genericTypes[] = $this->parse($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + } + + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET); + + return new Ast\Type\GenericTypeNode($baseType, $genericTypes); + } + + + private function parseCallable(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $identifier): Ast\Type\TypeNode + { + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); + + $parameters = []; + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { + $parameters[] = $this->parseCallableParameter($tokens); + while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { + $parameters[] = $this->parseCallableParameter($tokens); + } + } + + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); + $tokens->consumeTokenType(Lexer::TOKEN_COLON); + $returnType = $this->parseCallableReturnType($tokens); + + return new Ast\Type\CallableTypeNode($identifier, $parameters, $returnType); + } + + + private function parseCallableParameter(TokenIterator $tokens): Ast\Type\CallableTypeParameterNode + { + $type = $this->parse($tokens); + $isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE); + $isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC); + + if ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) { + $parameterName = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_VARIABLE); + + } else { + $parameterName = ''; + } + + $isOptional = $tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL); + return new Ast\Type\CallableTypeParameterNode($type, $isReference, $isVariadic, $parameterName, $isOptional); + } + + + private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNode + { + if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) { + $type = $this->parseNullable($tokens); + + } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { + $type = $this->parse($tokens); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); + + } else { + $type = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue()); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) { + $type = $this->parseGeneric($tokens, $type); + + } elseif ($type->name === 'array' && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) { + $type = $this->parseArrayShape($tokens, $type); + } + } + + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArray($tokens, $type); + } + + return $type; + } + + + private function tryParseCallable(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $identifier): Ast\Type\TypeNode + { + try { + $tokens->pushSavePoint(); + $type = $this->parseCallable($tokens, $identifier); + $tokens->dropSavePoint(); + + } catch (\PHPStan\PhpDocParser\Parser\ParserException $e) { + $tokens->rollback(); + $type = $identifier; + } + + return $type; + } + + + private function tryParseArray(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode + { + try { + while ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $tokens->pushSavePoint(); + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET); + $tokens->dropSavePoint(); + $type = new Ast\Type\ArrayTypeNode($type); + } + + } catch (\PHPStan\PhpDocParser\Parser\ParserException $e) { + $tokens->rollback(); + } + + return $type; + } + + + private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\ArrayShapeNode + { + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $items = [$this->parseArrayShapeItem($tokens)]; + + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { + // trailing comma case + return new Ast\Type\ArrayShapeNode($items); + } + + $items[] = $this->parseArrayShapeItem($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + } + + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET); + + return new Ast\Type\ArrayShapeNode($items); + } + + + private function parseArrayShapeItem(TokenIterator $tokens): Ast\Type\ArrayShapeItemNode + { + try { + $tokens->pushSavePoint(); + $key = $this->parseArrayShapeKey($tokens); + $optional = $tokens->tryConsumeTokenType(Lexer::TOKEN_NULLABLE); + $tokens->consumeTokenType(Lexer::TOKEN_COLON); + $value = $this->parse($tokens); + $tokens->dropSavePoint(); + + return new Ast\Type\ArrayShapeItemNode($key, $optional, $value); + } catch (\PHPStan\PhpDocParser\Parser\ParserException $e) { + $tokens->rollback(); + $value = $this->parse($tokens); + + return new Ast\Type\ArrayShapeItemNode(null, false, $value); + } + } + + /** + * @return Ast\ConstExpr\ConstExprIntegerNode|Ast\ConstExpr\ConstExprStringNode|Ast\Type\IdentifierTypeNode + */ + private function parseArrayShapeKey(TokenIterator $tokens) + { + if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) { + $key = new Ast\ConstExpr\ConstExprIntegerNode($tokens->currentTokenValue()); + $tokens->next(); + + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) { + $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'")); + $tokens->next(); + + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) { + $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"')); + $tokens->next(); + + } else { + $key = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue()); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + } + + return $key; + } + +} diff --git a/app/vendor/phpunit/php-code-coverage/.gitattributes b/app/vendor/phpunit/php-code-coverage/.gitattributes deleted file mode 100644 index 34d242ba9..000000000 --- a/app/vendor/phpunit/php-code-coverage/.gitattributes +++ /dev/null @@ -1,3 +0,0 @@ -/tools export-ignore - -*.php diff=php diff --git a/app/vendor/phpunit/php-code-coverage/.github/CONTRIBUTING.md b/app/vendor/phpunit/php-code-coverage/.github/CONTRIBUTING.md deleted file mode 100644 index 33392505b..000000000 --- a/app/vendor/phpunit/php-code-coverage/.github/CONTRIBUTING.md +++ /dev/null @@ -1 +0,0 @@ -Please refer to [https://github.com/sebastianbergmann/phpunit/blob/master/CONTRIBUTING.md](https://github.com/sebastianbergmann/phpunit/blob/master/.github/CONTRIBUTING.md) for details on how to contribute to this project. diff --git a/app/vendor/phpunit/php-code-coverage/.github/FUNDING.yml b/app/vendor/phpunit/php-code-coverage/.github/FUNDING.yml deleted file mode 100644 index c2fba0f90..000000000 --- a/app/vendor/phpunit/php-code-coverage/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: sebastianbergmann diff --git a/app/vendor/phpunit/php-code-coverage/.github/ISSUE_TEMPLATE.md b/app/vendor/phpunit/php-code-coverage/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index dc8e3b02f..000000000 --- a/app/vendor/phpunit/php-code-coverage/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,18 +0,0 @@ -| Q | A -| --------------------------| --------------- -| php-code-coverage version | x.y.z -| PHP version | x.y.z -| Driver | Xdebug / PHPDBG -| Xdebug version (if used) | x.y.z -| Installation Method | Composer / PHPUnit PHAR -| Usage Method | PHPUnit / other -| PHPUnit version (if used) | x.y.z - - - diff --git a/app/vendor/phpunit/php-code-coverage/.gitignore b/app/vendor/phpunit/php-code-coverage/.gitignore deleted file mode 100644 index 3c77ffd8a..000000000 --- a/app/vendor/phpunit/php-code-coverage/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -/tests/_files/tmp -/vendor -/composer.lock -/.idea -/.php_cs -/.php_cs.cache -/.phpunit.result.cache diff --git a/app/vendor/phpunit/php-code-coverage/.php_cs.dist b/app/vendor/phpunit/php-code-coverage/.php_cs.dist deleted file mode 100644 index cc2064449..000000000 --- a/app/vendor/phpunit/php-code-coverage/.php_cs.dist +++ /dev/null @@ -1,197 +0,0 @@ - - -For the full copyright and license information, please view the LICENSE -file that was distributed with this source code. -EOF; - -return PhpCsFixer\Config::create() - ->setRiskyAllowed(true) - ->setRules( - [ - 'align_multiline_comment' => true, - 'array_indentation' => true, - 'array_syntax' => ['syntax' => 'short'], - 'binary_operator_spaces' => [ - 'operators' => [ - '=' => 'align', - '=>' => 'align', - ], - ], - 'blank_line_after_namespace' => true, - 'blank_line_before_statement' => [ - 'statements' => [ - 'break', - 'continue', - 'declare', - 'do', - 'for', - 'foreach', - 'if', - 'include', - 'include_once', - 'require', - 'require_once', - 'return', - 'switch', - 'throw', - 'try', - 'while', - 'yield', - ], - ], - 'braces' => true, - 'cast_spaces' => true, - 'class_attributes_separation' => ['elements' => ['const', 'method', 'property']], - 'combine_consecutive_issets' => true, - 'combine_consecutive_unsets' => true, - 'compact_nullable_typehint' => true, - 'concat_space' => ['spacing' => 'one'], - 'declare_equal_normalize' => ['space' => 'none'], - 'declare_strict_types' => true, - 'dir_constant' => true, - 'elseif' => true, - 'encoding' => true, - 'full_opening_tag' => true, - 'function_declaration' => true, - 'header_comment' => ['header' => $header, 'separate' => 'none'], - 'indentation_type' => true, - 'is_null' => true, - 'line_ending' => true, - 'list_syntax' => ['syntax' => 'short'], - 'logical_operators' => true, - 'lowercase_cast' => true, - 'lowercase_constants' => true, - 'lowercase_keywords' => true, - 'lowercase_static_reference' => true, - 'magic_constant_casing' => true, - 'method_argument_space' => ['ensure_fully_multiline' => true], - 'modernize_types_casting' => true, - 'multiline_comment_opening_closing' => true, - 'multiline_whitespace_before_semicolons' => true, - 'native_constant_invocation' => true, - 'native_function_casing' => true, - 'native_function_invocation' => true, - 'new_with_braces' => false, - 'no_alias_functions' => true, - 'no_alternative_syntax' => true, - 'no_blank_lines_after_class_opening' => true, - 'no_blank_lines_after_phpdoc' => true, - 'no_blank_lines_before_namespace' => true, - 'no_closing_tag' => true, - 'no_empty_comment' => true, - 'no_empty_phpdoc' => true, - 'no_empty_statement' => true, - 'no_extra_blank_lines' => true, - 'no_homoglyph_names' => true, - 'no_leading_import_slash' => true, - 'no_leading_namespace_whitespace' => true, - 'no_mixed_echo_print' => ['use' => 'print'], - 'no_multiline_whitespace_around_double_arrow' => true, - 'no_null_property_initialization' => true, - 'no_php4_constructor' => true, - 'no_short_bool_cast' => true, - 'no_short_echo_tag' => true, - 'no_singleline_whitespace_before_semicolons' => true, - 'no_spaces_after_function_name' => true, - 'no_spaces_inside_parenthesis' => true, - 'no_superfluous_elseif' => true, - 'no_superfluous_phpdoc_tags' => true, - 'no_trailing_comma_in_list_call' => true, - 'no_trailing_comma_in_singleline_array' => true, - 'no_trailing_whitespace' => true, - 'no_trailing_whitespace_in_comment' => true, - 'no_unneeded_control_parentheses' => true, - 'no_unneeded_curly_braces' => true, - 'no_unneeded_final_method' => true, - 'no_unreachable_default_argument_value' => true, - 'no_unset_on_property' => true, - 'no_unused_imports' => true, - 'no_useless_else' => true, - 'no_useless_return' => true, - 'no_whitespace_before_comma_in_array' => true, - 'no_whitespace_in_blank_line' => true, - 'non_printable_character' => true, - 'normalize_index_brace' => true, - 'object_operator_without_whitespace' => true, - 'ordered_class_elements' => [ - 'order' => [ - 'use_trait', - 'constant_public', - 'constant_protected', - 'constant_private', - 'property_public_static', - 'property_protected_static', - 'property_private_static', - 'property_public', - 'property_protected', - 'property_private', - 'method_public_static', - 'construct', - 'destruct', - 'magic', - 'phpunit', - 'method_public', - 'method_protected', - 'method_private', - 'method_protected_static', - 'method_private_static', - ], - ], - 'ordered_imports' => true, - 'phpdoc_add_missing_param_annotation' => true, - 'phpdoc_align' => true, - 'phpdoc_annotation_without_dot' => true, - 'phpdoc_indent' => true, - 'phpdoc_no_access' => true, - 'phpdoc_no_empty_return' => true, - 'phpdoc_no_package' => true, - 'phpdoc_order' => true, - 'phpdoc_return_self_reference' => true, - 'phpdoc_scalar' => true, - 'phpdoc_separation' => true, - 'phpdoc_single_line_var_spacing' => true, - 'phpdoc_to_comment' => true, - 'phpdoc_trim' => true, - 'phpdoc_trim_consecutive_blank_line_separation' => true, - 'phpdoc_types' => ['groups' => ['simple', 'meta']], - 'phpdoc_types_order' => true, - 'phpdoc_var_without_name' => true, - 'pow_to_exponentiation' => true, - 'protected_to_private' => true, - 'return_assignment' => true, - 'return_type_declaration' => ['space_before' => 'none'], - 'self_accessor' => true, - 'semicolon_after_instruction' => true, - 'set_type_to_cast' => true, - 'short_scalar_cast' => true, - 'simplified_null_return' => true, - 'single_blank_line_at_eof' => true, - 'single_import_per_statement' => true, - 'single_line_after_imports' => true, - 'single_quote' => true, - 'standardize_not_equals' => true, - 'ternary_to_null_coalescing' => true, - 'trailing_comma_in_multiline_array' => true, - 'trim_array_spaces' => true, - 'unary_operator_spaces' => true, - 'visibility_required' => [ - 'elements' => [ - 'const', - 'method', - 'property', - ], - ], - 'void_return' => true, - 'whitespace_after_comma_in_array' => true, - ] - ) - ->setFinder( - PhpCsFixer\Finder::create() - ->files() - ->in(__DIR__ . '/src') - ->in(__DIR__ . '/tests/tests') - ); diff --git a/app/vendor/phpunit/php-code-coverage/.travis.yml b/app/vendor/phpunit/php-code-coverage/.travis.yml deleted file mode 100644 index 1bf56482e..000000000 --- a/app/vendor/phpunit/php-code-coverage/.travis.yml +++ /dev/null @@ -1,60 +0,0 @@ -language: php - -php: - - 7.2 - - 7.3 - - 7.4snapshot - -matrix: - fast_finish: true - -env: - matrix: - - DRIVER="xdebug" DEPENDENCIES="high" - - DRIVER="phpdbg" DEPENDENCIES="high" - - DRIVER="pcov" DEPENDENCIES="high" - - DRIVER="xdebug" DEPENDENCIES="low" - - DRIVER="phpdbg" DEPENDENCIES="low" - - DRIVER="pcov" DEPENDENCIES="low" - global: - - DEFAULT_COMPOSER_FLAGS="--no-interaction --no-ansi --no-progress --no-suggest" - -before_install: - - ./tools/composer clear-cache - -install: - - if [[ "$DEPENDENCIES" = 'high' ]]; then travis_retry ./tools/composer update $DEFAULT_COMPOSER_FLAGS; fi - - if [[ "$DEPENDENCIES" = 'low' ]]; then travis_retry ./tools/composer update $DEFAULT_COMPOSER_FLAGS --prefer-lowest; fi - -before_script: - - | - if [[ "$DRIVER" = 'pcov' ]]; then - echo > $HOME/.phpenv/versions/$TRAVIS_PHP_VERSION/etc/conf.d/xdebug.ini - git clone --single-branch --branch=v1.0.6 --depth=1 https://github.com/krakjoe/pcov - cd pcov - phpize - ./configure - make clean install - echo "extension=pcov.so" > $HOME/.phpenv/versions/$TRAVIS_PHP_VERSION/etc/conf.d/pcov.ini - cd $TRAVIS_BUILD_DIR - fi - -script: - - if [[ "$DRIVER" = 'phpdbg' ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi - - if [[ "$DRIVER" != 'phpdbg' ]]; then vendor/bin/phpunit --coverage-clover=coverage.xml; fi - -after_success: - - bash <(curl -s https://codecov.io/bash) - -notifications: - email: false - -jobs: - include: - - stage: Static Code Analysis - php: 7.3 - env: php-cs-fixer - install: - - phpenv config-rm xdebug.ini - script: - - ./tools/php-cs-fixer fix --dry-run -v --show-progress=dots --diff-format=udiff diff --git a/app/vendor/phpunit/php-code-coverage/ChangeLog.md b/app/vendor/phpunit/php-code-coverage/ChangeLog.md index 2ac79e330..fe6bcfdec 100644 --- a/app/vendor/phpunit/php-code-coverage/ChangeLog.md +++ b/app/vendor/phpunit/php-code-coverage/ChangeLog.md @@ -2,6 +2,224 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. +## [9.2.7] - 2021-09-17 + +### Fixed + +* [#860](https://github.com/sebastianbergmann/php-code-coverage/pull/860): Empty value for `XDEBUG_MODE` environment variable is not handled correctly + +## [9.2.6] - 2021-03-28 + +### Fixed + +* [#846](https://github.com/sebastianbergmann/php-code-coverage/issues/846): Method name should not appear in the method signature attribute of Cobertura XML + +## [9.2.5] - 2020-11-28 + +### Fixed + +* [#831](https://github.com/sebastianbergmann/php-code-coverage/issues/831): Files that do not contain a newline are not handled correctly + +## [9.2.4] - 2020-11-27 + +### Added + +* [#834](https://github.com/sebastianbergmann/php-code-coverage/issues/834): Support `XDEBUG_MODE` environment variable + +## [9.2.3] - 2020-10-30 + +### Changed + +* Bumped required version of `nikic/php-parser` + +## [9.2.2] - 2020-10-28 + +### Fixed + +* [#820](https://github.com/sebastianbergmann/php-code-coverage/issues/820): Hidden dependency on PHPUnit + +## [9.2.1] - 2020-10-26 + +### Fixed + +* `SebastianBergmann\CodeCoverage\Exception` now correctly extends `\Throwable` + +## [9.2.0] - 2020-10-02 + +### Added + +* [#812](https://github.com/sebastianbergmann/php-code-coverage/pull/812): Support for Cobertura XML report format + +### Changed + +* Reduced the number of I/O operations performed by the static analysis cache + +## [9.1.11] - 2020-09-19 + +### Fixed + +* [#811](https://github.com/sebastianbergmann/php-code-coverage/issues/811): `T_FN` constant is used on PHP 7.3 where it is not available + +## [9.1.10] - 2020-09-18 + +### Added + +* `SebastianBergmann\CodeCoverage\Driver\Selector::forLineCoverage()` and `SebastianBergmann\CodeCoverage\Driver\Selector::forLineAndPathCoverage()` have been added + +### Fixed + +* [#810](https://github.com/sebastianbergmann/php-code-coverage/issues/810): `SebastianBergmann\CodeCoverage\Driver\Driver::forLineCoverage()` and `SebastianBergmann\CodeCoverage\Driver\Driver::forLineAndPathCoverage()` are marked as internal + +### Removed + +* `SebastianBergmann\CodeCoverage\Driver\Driver::forLineCoverage()` and `SebastianBergmann\CodeCoverage\Driver\Driver::forLineAndPathCoverage()` are now deprecated + +## [9.1.9] - 2020-09-15 + +### Fixed + +* [#808](https://github.com/sebastianbergmann/php-code-coverage/issues/808): `PHP Warning: Use of undefined constant T_MATCH` + +## [9.1.8] - 2020-09-07 + +### Changed + +* [#800](https://github.com/sebastianbergmann/php-code-coverage/pull/800): All files on the inclusion list are no longer loaded when `SebastianBergmann\CodeCoverage::start()` is called for the first time and `processUncoveredFiles` is set to `true` + +### Fixed + +* [#799](https://github.com/sebastianbergmann/php-code-coverage/issues/799): Uncovered new line at end of file + +## [9.1.7] - 2020-09-03 + +### Fixed + +* Fixed regressions introduced in versions 9.1.5 and 9.1.6 + +## [9.1.6] - 2020-08-31 + +### Fixed + +* [#799](https://github.com/sebastianbergmann/php-code-coverage/issues/799): Uncovered new line at end of file +* [#803](https://github.com/sebastianbergmann/php-code-coverage/issues/803): HTML report does not sort directories and files anymore + +## [9.1.5] - 2020-08-27 + +### Changed + +* [#800](https://github.com/sebastianbergmann/php-code-coverage/pull/800): All files on the inclusion list are no longer loaded when `SebastianBergmann\CodeCoverage::start()` is called for the first time and `processUncoveredFiles` is set to `true` + +### Fixed + +* [#797](https://github.com/sebastianbergmann/php-code-coverage/pull/797): Class name is wrongly removed from namespace name + +## [9.1.4] - 2020-08-13 + +### Fixed + +* [#793](https://github.com/sebastianbergmann/php-code-coverage/issues/793): Lines with `::class` constant are not covered + +## [9.1.3] - 2020-08-10 + +### Changed + +* Changed PHP-Parser usage to parse sourcecode according to the PHP version we are currently running on instead of using emulative lexing + +## [9.1.2] - 2020-08-10 + +### Fixed + +* [#791](https://github.com/sebastianbergmann/php-code-coverage/pull/791): Cache Warmer does not warm all caches + +## [9.1.1] - 2020-08-10 + +### Added + +* Added `SebastianBergmann\CodeCoverage::cacheDirectory()` method for querying where the cache writes its files + +## [9.1.0] - 2020-08-10 + +### Added + +* Implemented a persistent cache for information gathered using PHP-Parser based static analysis (hereinafter referred to as "cache") +* Added `SebastianBergmann\CodeCoverage::cacheStaticAnalysis(string $cacheDirectory)` method for enabling the cache; it will write its files to `$directory` +* Added `SebastianBergmann\CodeCoverage::doNotCacheStaticAnalysis` method for disabling the cache +* Added `SebastianBergmann\CodeCoverage::cachesStaticAnalysis()` method for querying whether the cache is enabled +* Added `SebastianBergmann\CodeCoverage\StaticAnalysis\CacheWarmer::warmCache()` method for warming the cache + +## [9.0.0] - 2020-08-07 + +### Added + +* [#761](https://github.com/sebastianbergmann/php-code-coverage/pull/761): Support for Branch Coverage and Path Coverage +* Added `SebastianBergmann\CodeCoverage\Driver\Driver::forLineCoverage()` for selecting the best available driver for line coverage +* Added `SebastianBergmann\CodeCoverage\Driver\Driver::forLineAndPathCoverage()` for selecting the best available driver for path coverage +* This component is now supported on PHP 8 +* This component now supports Xdebug 3 + +### Changed + +* [#746](https://github.com/sebastianbergmann/php-code-coverage/pull/746): Remove some ancient workarounds for very old Xdebug versions +* [#747](https://github.com/sebastianbergmann/php-code-coverage/pull/747): Use native filtering in PCOV and Xdebug drivers +* [#748](https://github.com/sebastianbergmann/php-code-coverage/pull/748): Store raw code coverage in value objects instead of arrays +* [#749](https://github.com/sebastianbergmann/php-code-coverage/pull/749): Store processed code coverage in value objects instead of arrays +* [#752](https://github.com/sebastianbergmann/php-code-coverage/pull/752): Rework how code coverage settings are propagated to the driver +* [#754](https://github.com/sebastianbergmann/php-code-coverage/pull/754): Implement collection of raw branch and path coverage +* [#755](https://github.com/sebastianbergmann/php-code-coverage/pull/755): Implement processing of raw branch and path coverage +* [#756](https://github.com/sebastianbergmann/php-code-coverage/pull/756): Improve handling of uncovered files +* `SebastianBergmann\CodeCoverage\Filter::addDirectoryToWhitelist()` has been renamed to `SebastianBergmann\CodeCoverage\Filter::includeDirectory()` +* `SebastianBergmann\CodeCoverage\Filter::addFilesToWhitelist()` has been renamed to `SebastianBergmann\CodeCoverage\Filter::includeFiles()` +* `SebastianBergmann\CodeCoverage\Filter::addFileToWhitelist()` has been renamed to `SebastianBergmann\CodeCoverage\Filter::includeFile()` +* `SebastianBergmann\CodeCoverage\Filter::removeDirectoryFromWhitelist()` has been renamed to `SebastianBergmann\CodeCoverage\Filter::excludeDirectory()` +* `SebastianBergmann\CodeCoverage\Filter::removeFileFromWhitelist()` has been renamed to `SebastianBergmann\CodeCoverage\Filter::excludeFile()` +* `SebastianBergmann\CodeCoverage\Filter::isFiltered()` has been renamed to `SebastianBergmann\CodeCoverage\Filter::isExcluded()` +* `SebastianBergmann\CodeCoverage\Filter::getWhitelist()` has been renamed to `SebastianBergmann\CodeCoverage\Filter::files()` +* The arguments for `CodeCoverage::__construct()` are no longer optional + +### Fixed + +* [#700](https://github.com/sebastianbergmann/php-code-coverage/pull/700): Throw an exception if code coverage fails to write to disk + +### Removed + +* `SebastianBergmann\CodeCoverage\CodeCoverage::setCacheTokens()` and `SebastianBergmann\CodeCoverage\CodeCoverage::getCacheTokens()` have been removed +* `SebastianBergmann\CodeCoverage\CodeCoverage::setCheckForUnintentionallyCoveredCode()` has been removed, please use `SebastianBergmann\CodeCoverage\CodeCoverage::enableCheckForUnintentionallyCoveredCode()` or `SebastianBergmann\CodeCoverage\CodeCoverage::disableCheckForUnintentionallyCoveredCode()` instead +* `SebastianBergmann\CodeCoverage\CodeCoverage::setSubclassesExcludedFromUnintentionallyCoveredCodeCheck()` has been removed, please use `SebastianBergmann\CodeCoverage\CodeCoverage::excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck()` instead +* `SebastianBergmann\CodeCoverage\CodeCoverage::setAddUncoveredFilesFromWhitelist()` has been removed, please use `SebastianBergmann\CodeCoverage\CodeCoverage::includeUncoveredFiles()` or `SebastianBergmann\CodeCoverage\CodeCoverage::excludeUncoveredFiles()` instead +* `SebastianBergmann\CodeCoverage\CodeCoverage::setProcessUncoveredFiles()` has been removed, please use `SebastianBergmann\CodeCoverage\CodeCoverage::processUncoveredFiles()` or `SebastianBergmann\CodeCoverage\CodeCoverage::doNotProcessUncoveredFiles()` instead +* `SebastianBergmann\CodeCoverage\CodeCoverage::setIgnoreDeprecatedCode()` has been removed, please use `SebastianBergmann\CodeCoverage\CodeCoverage::ignoreDeprecatedCode()` or `SebastianBergmann\CodeCoverage\CodeCoverage::doNotIgnoreDeprecatedCode()` instead +* `SebastianBergmann\CodeCoverage\CodeCoverage::setDisableIgnoredLines()` has been removed, please use `SebastianBergmann\CodeCoverage\CodeCoverage::enableAnnotationsForIgnoringCode()` or `SebastianBergmann\CodeCoverage\CodeCoverage::disableAnnotationsForIgnoringCode()` instead +* `SebastianBergmann\CodeCoverage\CodeCoverage::setCheckForMissingCoversAnnotation()` has been removed +* `SebastianBergmann\CodeCoverage\CodeCoverage::setCheckForUnexecutedCoveredCode()` has been removed +* `SebastianBergmann\CodeCoverage\CodeCoverage::setForceCoversAnnotation()` has been removed +* `SebastianBergmann\CodeCoverage\Filter::hasWhitelist()` has been removed, please use `SebastianBergmann\CodeCoverage\Filter::isEmpty()` instead +* `SebastianBergmann\CodeCoverage\Filter::getWhitelistedFiles()` has been removed +* `SebastianBergmann\CodeCoverage\Filter::setWhitelistedFiles()` has been removed + +## [8.0.2] - 2020-05-23 + +### Fixed + +* [#750](https://github.com/sebastianbergmann/php-code-coverage/pull/750): Inconsistent handling of namespaces +* [#751](https://github.com/sebastianbergmann/php-code-coverage/pull/751): Dead code is not highlighted correctly +* [#753](https://github.com/sebastianbergmann/php-code-coverage/issues/753): Do not use `$_SERVER['REQUEST_TIME']` because the test(ed) code might unset it + +## [8.0.1] - 2020-02-19 + +### Fixed + +* [#731](https://github.com/sebastianbergmann/php-code-coverage/pull/731): Confusing footer in the HTML report + +## [8.0.0] - 2020-02-07 + +### Fixed + +* [#721](https://github.com/sebastianbergmann/php-code-coverage/pull/721): Workaround for PHP bug [#79191](https://bugs.php.net/bug.php?id=79191) + +### Removed + +* This component is no longer supported on PHP 7.2 + ## [7.0.15] - 2021-07-26 ### Changed @@ -100,7 +318,7 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](htt ### Added -* Implemented [#663](https://github.com/sebastianbergmann/php-code-coverage/pull/663): Support for PCOV +* [#663](https://github.com/sebastianbergmann/php-code-coverage/pull/663): Support for PCOV ### Fixed @@ -111,37 +329,30 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](htt * This component is no longer supported on PHP 7.1 -## [6.1.4] - 2018-10-31 - -### Fixed - -* [#650](https://github.com/sebastianbergmann/php-code-coverage/issues/650): Wasted screen space in HTML code coverage report - -## [6.1.3] - 2018-10-23 - -### Changed - -* Use `^3.1` of `sebastian/environment` again due to [regression](https://github.com/sebastianbergmann/environment/issues/31) - -## [6.1.2] - 2018-10-23 - -### Fixed - -* [#645](https://github.com/sebastianbergmann/php-code-coverage/pull/645): Crash that can occur when php-token-stream parses invalid files - -## [6.1.1] - 2018-10-18 - -### Changed - -* This component now allows `^4` of `sebastian/environment` - -## [6.1.0] - 2018-10-16 - -### Changed - -* Class names are now abbreviated (unqualified name shown, fully qualified name shown on hover) in the file view of the HTML report -* Update HTML report to Bootstrap 4 - +[9.2.7]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.6...9.2.7 +[9.2.6]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.5...9.2.6 +[9.2.5]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.4...9.2.5 +[9.2.4]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.3...9.2.4 +[9.2.3]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.2...9.2.3 +[9.2.2]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.1...9.2.2 +[9.2.1]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.0...9.2.1 +[9.2.0]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.1.11...9.2.0 +[9.1.11]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.1.10...9.1.11 +[9.1.10]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.1.9...9.1.10 +[9.1.9]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.1.8...9.1.9 +[9.1.8]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.1.7...9.1.8 +[9.1.7]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.1.6...9.1.7 +[9.1.6]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.1.5...9.1.6 +[9.1.5]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.1.4...9.1.5 +[9.1.4]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.1.3...9.1.4 +[9.1.3]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.1.2...9.1.3 +[9.1.2]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.1.1...9.1.2 +[9.1.1]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.1.0...9.1.1 +[9.1.0]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.0.0...9.1.0 +[9.0.0]: https://github.com/sebastianbergmann/php-code-coverage/compare/8.0...9.0.0 +[8.0.2]: https://github.com/sebastianbergmann/php-code-coverage/compare/8.0.1...8.0.2 +[8.0.1]: https://github.com/sebastianbergmann/php-code-coverage/compare/8.0.0...8.0.1 +[8.0.0]: https://github.com/sebastianbergmann/php-code-coverage/compare/7.0.10...8.0.0 [7.0.15]: https://github.com/sebastianbergmann/php-code-coverage/compare/7.0.14...7.0.15 [7.0.14]: https://github.com/sebastianbergmann/php-code-coverage/compare/7.0.13...7.0.14 [7.0.13]: https://github.com/sebastianbergmann/php-code-coverage/compare/7.0.12...7.0.13 @@ -158,9 +369,3 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](htt [7.0.2]: https://github.com/sebastianbergmann/php-code-coverage/compare/7.0.1...7.0.2 [7.0.1]: https://github.com/sebastianbergmann/php-code-coverage/compare/7.0.0...7.0.1 [7.0.0]: https://github.com/sebastianbergmann/php-code-coverage/compare/6.1.4...7.0.0 -[6.1.4]: https://github.com/sebastianbergmann/php-code-coverage/compare/6.1.3...6.1.4 -[6.1.3]: https://github.com/sebastianbergmann/php-code-coverage/compare/6.1.2...6.1.3 -[6.1.2]: https://github.com/sebastianbergmann/php-code-coverage/compare/6.1.1...6.1.2 -[6.1.1]: https://github.com/sebastianbergmann/php-code-coverage/compare/6.1.0...6.1.1 -[6.1.0]: https://github.com/sebastianbergmann/php-code-coverage/compare/6.0...6.1.0 - diff --git a/app/vendor/phpunit/php-code-coverage/LICENSE b/app/vendor/phpunit/php-code-coverage/LICENSE index b1a0140a1..48609af5e 100644 --- a/app/vendor/phpunit/php-code-coverage/LICENSE +++ b/app/vendor/phpunit/php-code-coverage/LICENSE @@ -1,6 +1,6 @@ php-code-coverage -Copyright (c) 2009-2019, Sebastian Bergmann . +Copyright (c) 2009-2021, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/app/vendor/phpunit/php-code-coverage/README.md b/app/vendor/phpunit/php-code-coverage/README.md index bd4a1692f..53ce9b338 100644 --- a/app/vendor/phpunit/php-code-coverage/README.md +++ b/app/vendor/phpunit/php-code-coverage/README.md @@ -1,29 +1,41 @@ -[![Latest Stable Version](https://poser.pugx.org/phpunit/php-code-coverage/v/stable.png)](https://packagist.org/packages/phpunit/php-code-coverage) -[![Build Status](https://travis-ci.org/sebastianbergmann/php-code-coverage.svg?branch=master)](https://travis-ci.org/sebastianbergmann/php-code-coverage) +# phpunit/php-code-coverage -# SebastianBergmann\CodeCoverage +[![Latest Stable Version](https://poser.pugx.org/phpunit/php-code-coverage/v/stable.png)](https://packagist.org/packages/phpunit/php-code-coverage) +[![CI Status](https://github.com/sebastianbergmann/php-code-coverage/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/php-code-coverage/actions) +[![Type Coverage](https://shepherd.dev/github/sebastianbergmann/php-code-coverage/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/php-code-coverage) -**SebastianBergmann\CodeCoverage** is a library that provides collection, processing, and rendering functionality for PHP code coverage information. +Provides collection, processing, and rendering functionality for PHP code coverage information. ## Installation You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/): - composer require phpunit/php-code-coverage +``` +composer require phpunit/php-code-coverage +``` If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency: - composer require --dev phpunit/php-code-coverage +``` +composer require --dev phpunit/php-code-coverage +``` -## Using the SebastianBergmann\CodeCoverage API +## Usage ```php -includeDirectory('/path/to/directory'); -$coverage->filter()->addDirectoryToWhitelist('/path/to/src'); +$coverage = new CodeCoverage( + (new Selector)->forLineCoverage($filter), + $filter +); $coverage->start(''); @@ -31,10 +43,6 @@ $coverage->start(''); $coverage->stop(); -$writer = new \SebastianBergmann\CodeCoverage\Report\Clover; -$writer->process($coverage, '/tmp/clover.xml'); -$writer = new \SebastianBergmann\CodeCoverage\Report\Html\Facade; -$writer->process($coverage, '/tmp/code-coverage-report'); +(new HtmlReport)->process($coverage, '/tmp/code-coverage-report'); ``` - diff --git a/app/vendor/phpunit/php-code-coverage/build.xml b/app/vendor/phpunit/php-code-coverage/build.xml deleted file mode 100644 index df8408ec2..000000000 --- a/app/vendor/phpunit/php-code-coverage/build.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/vendor/phpunit/php-code-coverage/composer.json b/app/vendor/phpunit/php-code-coverage/composer.json index ed6e8132b..898484ffb 100644 --- a/app/vendor/phpunit/php-code-coverage/composer.json +++ b/app/vendor/phpunit/php-code-coverage/composer.json @@ -20,27 +20,34 @@ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues" }, "config": { + "platform": { + "php": "7.3.0" + }, "optimize-autoloader": true, "sort-packages": true }, "prefer-stable": true, "require": { - "php": ">=7.2", + "php": ">=7.3", "ext-dom": "*", + "ext-libxml": "*", "ext-xmlwriter": "*", - "phpunit/php-file-iterator": "^2.0.2", - "phpunit/php-token-stream": "^3.1.3 || ^4.0", - "phpunit/php-text-template": "^1.2.1", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^4.2.2", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1.3" + "nikic/php-parser": "^4.12.0", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^8.2.2" + "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-xdebug": "^2.7.2" + "ext-pcov": "*", + "ext-xdebug": "*" }, "autoload": { "classmap": [ @@ -55,7 +62,7 @@ }, "extra": { "branch-alias": { - "dev-master": "7.0-dev" + "dev-master": "9.2-dev" } } } diff --git a/app/vendor/phpunit/php-code-coverage/phive.xml b/app/vendor/phpunit/php-code-coverage/phive.xml deleted file mode 100644 index 3ec79ee4d..000000000 --- a/app/vendor/phpunit/php-code-coverage/phive.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/app/vendor/phpunit/php-code-coverage/phpunit.xml b/app/vendor/phpunit/php-code-coverage/phpunit.xml deleted file mode 100644 index 37e22199f..000000000 --- a/app/vendor/phpunit/php-code-coverage/phpunit.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - tests/tests - - - - - src - - - - - - - - diff --git a/app/vendor/phpunit/php-code-coverage/src/CodeCoverage.php b/app/vendor/phpunit/php-code-coverage/src/CodeCoverage.php index ced3b75df..6445c6d27 100644 --- a/app/vendor/phpunit/php-code-coverage/src/CodeCoverage.php +++ b/app/vendor/phpunit/php-code-coverage/src/CodeCoverage.php @@ -1,6 +1,6 @@ * @@ -9,23 +9,41 @@ */ namespace SebastianBergmann\CodeCoverage; +use function array_diff; +use function array_diff_key; +use function array_flip; +use function array_keys; +use function array_merge; +use function array_unique; +use function array_values; +use function count; +use function explode; +use function get_class; +use function is_array; +use function is_file; +use function sort; use PHPUnit\Framework\TestCase; use PHPUnit\Runner\PhptTestCase; use PHPUnit\Util\Test; +use ReflectionClass; use SebastianBergmann\CodeCoverage\Driver\Driver; -use SebastianBergmann\CodeCoverage\Driver\PCOV; -use SebastianBergmann\CodeCoverage\Driver\PHPDBG; -use SebastianBergmann\CodeCoverage\Driver\Xdebug; use SebastianBergmann\CodeCoverage\Node\Builder; use SebastianBergmann\CodeCoverage\Node\Directory; +use SebastianBergmann\CodeCoverage\StaticAnalysis\CachingCoveredFileAnalyser; +use SebastianBergmann\CodeCoverage\StaticAnalysis\CachingUncoveredFileAnalyser; +use SebastianBergmann\CodeCoverage\StaticAnalysis\CoveredFileAnalyser; +use SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingCoveredFileAnalyser; +use SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingUncoveredFileAnalyser; +use SebastianBergmann\CodeCoverage\StaticAnalysis\UncoveredFileAnalyser; use SebastianBergmann\CodeUnitReverseLookup\Wizard; -use SebastianBergmann\Environment\Runtime; /** * Provides collection functionality for PHP code coverage information. */ final class CodeCoverage { + private const UNCOVERED_FILES = 'UNCOVERED_FILES'; + /** * @var Driver */ @@ -41,11 +59,6 @@ final class CodeCoverage */ private $wizard; - /** - * @var bool - */ - private $cacheTokens = false; - /** * @var bool */ @@ -54,27 +67,12 @@ final class CodeCoverage /** * @var bool */ - private $forceCoversAnnotation = false; - - /** - * @var bool - */ - private $checkForUnexecutedCoveredCode = false; - - /** - * @var bool - */ - private $checkForMissingCoversAnnotation = false; - - /** - * @var bool - */ - private $addUncoveredFilesFromWhitelist = true; + private $includeUncoveredFiles = true; /** * @var bool */ - private $processUncoveredFilesFromWhitelist = false; + private $processUncoveredFiles = false; /** * @var bool @@ -89,19 +87,14 @@ final class CodeCoverage /** * Code coverage data. * - * @var array - */ - private $data = []; - - /** - * @var array + * @var ProcessedCodeCoverageData */ - private $ignoredLines = []; + private $data; /** * @var bool */ - private $disableIgnoredLines = false; + private $useAnnotationsForIgnoringCode = true; /** * Test data. @@ -111,45 +104,30 @@ final class CodeCoverage private $tests = []; /** - * @var string[] + * @psalm-var list */ - private $unintentionallyCoveredSubclassesWhitelist = []; + private $parentClassesExcludedFromUnintentionallyCoveredCodeCheck = []; /** - * Determine if the data has been initialized or not - * - * @var bool + * @var ?CoveredFileAnalyser */ - private $isInitialized = false; + private $coveredFileAnalyser; /** - * Determine whether we need to check for dead and unused code on each test - * - * @var bool + * @var ?UncoveredFileAnalyser */ - private $shouldCheckForDeadAndUnused = true; + private $uncoveredFileAnalyser; /** - * @var Directory + * @var ?string */ - private $report; + private $cacheDirectory; - /** - * @throws RuntimeException - */ - public function __construct(Driver $driver = null, Filter $filter = null) + public function __construct(Driver $driver, Filter $filter) { - if ($filter === null) { - $filter = new Filter; - } - - if ($driver === null) { - $driver = $this->selectDriver($filter); - } - $this->driver = $driver; $this->filter = $filter; - + $this->data = new ProcessedCodeCoverageData; $this->wizard = new Wizard; } @@ -158,11 +136,7 @@ public function __construct(Driver $driver = null, Filter $filter = null) */ public function getReport(): Directory { - if ($this->report === null) { - $this->report = (new Builder)->build($this); - } - - return $this->report; + return (new Builder($this->coveredFileAnalyser()))->build($this); } /** @@ -170,11 +144,9 @@ public function getReport(): Directory */ public function clear(): void { - $this->isInitialized = false; - $this->currentId = null; - $this->data = []; - $this->tests = []; - $this->report = null; + $this->currentId = null; + $this->data = new ProcessedCodeCoverageData; + $this->tests = []; } /** @@ -188,10 +160,14 @@ public function filter(): Filter /** * Returns the collected code coverage data. */ - public function getData(bool $raw = false): array + public function getData(bool $raw = false): ProcessedCodeCoverageData { - if (!$raw && $this->addUncoveredFilesFromWhitelist) { - $this->addUncoveredFilesFromWhitelist(); + if (!$raw) { + if ($this->processUncoveredFiles) { + $this->processUncoveredFilesFromFilter(); + } elseif ($this->includeUncoveredFiles) { + $this->addUncoveredFilesFromFilter(); + } } return $this->data; @@ -200,10 +176,9 @@ public function getData(bool $raw = false): array /** * Sets the coverage data. */ - public function setData(array $data): void + public function setData(ProcessedCodeCoverageData $data): void { - $this->data = $data; - $this->report = null; + $this->data = $data; } /** @@ -226,8 +201,6 @@ public function setTests(array $tests): void * Start collection of code coverage information. * * @param PhptTestCase|string|TestCase $id - * - * @throws RuntimeException */ public function start($id, bool $clear = false): void { @@ -235,37 +208,26 @@ public function start($id, bool $clear = false): void $this->clear(); } - if ($this->isInitialized === false) { - $this->initializeData(); - } - $this->currentId = $id; - $this->driver->start($this->shouldCheckForDeadAndUnused); + $this->driver->start(); } /** * Stop collection of code coverage information. * * @param array|false $linesToBeCovered - * - * @throws MissingCoversAnnotationException - * @throws CoveredCodeNotExecutedException - * @throws RuntimeException - * @throws InvalidArgumentException - * @throws \ReflectionException - */ - public function stop(bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], bool $ignoreForceCoversAnnotation = false): array - { - if (!\is_array($linesToBeCovered) && $linesToBeCovered !== false) { - throw InvalidArgumentException::create( - 2, - 'array or false' + */ + public function stop(bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = []): RawCodeCoverageData + { + if (!is_array($linesToBeCovered) && $linesToBeCovered !== false) { + throw new InvalidArgumentException( + '$linesToBeCovered must be an array or false' ); } $data = $this->driver->stop(); - $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed, $ignoreForceCoversAnnotation); + $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed); $this->currentId = null; @@ -278,215 +240,195 @@ public function stop(bool $append = true, $linesToBeCovered = [], array $linesTo * @param PhptTestCase|string|TestCase $id * @param array|false $linesToBeCovered * - * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException - * @throws \SebastianBergmann\CodeCoverage\MissingCoversAnnotationException - * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException - * @throws \ReflectionException - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws RuntimeException + * @throws ReflectionException + * @throws TestIdMissingException + * @throws UnintentionallyCoveredCodeException */ - public function append(array $data, $id = null, bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], bool $ignoreForceCoversAnnotation = false): void + public function append(RawCodeCoverageData $rawData, $id = null, bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = []): void { if ($id === null) { $id = $this->currentId; } if ($id === null) { - throw new RuntimeException; + throw new TestIdMissingException; + } + + $this->applyFilter($rawData); + + if ($this->useAnnotationsForIgnoringCode) { + $this->applyIgnoredLinesFilter($rawData); } - $this->applyWhitelistFilter($data); - $this->applyIgnoredLinesFilter($data); - $this->initializeFilesThatAreSeenTheFirstTime($data); + $this->data->initializeUnseenData($rawData); if (!$append) { return; } - if ($id !== 'UNCOVERED_FILES_FROM_WHITELIST') { + if ($id !== self::UNCOVERED_FILES) { $this->applyCoversAnnotationFilter( - $data, + $rawData, $linesToBeCovered, - $linesToBeUsed, - $ignoreForceCoversAnnotation + $linesToBeUsed ); - } - - if (empty($data)) { - return; - } - - $size = 'unknown'; - $status = -1; - - if ($id instanceof TestCase) { - $_size = $id->getSize(); - if ($_size === Test::SMALL) { - $size = 'small'; - } elseif ($_size === Test::MEDIUM) { - $size = 'medium'; - } elseif ($_size === Test::LARGE) { - $size = 'large'; + if (empty($rawData->lineCoverage())) { + return; } - $status = $id->getStatus(); - $id = \get_class($id) . '::' . $id->getName(); - } elseif ($id instanceof PhptTestCase) { - $size = 'large'; - $id = $id->getName(); - } - - $this->tests[$id] = ['size' => $size, 'status' => $status]; + $size = 'unknown'; + $status = -1; + $fromTestcase = false; - foreach ($data as $file => $lines) { - if (!$this->filter->isFile($file)) { - continue; - } + if ($id instanceof TestCase) { + $fromTestcase = true; + $_size = $id->getSize(); - foreach ($lines as $k => $v) { - if ($v === Driver::LINE_EXECUTED) { - if (empty($this->data[$file][$k]) || !\in_array($id, $this->data[$file][$k])) { - $this->data[$file][$k][] = $id; - } + if ($_size === Test::SMALL) { + $size = 'small'; + } elseif ($_size === Test::MEDIUM) { + $size = 'medium'; + } elseif ($_size === Test::LARGE) { + $size = 'large'; } + + $status = $id->getStatus(); + $id = get_class($id) . '::' . $id->getName(); + } elseif ($id instanceof PhptTestCase) { + $fromTestcase = true; + $size = 'large'; + $id = $id->getName(); } - } - $this->report = null; + $this->tests[$id] = ['size' => $size, 'status' => $status, 'fromTestcase' => $fromTestcase]; + + $this->data->markCodeAsExecutedByTestCase($id, $rawData); + } } /** * Merges the data from another instance. - * - * @param CodeCoverage $that */ public function merge(self $that): void { - $this->filter->setWhitelistedFiles( - \array_merge($this->filter->getWhitelistedFiles(), $that->filter()->getWhitelistedFiles()) + $this->filter->includeFiles( + $that->filter()->files() ); - foreach ($that->data as $file => $lines) { - if (!isset($this->data[$file])) { - if (!$this->filter->isFiltered($file)) { - $this->data[$file] = $lines; - } - - continue; - } + $this->data->merge($that->data); - // we should compare the lines if any of two contains data - $compareLineNumbers = \array_unique( - \array_merge( - \array_keys($this->data[$file]), - \array_keys($that->data[$file]) - ) - ); - - foreach ($compareLineNumbers as $line) { - $thatPriority = $this->getLinePriority($that->data[$file], $line); - $thisPriority = $this->getLinePriority($this->data[$file], $line); + $this->tests = array_merge($this->tests, $that->getTests()); + } - if ($thatPriority > $thisPriority) { - $this->data[$file][$line] = $that->data[$file][$line]; - } elseif ($thatPriority === $thisPriority && \is_array($this->data[$file][$line])) { - $this->data[$file][$line] = \array_unique( - \array_merge($this->data[$file][$line], $that->data[$file][$line]) - ); - } - } - } + public function enableCheckForUnintentionallyCoveredCode(): void + { + $this->checkForUnintentionallyCoveredCode = true; + } - $this->tests = \array_merge($this->tests, $that->getTests()); - $this->report = null; + public function disableCheckForUnintentionallyCoveredCode(): void + { + $this->checkForUnintentionallyCoveredCode = false; } - public function setCacheTokens(bool $flag): void + public function includeUncoveredFiles(): void { - $this->cacheTokens = $flag; + $this->includeUncoveredFiles = true; } - public function getCacheTokens(): bool + public function excludeUncoveredFiles(): void { - return $this->cacheTokens; + $this->includeUncoveredFiles = false; } - public function setCheckForUnintentionallyCoveredCode(bool $flag): void + public function processUncoveredFiles(): void { - $this->checkForUnintentionallyCoveredCode = $flag; + $this->processUncoveredFiles = true; } - public function setForceCoversAnnotation(bool $flag): void + public function doNotProcessUncoveredFiles(): void { - $this->forceCoversAnnotation = $flag; + $this->processUncoveredFiles = false; } - public function setCheckForMissingCoversAnnotation(bool $flag): void + public function enableAnnotationsForIgnoringCode(): void { - $this->checkForMissingCoversAnnotation = $flag; + $this->useAnnotationsForIgnoringCode = true; } - public function setCheckForUnexecutedCoveredCode(bool $flag): void + public function disableAnnotationsForIgnoringCode(): void { - $this->checkForUnexecutedCoveredCode = $flag; + $this->useAnnotationsForIgnoringCode = false; } - public function setAddUncoveredFilesFromWhitelist(bool $flag): void + public function ignoreDeprecatedCode(): void { - $this->addUncoveredFilesFromWhitelist = $flag; + $this->ignoreDeprecatedCode = true; } - public function setProcessUncoveredFilesFromWhitelist(bool $flag): void + public function doNotIgnoreDeprecatedCode(): void { - $this->processUncoveredFilesFromWhitelist = $flag; + $this->ignoreDeprecatedCode = false; } - public function setDisableIgnoredLines(bool $flag): void + /** + * @psalm-assert-if-true !null $this->cacheDirectory + */ + public function cachesStaticAnalysis(): bool { - $this->disableIgnoredLines = $flag; + return $this->cacheDirectory !== null; } - public function setIgnoreDeprecatedCode(bool $flag): void + public function cacheStaticAnalysis(string $directory): void { - $this->ignoreDeprecatedCode = $flag; + $this->cacheDirectory = $directory; } - public function setUnintentionallyCoveredSubclassesWhitelist(array $whitelist): void + public function doNotCacheStaticAnalysis(): void { - $this->unintentionallyCoveredSubclassesWhitelist = $whitelist; + $this->cacheDirectory = null; } /** - * Determine the priority for a line - * - * 1 = the line is not set - * 2 = the line has not been tested - * 3 = the line is dead code - * 4 = the line has been tested - * - * During a merge, a higher number is better. - * - * @param array $data - * @param int $line - * - * @return int + * @throws StaticAnalysisCacheNotConfiguredException */ - private function getLinePriority($data, $line) + public function cacheDirectory(): string { - if (!\array_key_exists($line, $data)) { - return 1; + if (!$this->cachesStaticAnalysis()) { + throw new StaticAnalysisCacheNotConfiguredException( + 'The static analysis cache is not configured' + ); } - if (\is_array($data[$line]) && \count($data[$line]) === 0) { - return 2; - } + return $this->cacheDirectory; + } - if ($data[$line] === null) { - return 3; - } + /** + * @psalm-param class-string $className + */ + public function excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(string $className): void + { + $this->parentClassesExcludedFromUnintentionallyCoveredCodeCheck[] = $className; + } - return 4; + public function enableBranchAndPathCoverage(): void + { + $this->driver->enableBranchAndPathCoverage(); + } + + public function disableBranchAndPathCoverage(): void + { + $this->driver->disableBranchAndPathCoverage(); + } + + public function collectsBranchAndPathCoverage(): bool + { + return $this->driver->collectsBranchAndPathCoverage(); + } + + public function detectsDeadCode(): bool + { + return $this->driver->detectsDeadCode(); } /** @@ -494,20 +436,13 @@ private function getLinePriority($data, $line) * * @param array|false $linesToBeCovered * - * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException - * @throws \ReflectionException - * @throws MissingCoversAnnotationException + * @throws ReflectionException * @throws UnintentionallyCoveredCodeException */ - private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, array $linesToBeUsed, bool $ignoreForceCoversAnnotation): void + private function applyCoversAnnotationFilter(RawCodeCoverageData $rawData, $linesToBeCovered, array $linesToBeUsed): void { - if ($linesToBeCovered === false || - ($this->forceCoversAnnotation && empty($linesToBeCovered) && !$ignoreForceCoversAnnotation)) { - if ($this->checkForMissingCoversAnnotation) { - throw new MissingCoversAnnotationException; - } - - $data = []; + if ($linesToBeCovered === false) { + $rawData->clear(); return; } @@ -519,281 +454,99 @@ private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, ar if ($this->checkForUnintentionallyCoveredCode && (!$this->currentId instanceof TestCase || (!$this->currentId->isMedium() && !$this->currentId->isLarge()))) { - $this->performUnintentionallyCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); + $this->performUnintentionallyCoveredCodeCheck($rawData, $linesToBeCovered, $linesToBeUsed); } - if ($this->checkForUnexecutedCoveredCode) { - $this->performUnexecutedCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); - } - - $data = \array_intersect_key($data, $linesToBeCovered); + $rawLineData = $rawData->lineCoverage(); + $filesWithNoCoverage = array_diff_key($rawLineData, $linesToBeCovered); - foreach (\array_keys($data) as $filename) { - $_linesToBeCovered = \array_flip($linesToBeCovered[$filename]); - $data[$filename] = \array_intersect_key($data[$filename], $_linesToBeCovered); + foreach (array_keys($filesWithNoCoverage) as $fileWithNoCoverage) { + $rawData->removeCoverageDataForFile($fileWithNoCoverage); } - } - private function applyWhitelistFilter(array &$data): void - { - foreach (\array_keys($data) as $filename) { - if ($this->filter->isFiltered($filename)) { - unset($data[$filename]); + if (is_array($linesToBeCovered)) { + foreach ($linesToBeCovered as $fileToBeCovered => $includedLines) { + $rawData->keepCoverageDataOnlyForLines($fileToBeCovered, $includedLines); } } } - /** - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - */ - private function applyIgnoredLinesFilter(array &$data): void + private function applyFilter(RawCodeCoverageData $data): void { - foreach (\array_keys($data) as $filename) { - if (!$this->filter->isFile($filename)) { - continue; - } + if ($this->filter->isEmpty()) { + return; + } - foreach ($this->getLinesToBeIgnored($filename) as $line) { - unset($data[$filename][$line]); + foreach (array_keys($data->lineCoverage()) as $filename) { + if ($this->filter->isExcluded($filename)) { + $data->removeCoverageDataForFile($filename); } } } - private function initializeFilesThatAreSeenTheFirstTime(array $data): void + private function applyIgnoredLinesFilter(RawCodeCoverageData $data): void { - foreach ($data as $file => $lines) { - if (!isset($this->data[$file]) && $this->filter->isFile($file)) { - $this->data[$file] = []; - - foreach ($lines as $k => $v) { - $this->data[$file][$k] = $v === -2 ? null : []; - } + foreach (array_keys($data->lineCoverage()) as $filename) { + if (!$this->filter->isFile($filename)) { + continue; } + + $data->removeCoverageDataForLines( + $filename, + $this->coveredFileAnalyser()->ignoredLinesFor($filename) + ); } } /** - * @throws CoveredCodeNotExecutedException - * @throws InvalidArgumentException - * @throws MissingCoversAnnotationException - * @throws RuntimeException * @throws UnintentionallyCoveredCodeException - * @throws \ReflectionException */ - private function addUncoveredFilesFromWhitelist(): void + private function addUncoveredFilesFromFilter(): void { - $data = []; - $uncoveredFiles = \array_diff( - $this->filter->getWhitelist(), - \array_keys($this->data) + $uncoveredFiles = array_diff( + $this->filter->files(), + $this->data->coveredFiles() ); foreach ($uncoveredFiles as $uncoveredFile) { - if (!\file_exists($uncoveredFile)) { - continue; - } - - $data[$uncoveredFile] = []; - - $lines = \count(\file($uncoveredFile)); - - for ($i = 1; $i <= $lines; $i++) { - $data[$uncoveredFile][$i] = Driver::LINE_NOT_EXECUTED; + if (is_file($uncoveredFile)) { + $this->append( + RawCodeCoverageData::fromUncoveredFile( + $uncoveredFile, + $this->uncoveredFileAnalyser() + ), + self::UNCOVERED_FILES + ); } } - - $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); } - private function getLinesToBeIgnored(string $fileName): array - { - if (isset($this->ignoredLines[$fileName])) { - return $this->ignoredLines[$fileName]; - } - - try { - return $this->getLinesToBeIgnoredInner($fileName); - } catch (\OutOfBoundsException $e) { - // This can happen with PHP_Token_Stream if the file is syntactically invalid, - // and probably affects a file that wasn't executed. - return []; - } - } - - private function getLinesToBeIgnoredInner(string $fileName): array + /** + * @throws UnintentionallyCoveredCodeException + */ + private function processUncoveredFilesFromFilter(): void { - $this->ignoredLines[$fileName] = []; - - $lines = \file($fileName); - - foreach ($lines as $index => $line) { - if (!\trim($line)) { - $this->ignoredLines[$fileName][] = $index + 1; - } - } - - if ($this->cacheTokens) { - $tokens = \PHP_Token_Stream_CachingFactory::get($fileName); - } else { - $tokens = new \PHP_Token_Stream($fileName); - } - - foreach ($tokens->getInterfaces() as $interface) { - $interfaceStartLine = $interface['startLine']; - $interfaceEndLine = $interface['endLine']; - - foreach (\range($interfaceStartLine, $interfaceEndLine) as $line) { - $this->ignoredLines[$fileName][] = $line; - } - } - - foreach (\array_merge($tokens->getClasses(), $tokens->getTraits()) as $classOrTrait) { - $classOrTraitStartLine = $classOrTrait['startLine']; - $classOrTraitEndLine = $classOrTrait['endLine']; - - if (empty($classOrTrait['methods'])) { - foreach (\range($classOrTraitStartLine, $classOrTraitEndLine) as $line) { - $this->ignoredLines[$fileName][] = $line; - } - - continue; - } - - $firstMethod = \array_shift($classOrTrait['methods']); - $firstMethodStartLine = $firstMethod['startLine']; - $lastMethodEndLine = $firstMethod['endLine']; - - do { - $lastMethod = \array_pop($classOrTrait['methods']); - } while ($lastMethod !== null && 0 === \strpos($lastMethod['signature'], 'anonymousFunction')); - - if ($lastMethod !== null) { - $lastMethodEndLine = $lastMethod['endLine']; - } - - foreach (\range($classOrTraitStartLine, $firstMethodStartLine) as $line) { - $this->ignoredLines[$fileName][] = $line; - } - - foreach (\range($lastMethodEndLine + 1, $classOrTraitEndLine) as $line) { - $this->ignoredLines[$fileName][] = $line; - } - } - - if ($this->disableIgnoredLines) { - $this->ignoredLines[$fileName] = \array_unique($this->ignoredLines[$fileName]); - \sort($this->ignoredLines[$fileName]); - - return $this->ignoredLines[$fileName]; - } - - $ignore = false; - $stop = false; - - foreach ($tokens->tokens() as $token) { - switch (\get_class($token)) { - case \PHP_Token_COMMENT::class: - case \PHP_Token_DOC_COMMENT::class: - $_token = \trim((string) $token); - $_line = \trim($lines[$token->getLine() - 1]); - - if ($_token === '// @codeCoverageIgnore' || - $_token === '//@codeCoverageIgnore') { - $ignore = true; - $stop = true; - } elseif ($_token === '// @codeCoverageIgnoreStart' || - $_token === '//@codeCoverageIgnoreStart') { - $ignore = true; - } elseif ($_token === '// @codeCoverageIgnoreEnd' || - $_token === '//@codeCoverageIgnoreEnd') { - $stop = true; - } - - if (!$ignore) { - $start = $token->getLine(); - $end = $start + \substr_count((string) $token, "\n"); - - // Do not ignore the first line when there is a token - // before the comment - if (0 !== \strpos($_token, $_line)) { - $start++; - } - - for ($i = $start; $i < $end; $i++) { - $this->ignoredLines[$fileName][] = $i; - } - - // A DOC_COMMENT token or a COMMENT token starting with "/*" - // does not contain the final \n character in its text - if (isset($lines[$i - 1]) && 0 === \strpos($_token, '/*') && '*/' === \substr(\trim($lines[$i - 1]), -2)) { - $this->ignoredLines[$fileName][] = $i; - } - } - - break; - - case \PHP_Token_INTERFACE::class: - case \PHP_Token_TRAIT::class: - case \PHP_Token_CLASS::class: - case \PHP_Token_FUNCTION::class: - /* @var \PHP_Token_Interface $token */ - - $docblock = (string) $token->getDocblock(); - - $this->ignoredLines[$fileName][] = $token->getLine(); - - if (\strpos($docblock, '@codeCoverageIgnore') || ($this->ignoreDeprecatedCode && \strpos($docblock, '@deprecated'))) { - $endLine = $token->getEndLine(); - - for ($i = $token->getLine(); $i <= $endLine; $i++) { - $this->ignoredLines[$fileName][] = $i; - } - } - - break; - - /* @noinspection PhpMissingBreakStatementInspection */ - case \PHP_Token_NAMESPACE::class: - $this->ignoredLines[$fileName][] = $token->getEndLine(); - - // Intentional fallthrough - case \PHP_Token_DECLARE::class: - case \PHP_Token_OPEN_TAG::class: - case \PHP_Token_CLOSE_TAG::class: - case \PHP_Token_USE::class: - case \PHP_Token_USE_FUNCTION::class: - $this->ignoredLines[$fileName][] = $token->getLine(); - - break; - } + $uncoveredFiles = array_diff( + $this->filter->files(), + $this->data->coveredFiles() + ); - if ($ignore) { - $this->ignoredLines[$fileName][] = $token->getLine(); + $this->driver->start(); - if ($stop) { - $ignore = false; - $stop = false; - } + foreach ($uncoveredFiles as $uncoveredFile) { + if (is_file($uncoveredFile)) { + include_once $uncoveredFile; } } - $this->ignoredLines[$fileName][] = \count($lines) + 1; - - $this->ignoredLines[$fileName] = \array_unique( - $this->ignoredLines[$fileName] - ); - - $this->ignoredLines[$fileName] = \array_unique($this->ignoredLines[$fileName]); - \sort($this->ignoredLines[$fileName]); - - return $this->ignoredLines[$fileName]; + $this->append($this->driver->stop(), self::UNCOVERED_FILES); } /** - * @throws \ReflectionException + * @throws ReflectionException * @throws UnintentionallyCoveredCodeException */ - private function performUnintentionallyCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed): void + private function performUnintentionallyCoveredCodeCheck(RawCodeCoverageData $data, array $linesToBeCovered, array $linesToBeUsed): void { $allowedLines = $this->getAllowedLines( $linesToBeCovered, @@ -802,7 +555,7 @@ private function performUnintentionallyCoveredCodeCheck(array &$data, array $lin $unintentionallyCoveredUnits = []; - foreach ($data as $file => $_data) { + foreach ($data->lineCoverage() as $file => $_data) { foreach ($_data as $line => $flag) { if ($flag === 1 && !isset($allowedLines[$file][$line])) { $unintentionallyCoveredUnits[] = $this->wizard->lookup($file, $line); @@ -819,66 +572,35 @@ private function performUnintentionallyCoveredCodeCheck(array &$data, array $lin } } - /** - * @throws CoveredCodeNotExecutedException - */ - private function performUnexecutedCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed): void - { - $executedCodeUnits = $this->coverageToCodeUnits($data); - $message = ''; - - foreach ($this->linesToCodeUnits($linesToBeCovered) as $codeUnit) { - if (!\in_array($codeUnit, $executedCodeUnits)) { - $message .= \sprintf( - '- %s is expected to be executed (@covers) but was not executed' . "\n", - $codeUnit - ); - } - } - - foreach ($this->linesToCodeUnits($linesToBeUsed) as $codeUnit) { - if (!\in_array($codeUnit, $executedCodeUnits)) { - $message .= \sprintf( - '- %s is expected to be executed (@uses) but was not executed' . "\n", - $codeUnit - ); - } - } - - if (!empty($message)) { - throw new CoveredCodeNotExecutedException($message); - } - } - private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed): array { $allowedLines = []; - foreach (\array_keys($linesToBeCovered) as $file) { + foreach (array_keys($linesToBeCovered) as $file) { if (!isset($allowedLines[$file])) { $allowedLines[$file] = []; } - $allowedLines[$file] = \array_merge( + $allowedLines[$file] = array_merge( $allowedLines[$file], $linesToBeCovered[$file] ); } - foreach (\array_keys($linesToBeUsed) as $file) { + foreach (array_keys($linesToBeUsed) as $file) { if (!isset($allowedLines[$file])) { $allowedLines[$file] = []; } - $allowedLines[$file] = \array_merge( + $allowedLines[$file] = array_merge( $allowedLines[$file], $linesToBeUsed[$file] ); } - foreach (\array_keys($allowedLines) as $file) { - $allowedLines[$file] = \array_flip( - \array_unique($allowedLines[$file]) + foreach (array_keys($allowedLines) as $file) { + $allowedLines[$file] = array_flip( + array_unique($allowedLines[$file]) ); } @@ -886,121 +608,78 @@ private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed): } /** - * @throws RuntimeException + * @throws ReflectionException */ - private function selectDriver(Filter $filter): Driver - { - $runtime = new Runtime; - - if ($runtime->hasPHPDBGCodeCoverage()) { - return new PHPDBG; - } - - if ($runtime->hasPCOV()) { - return new PCOV; - } - - if ($runtime->hasXdebug()) { - return new Xdebug($filter); - } - - throw new RuntimeException('No code coverage driver available'); - } - private function processUnintentionallyCoveredUnits(array $unintentionallyCoveredUnits): array { - $unintentionallyCoveredUnits = \array_unique($unintentionallyCoveredUnits); - \sort($unintentionallyCoveredUnits); + $unintentionallyCoveredUnits = array_unique($unintentionallyCoveredUnits); + sort($unintentionallyCoveredUnits); - foreach (\array_keys($unintentionallyCoveredUnits) as $k => $v) { - $unit = \explode('::', $unintentionallyCoveredUnits[$k]); + foreach (array_keys($unintentionallyCoveredUnits) as $k => $v) { + $unit = explode('::', $unintentionallyCoveredUnits[$k]); - if (\count($unit) !== 2) { + if (count($unit) !== 2) { continue; } - $class = new \ReflectionClass($unit[0]); + try { + $class = new ReflectionClass($unit[0]); - foreach ($this->unintentionallyCoveredSubclassesWhitelist as $whitelisted) { - if ($class->isSubclassOf($whitelisted)) { - unset($unintentionallyCoveredUnits[$k]); + foreach ($this->parentClassesExcludedFromUnintentionallyCoveredCodeCheck as $parentClass) { + if ($class->isSubclassOf($parentClass)) { + unset($unintentionallyCoveredUnits[$k]); - break; + break; + } } + } catch (\ReflectionException $e) { + throw new ReflectionException( + $e->getMessage(), + (int) $e->getCode(), + $e + ); } } - return \array_values($unintentionallyCoveredUnits); + return array_values($unintentionallyCoveredUnits); } - /** - * @throws CoveredCodeNotExecutedException - * @throws InvalidArgumentException - * @throws MissingCoversAnnotationException - * @throws RuntimeException - * @throws UnintentionallyCoveredCodeException - * @throws \ReflectionException - */ - private function initializeData(): void + private function coveredFileAnalyser(): CoveredFileAnalyser { - $this->isInitialized = true; - - if ($this->processUncoveredFilesFromWhitelist) { - $this->shouldCheckForDeadAndUnused = false; - - $this->driver->start(); - - foreach ($this->filter->getWhitelist() as $file) { - if ($this->filter->isFile($file)) { - include_once $file; - } - } - - $data = []; - - foreach ($this->driver->stop() as $file => $fileCoverage) { - if ($this->filter->isFiltered($file)) { - continue; - } - - foreach (\array_keys($fileCoverage) as $key) { - if ($fileCoverage[$key] === Driver::LINE_EXECUTED) { - $fileCoverage[$key] = Driver::LINE_NOT_EXECUTED; - } - } - - $data[$file] = $fileCoverage; - } - - $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); + if ($this->coveredFileAnalyser !== null) { + return $this->coveredFileAnalyser; } - } - private function coverageToCodeUnits(array $data): array - { - $codeUnits = []; + $this->coveredFileAnalyser = new ParsingCoveredFileAnalyser( + $this->useAnnotationsForIgnoringCode, + $this->ignoreDeprecatedCode + ); - foreach ($data as $filename => $lines) { - foreach ($lines as $line => $flag) { - if ($flag === 1) { - $codeUnits[] = $this->wizard->lookup($filename, $line); - } - } + if ($this->cachesStaticAnalysis()) { + $this->coveredFileAnalyser = new CachingCoveredFileAnalyser( + $this->cacheDirectory, + $this->coveredFileAnalyser + ); } - return \array_unique($codeUnits); + return $this->coveredFileAnalyser; } - private function linesToCodeUnits(array $data): array + private function uncoveredFileAnalyser(): UncoveredFileAnalyser { - $codeUnits = []; + if ($this->uncoveredFileAnalyser !== null) { + return $this->uncoveredFileAnalyser; + } - foreach ($data as $filename => $lines) { - foreach ($lines as $line) { - $codeUnits[] = $this->wizard->lookup($filename, $line); - } + $this->uncoveredFileAnalyser = new ParsingUncoveredFileAnalyser; + + if ($this->cachesStaticAnalysis()) { + $this->uncoveredFileAnalyser = new CachingUncoveredFileAnalyser( + $this->cacheDirectory, + $this->uncoveredFileAnalyser + ); } - return \array_unique($codeUnits); + return $this->uncoveredFileAnalyser; } } diff --git a/app/vendor/phpunit/php-code-coverage/src/CrapIndex.php b/app/vendor/phpunit/php-code-coverage/src/CrapIndex.php new file mode 100644 index 000000000..bd1639019 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/CrapIndex.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use function sprintf; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class CrapIndex +{ + /** + * @var int + */ + private $cyclomaticComplexity; + + /** + * @var float + */ + private $codeCoverage; + + public static function fromCyclomaticComplexityAndCoveragePercentage(int $cyclomaticComplexity, float $codeCoverage): self + { + return new self($cyclomaticComplexity, $codeCoverage); + } + + public function __construct(int $cyclomaticComplexity, float $codeCoverage) + { + $this->cyclomaticComplexity = $cyclomaticComplexity; + $this->codeCoverage = $codeCoverage; + } + + public function asString(): string + { + if ($this->codeCoverage === 0.0) { + return (string) ($this->cyclomaticComplexity ** 2 + $this->cyclomaticComplexity); + } + + if ($this->codeCoverage >= 95) { + return (string) $this->cyclomaticComplexity; + } + + return sprintf( + '%01.2F', + $this->cyclomaticComplexity ** 2 * (1 - $this->codeCoverage / 100) ** 3 + $this->cyclomaticComplexity + ); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Directory.php b/app/vendor/phpunit/php-code-coverage/src/Directory.php new file mode 100644 index 000000000..cd96e7858 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Directory.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use function is_dir; +use function mkdir; +use function sprintf; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Directory +{ + /** + * @throws DirectoryCouldNotBeCreatedException + */ + public static function create(string $directory): void + { + $success = !(!is_dir($directory) && !@mkdir($directory, 0777, true) && !is_dir($directory)); + + if (!$success) { + throw new DirectoryCouldNotBeCreatedException( + sprintf( + 'Directory "%s" could not be created', + $directory + ) + ); + } + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Driver/Driver.php b/app/vendor/phpunit/php-code-coverage/src/Driver/Driver.php index 17acbf62d..dc2de68f4 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Driver/Driver.php +++ b/app/vendor/phpunit/php-code-coverage/src/Driver/Driver.php @@ -1,6 +1,6 @@ * @@ -9,17 +9,25 @@ */ namespace SebastianBergmann\CodeCoverage\Driver; +use function sprintf; +use SebastianBergmann\CodeCoverage\BranchAndPathCoverageNotSupportedException; +use SebastianBergmann\CodeCoverage\DeadCodeDetectionNotSupportedException; +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException; +use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException; +use SebastianBergmann\CodeCoverage\RawCodeCoverageData; + /** - * Interface for code coverage drivers. + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -interface Driver +abstract class Driver { /** * @var int * * @see http://xdebug.org/docs/code_coverage */ - public const LINE_EXECUTED = 1; + public const LINE_NOT_EXECUTABLE = -2; /** * @var int @@ -33,15 +41,127 @@ interface Driver * * @see http://xdebug.org/docs/code_coverage */ - public const LINE_NOT_EXECUTABLE = -2; + public const LINE_EXECUTED = 1; + + /** + * @var int + * + * @see http://xdebug.org/docs/code_coverage + */ + public const BRANCH_NOT_HIT = 0; + + /** + * @var int + * + * @see http://xdebug.org/docs/code_coverage + */ + public const BRANCH_HIT = 1; + + /** + * @var bool + */ + private $collectBranchAndPathCoverage = false; + + /** + * @var bool + */ + private $detectDeadCode = false; /** - * Start collection of code coverage information. + * @throws NoCodeCoverageDriverAvailableException + * @throws PcovNotAvailableException + * @throws PhpdbgNotAvailableException + * @throws Xdebug2NotEnabledException + * @throws Xdebug3NotEnabledException + * @throws XdebugNotAvailableException + * + * @deprecated Use DriverSelector::forLineCoverage() instead */ - public function start(bool $determineUnusedAndDead = true): void; + public static function forLineCoverage(Filter $filter): self + { + return (new Selector)->forLineCoverage($filter); + } /** - * Stop collection of code coverage information. + * @throws NoCodeCoverageDriverWithPathCoverageSupportAvailableException + * @throws Xdebug2NotEnabledException + * @throws Xdebug3NotEnabledException + * @throws XdebugNotAvailableException + * + * @deprecated Use DriverSelector::forLineAndPathCoverage() instead */ - public function stop(): array; + public static function forLineAndPathCoverage(Filter $filter): self + { + return (new Selector)->forLineAndPathCoverage($filter); + } + + public function canCollectBranchAndPathCoverage(): bool + { + return false; + } + + public function collectsBranchAndPathCoverage(): bool + { + return $this->collectBranchAndPathCoverage; + } + + /** + * @throws BranchAndPathCoverageNotSupportedException + */ + public function enableBranchAndPathCoverage(): void + { + if (!$this->canCollectBranchAndPathCoverage()) { + throw new BranchAndPathCoverageNotSupportedException( + sprintf( + '%s does not support branch and path coverage', + $this->nameAndVersion() + ) + ); + } + + $this->collectBranchAndPathCoverage = true; + } + + public function disableBranchAndPathCoverage(): void + { + $this->collectBranchAndPathCoverage = false; + } + + public function canDetectDeadCode(): bool + { + return false; + } + + public function detectsDeadCode(): bool + { + return $this->detectDeadCode; + } + + /** + * @throws DeadCodeDetectionNotSupportedException + */ + public function enableDeadCodeDetection(): void + { + if (!$this->canDetectDeadCode()) { + throw new DeadCodeDetectionNotSupportedException( + sprintf( + '%s does not support dead code detection', + $this->nameAndVersion() + ) + ); + } + + $this->detectDeadCode = true; + } + + public function disableDeadCodeDetection(): void + { + $this->detectDeadCode = false; + } + + abstract public function nameAndVersion(): string; + + abstract public function start(): void; + + abstract public function stop(): RawCodeCoverageData; } diff --git a/app/vendor/phpunit/php-code-coverage/src/Driver/PCOV.php b/app/vendor/phpunit/php-code-coverage/src/Driver/PCOV.php deleted file mode 100644 index 7a6a3b6c8..000000000 --- a/app/vendor/phpunit/php-code-coverage/src/Driver/PCOV.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Driver; - -/** - * Driver for PCOV code coverage functionality. - * - * @codeCoverageIgnore - */ -final class PCOV implements Driver -{ - /** - * Start collection of code coverage information. - */ - public function start(bool $determineUnusedAndDead = true): void - { - \pcov\start(); - } - - /** - * Stop collection of code coverage information. - */ - public function stop(): array - { - \pcov\stop(); - - $waiting = \pcov\waiting(); - $collect = []; - - if ($waiting) { - $collect = \pcov\collect(\pcov\inclusive, $waiting); - - \pcov\clear(); - } - - return $collect; - } -} diff --git a/app/vendor/phpunit/php-code-coverage/src/Driver/PHPDBG.php b/app/vendor/phpunit/php-code-coverage/src/Driver/PHPDBG.php deleted file mode 100644 index e9f999a05..000000000 --- a/app/vendor/phpunit/php-code-coverage/src/Driver/PHPDBG.php +++ /dev/null @@ -1,96 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Driver; - -use SebastianBergmann\CodeCoverage\RuntimeException; - -/** - * Driver for PHPDBG's code coverage functionality. - * - * @codeCoverageIgnore - */ -final class PHPDBG implements Driver -{ - /** - * @throws RuntimeException - */ - public function __construct() - { - if (\PHP_SAPI !== 'phpdbg') { - throw new RuntimeException( - 'This driver requires the PHPDBG SAPI' - ); - } - - if (!\function_exists('phpdbg_start_oplog')) { - throw new RuntimeException( - 'This build of PHPDBG does not support code coverage' - ); - } - } - - /** - * Start collection of code coverage information. - */ - public function start(bool $determineUnusedAndDead = true): void - { - \phpdbg_start_oplog(); - } - - /** - * Stop collection of code coverage information. - */ - public function stop(): array - { - static $fetchedLines = []; - - $dbgData = \phpdbg_end_oplog(); - - if ($fetchedLines == []) { - $sourceLines = \phpdbg_get_executable(); - } else { - $newFiles = \array_diff(\get_included_files(), \array_keys($fetchedLines)); - - $sourceLines = []; - - if ($newFiles) { - $sourceLines = phpdbg_get_executable(['files' => $newFiles]); - } - } - - foreach ($sourceLines as $file => $lines) { - foreach ($lines as $lineNo => $numExecuted) { - $sourceLines[$file][$lineNo] = self::LINE_NOT_EXECUTED; - } - } - - $fetchedLines = \array_merge($fetchedLines, $sourceLines); - - return $this->detectExecutedLines($fetchedLines, $dbgData); - } - - /** - * Convert phpdbg based data into the format CodeCoverage expects - */ - private function detectExecutedLines(array $sourceLines, array $dbgData): array - { - foreach ($dbgData as $file => $coveredLines) { - foreach ($coveredLines as $lineNo => $numExecuted) { - // phpdbg also reports $lineNo=0 when e.g. exceptions get thrown. - // make sure we only mark lines executed which are actually executable. - if (isset($sourceLines[$file][$lineNo])) { - $sourceLines[$file][$lineNo] = self::LINE_EXECUTED; - } - } - } - - return $sourceLines; - } -} diff --git a/app/vendor/phpunit/php-code-coverage/src/Driver/PcovDriver.php b/app/vendor/phpunit/php-code-coverage/src/Driver/PcovDriver.php new file mode 100644 index 000000000..f4eca6031 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Driver/PcovDriver.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use function extension_loaded; +use function phpversion; +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\RawCodeCoverageData; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class PcovDriver extends Driver +{ + /** + * @var Filter + */ + private $filter; + + /** + * @throws PcovNotAvailableException + */ + public function __construct(Filter $filter) + { + if (!extension_loaded('pcov')) { + throw new PcovNotAvailableException; + } + + $this->filter = $filter; + } + + public function start(): void + { + \pcov\start(); + } + + public function stop(): RawCodeCoverageData + { + \pcov\stop(); + + $collect = \pcov\collect( + \pcov\inclusive, + !$this->filter->isEmpty() ? $this->filter->files() : \pcov\waiting() + ); + + \pcov\clear(); + + return RawCodeCoverageData::fromXdebugWithoutPathCoverage($collect); + } + + public function nameAndVersion(): string + { + return 'PCOV ' . phpversion('pcov'); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Driver/PhpdbgDriver.php b/app/vendor/phpunit/php-code-coverage/src/Driver/PhpdbgDriver.php new file mode 100644 index 000000000..7ee13b00f --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Driver/PhpdbgDriver.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use const PHP_SAPI; +use const PHP_VERSION; +use function array_diff; +use function array_keys; +use function array_merge; +use function get_included_files; +use function phpdbg_end_oplog; +use function phpdbg_get_executable; +use function phpdbg_start_oplog; +use SebastianBergmann\CodeCoverage\RawCodeCoverageData; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class PhpdbgDriver extends Driver +{ + /** + * @throws PhpdbgNotAvailableException + */ + public function __construct() + { + if (PHP_SAPI !== 'phpdbg') { + throw new PhpdbgNotAvailableException; + } + } + + public function start(): void + { + phpdbg_start_oplog(); + } + + public function stop(): RawCodeCoverageData + { + static $fetchedLines = []; + + $dbgData = phpdbg_end_oplog(); + + if ($fetchedLines === []) { + $sourceLines = phpdbg_get_executable(); + } else { + $newFiles = array_diff(get_included_files(), array_keys($fetchedLines)); + + $sourceLines = []; + + if ($newFiles) { + $sourceLines = phpdbg_get_executable(['files' => $newFiles]); + } + } + + foreach ($sourceLines as $file => $lines) { + foreach ($lines as $lineNo => $numExecuted) { + $sourceLines[$file][$lineNo] = self::LINE_NOT_EXECUTED; + } + } + + $fetchedLines = array_merge($fetchedLines, $sourceLines); + + return RawCodeCoverageData::fromXdebugWithoutPathCoverage( + $this->detectExecutedLines($fetchedLines, $dbgData) + ); + } + + public function nameAndVersion(): string + { + return 'PHPDBG ' . PHP_VERSION; + } + + private function detectExecutedLines(array $sourceLines, array $dbgData): array + { + foreach ($dbgData as $file => $coveredLines) { + foreach ($coveredLines as $lineNo => $numExecuted) { + // phpdbg also reports $lineNo=0 when e.g. exceptions get thrown. + // make sure we only mark lines executed which are actually executable. + if (isset($sourceLines[$file][$lineNo])) { + $sourceLines[$file][$lineNo] = self::LINE_EXECUTED; + } + } + } + + return $sourceLines; + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Driver/Selector.php b/app/vendor/phpunit/php-code-coverage/src/Driver/Selector.php new file mode 100644 index 000000000..936ee8981 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Driver/Selector.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use function phpversion; +use function version_compare; +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException; +use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException; +use SebastianBergmann\Environment\Runtime; + +final class Selector +{ + /** + * @throws NoCodeCoverageDriverAvailableException + * @throws PcovNotAvailableException + * @throws PhpdbgNotAvailableException + * @throws Xdebug2NotEnabledException + * @throws Xdebug3NotEnabledException + * @throws XdebugNotAvailableException + */ + public function forLineCoverage(Filter $filter): Driver + { + $runtime = new Runtime; + + if ($runtime->hasPHPDBGCodeCoverage()) { + return new PhpdbgDriver; + } + + if ($runtime->hasPCOV()) { + return new PcovDriver($filter); + } + + if ($runtime->hasXdebug()) { + if (version_compare(phpversion('xdebug'), '3', '>=')) { + $driver = new Xdebug3Driver($filter); + } else { + $driver = new Xdebug2Driver($filter); + } + + $driver->enableDeadCodeDetection(); + + return $driver; + } + + throw new NoCodeCoverageDriverAvailableException; + } + + /** + * @throws NoCodeCoverageDriverWithPathCoverageSupportAvailableException + * @throws Xdebug2NotEnabledException + * @throws Xdebug3NotEnabledException + * @throws XdebugNotAvailableException + */ + public function forLineAndPathCoverage(Filter $filter): Driver + { + if ((new Runtime)->hasXdebug()) { + if (version_compare(phpversion('xdebug'), '3', '>=')) { + $driver = new Xdebug3Driver($filter); + } else { + $driver = new Xdebug2Driver($filter); + } + + $driver->enableDeadCodeDetection(); + $driver->enableBranchAndPathCoverage(); + + return $driver; + } + + throw new NoCodeCoverageDriverWithPathCoverageSupportAvailableException; + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Driver/Xdebug.php b/app/vendor/phpunit/php-code-coverage/src/Driver/Xdebug.php deleted file mode 100644 index 06779eaae..000000000 --- a/app/vendor/phpunit/php-code-coverage/src/Driver/Xdebug.php +++ /dev/null @@ -1,123 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Driver; - -use SebastianBergmann\CodeCoverage\Filter; -use SebastianBergmann\CodeCoverage\RuntimeException; - -/** - * Driver for Xdebug's code coverage functionality. - * - * @codeCoverageIgnore - */ -final class Xdebug implements Driver -{ - /** - * @var array - */ - private $cacheNumLines = []; - - /** - * @var Filter - */ - private $filter; - - /** - * @throws RuntimeException - */ - public function __construct(Filter $filter = null) - { - if (!\extension_loaded('xdebug')) { - throw new RuntimeException('This driver requires Xdebug'); - } - - if (\version_compare(\phpversion('xdebug'), '3', '>=')) { - $mode = \getenv('XDEBUG_MODE'); - - if ($mode === false) { - $mode = \ini_get('xdebug.mode'); - } - - if ($mode === false || - !\in_array('coverage', \explode(',', $mode), true)) { - throw new RuntimeException('XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set'); - } - } elseif (!\ini_get('xdebug.coverage_enable')) { - throw new RuntimeException('xdebug.coverage_enable=On has to be set'); - } - - if ($filter === null) { - $filter = new Filter; - } - - $this->filter = $filter; - } - - /** - * Start collection of code coverage information. - */ - public function start(bool $determineUnusedAndDead = true): void - { - if ($determineUnusedAndDead) { - \xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); - } else { - \xdebug_start_code_coverage(); - } - } - - /** - * Stop collection of code coverage information. - */ - public function stop(): array - { - $data = \xdebug_get_code_coverage(); - - \xdebug_stop_code_coverage(); - - return $this->cleanup($data); - } - - private function cleanup(array $data): array - { - foreach (\array_keys($data) as $file) { - unset($data[$file][0]); - - if (!$this->filter->isFile($file)) { - continue; - } - - $numLines = $this->getNumberOfLinesInFile($file); - - foreach (\array_keys($data[$file]) as $line) { - if ($line > $numLines) { - unset($data[$file][$line]); - } - } - } - - return $data; - } - - private function getNumberOfLinesInFile(string $fileName): int - { - if (!isset($this->cacheNumLines[$fileName])) { - $buffer = \file_get_contents($fileName); - $lines = \substr_count($buffer, "\n"); - - if (\substr($buffer, -1) !== "\n") { - $lines++; - } - - $this->cacheNumLines[$fileName] = $lines; - } - - return $this->cacheNumLines[$fileName]; - } -} diff --git a/app/vendor/phpunit/php-code-coverage/src/Driver/Xdebug2Driver.php b/app/vendor/phpunit/php-code-coverage/src/Driver/Xdebug2Driver.php new file mode 100644 index 000000000..74cbbfbcd --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Driver/Xdebug2Driver.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use const XDEBUG_CC_BRANCH_CHECK; +use const XDEBUG_CC_DEAD_CODE; +use const XDEBUG_CC_UNUSED; +use const XDEBUG_FILTER_CODE_COVERAGE; +use const XDEBUG_PATH_INCLUDE; +use const XDEBUG_PATH_WHITELIST; +use function defined; +use function extension_loaded; +use function ini_get; +use function phpversion; +use function sprintf; +use function version_compare; +use function xdebug_get_code_coverage; +use function xdebug_set_filter; +use function xdebug_start_code_coverage; +use function xdebug_stop_code_coverage; +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\RawCodeCoverageData; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Xdebug2Driver extends Driver +{ + /** + * @var bool + */ + private $pathCoverageIsMixedCoverage; + + /** + * @throws WrongXdebugVersionException + * @throws Xdebug2NotEnabledException + * @throws XdebugNotAvailableException + */ + public function __construct(Filter $filter) + { + if (!extension_loaded('xdebug')) { + throw new XdebugNotAvailableException; + } + + if (version_compare(phpversion('xdebug'), '3', '>=')) { + throw new WrongXdebugVersionException( + sprintf( + 'This driver requires Xdebug 2 but version %s is loaded', + phpversion('xdebug') + ) + ); + } + + if (!ini_get('xdebug.coverage_enable')) { + throw new Xdebug2NotEnabledException; + } + + if (!$filter->isEmpty()) { + if (defined('XDEBUG_PATH_WHITELIST')) { + $listType = XDEBUG_PATH_WHITELIST; + } else { + $listType = XDEBUG_PATH_INCLUDE; + } + + xdebug_set_filter( + XDEBUG_FILTER_CODE_COVERAGE, + $listType, + $filter->files() + ); + } + + $this->pathCoverageIsMixedCoverage = version_compare(phpversion('xdebug'), '2.9.6', '<'); + } + + public function canCollectBranchAndPathCoverage(): bool + { + return true; + } + + public function canDetectDeadCode(): bool + { + return true; + } + + public function start(): void + { + $flags = XDEBUG_CC_UNUSED; + + if ($this->detectsDeadCode() || $this->collectsBranchAndPathCoverage()) { + $flags |= XDEBUG_CC_DEAD_CODE; + } + + if ($this->collectsBranchAndPathCoverage()) { + $flags |= XDEBUG_CC_BRANCH_CHECK; + } + + xdebug_start_code_coverage($flags); + } + + public function stop(): RawCodeCoverageData + { + $data = xdebug_get_code_coverage(); + + xdebug_stop_code_coverage(); + + if ($this->collectsBranchAndPathCoverage()) { + if ($this->pathCoverageIsMixedCoverage) { + return RawCodeCoverageData::fromXdebugWithMixedCoverage($data); + } + + return RawCodeCoverageData::fromXdebugWithPathCoverage($data); + } + + return RawCodeCoverageData::fromXdebugWithoutPathCoverage($data); + } + + public function nameAndVersion(): string + { + return 'Xdebug ' . phpversion('xdebug'); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Driver/Xdebug3Driver.php b/app/vendor/phpunit/php-code-coverage/src/Driver/Xdebug3Driver.php new file mode 100644 index 000000000..b85db4034 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Driver/Xdebug3Driver.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use const XDEBUG_CC_BRANCH_CHECK; +use const XDEBUG_CC_DEAD_CODE; +use const XDEBUG_CC_UNUSED; +use const XDEBUG_FILTER_CODE_COVERAGE; +use const XDEBUG_PATH_INCLUDE; +use function explode; +use function extension_loaded; +use function getenv; +use function in_array; +use function ini_get; +use function phpversion; +use function sprintf; +use function version_compare; +use function xdebug_get_code_coverage; +use function xdebug_set_filter; +use function xdebug_start_code_coverage; +use function xdebug_stop_code_coverage; +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\RawCodeCoverageData; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Xdebug3Driver extends Driver +{ + /** + * @throws WrongXdebugVersionException + * @throws Xdebug3NotEnabledException + * @throws XdebugNotAvailableException + */ + public function __construct(Filter $filter) + { + if (!extension_loaded('xdebug')) { + throw new XdebugNotAvailableException; + } + + if (version_compare(phpversion('xdebug'), '3', '<')) { + throw new WrongXdebugVersionException( + sprintf( + 'This driver requires Xdebug 3 but version %s is loaded', + phpversion('xdebug') + ) + ); + } + + $mode = getenv('XDEBUG_MODE'); + + if ($mode === false || $mode === '') { + $mode = ini_get('xdebug.mode'); + } + + if ($mode === false || + !in_array('coverage', explode(',', $mode), true)) { + throw new Xdebug3NotEnabledException; + } + + if (!$filter->isEmpty()) { + xdebug_set_filter( + XDEBUG_FILTER_CODE_COVERAGE, + XDEBUG_PATH_INCLUDE, + $filter->files() + ); + } + } + + public function canCollectBranchAndPathCoverage(): bool + { + return true; + } + + public function canDetectDeadCode(): bool + { + return true; + } + + public function start(): void + { + $flags = XDEBUG_CC_UNUSED; + + if ($this->detectsDeadCode() || $this->collectsBranchAndPathCoverage()) { + $flags |= XDEBUG_CC_DEAD_CODE; + } + + if ($this->collectsBranchAndPathCoverage()) { + $flags |= XDEBUG_CC_BRANCH_CHECK; + } + + xdebug_start_code_coverage($flags); + } + + public function stop(): RawCodeCoverageData + { + $data = xdebug_get_code_coverage(); + + xdebug_stop_code_coverage(); + + if ($this->collectsBranchAndPathCoverage()) { + return RawCodeCoverageData::fromXdebugWithPathCoverage($data); + } + + return RawCodeCoverageData::fromXdebugWithoutPathCoverage($data); + } + + public function nameAndVersion(): string + { + return 'Xdebug ' . phpversion('xdebug'); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/BranchAndPathCoverageNotSupportedException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/BranchAndPathCoverageNotSupportedException.php new file mode 100644 index 000000000..ab2089197 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/BranchAndPathCoverageNotSupportedException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class BranchAndPathCoverageNotSupportedException extends RuntimeException implements Exception +{ +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php deleted file mode 100644 index a88ab34fb..000000000 --- a/app/vendor/phpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage; - -/** - * Exception that is raised when covered code is not executed. - */ -final class CoveredCodeNotExecutedException extends RuntimeException -{ -} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/DeadCodeDetectionNotSupportedException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/DeadCodeDetectionNotSupportedException.php new file mode 100644 index 000000000..d36006489 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/DeadCodeDetectionNotSupportedException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class DeadCodeDetectionNotSupportedException extends RuntimeException implements Exception +{ +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/DirectoryCouldNotBeCreatedException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/DirectoryCouldNotBeCreatedException.php new file mode 100644 index 000000000..c1e9213ac --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/DirectoryCouldNotBeCreatedException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class DirectoryCouldNotBeCreatedException extends RuntimeException implements Exception +{ +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/Exception.php b/app/vendor/phpunit/php-code-coverage/src/Exception/Exception.php index 32bf894d9..28dc48b8a 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Exception/Exception.php +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/Exception.php @@ -1,6 +1,6 @@ * @@ -9,9 +9,8 @@ */ namespace SebastianBergmann\CodeCoverage; -/** - * Exception interface for php-code-coverage component. - */ -interface Exception +use Throwable; + +interface Exception extends Throwable { } diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/InvalidArgumentException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/InvalidArgumentException.php index cf2fcfccc..17e4b7076 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Exception/InvalidArgumentException.php +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/InvalidArgumentException.php @@ -1,6 +1,6 @@ * @@ -11,26 +11,4 @@ final class InvalidArgumentException extends \InvalidArgumentException implements Exception { - /** - * @param int $argument - * @param string $type - * @param null|mixed $value - * - * @return InvalidArgumentException - */ - public static function create($argument, $type, $value = null): self - { - $stack = \debug_backtrace(0); - - return new self( - \sprintf( - 'Argument #%d%sof %s::%s() must be a %s', - $argument, - $value !== null ? ' (' . \gettype($value) . '#' . $value . ')' : ' (No Value) ', - $stack[1]['class'], - $stack[1]['function'], - $type - ) - ); - } } diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/MissingCoversAnnotationException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/MissingCoversAnnotationException.php deleted file mode 100644 index 56c47363f..000000000 --- a/app/vendor/phpunit/php-code-coverage/src/Exception/MissingCoversAnnotationException.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage; - -/** - * Exception that is raised when @covers must be used but is not. - */ -final class MissingCoversAnnotationException extends RuntimeException -{ -} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverAvailableException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverAvailableException.php new file mode 100644 index 000000000..b1494e267 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverAvailableException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class NoCodeCoverageDriverAvailableException extends RuntimeException implements Exception +{ + public function __construct() + { + parent::__construct('No code coverage driver available'); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php new file mode 100644 index 000000000..0065b740d --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class NoCodeCoverageDriverWithPathCoverageSupportAvailableException extends RuntimeException implements Exception +{ + public function __construct() + { + parent::__construct('No code coverage driver with path coverage support available'); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/ParserException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/ParserException.php new file mode 100644 index 000000000..a907e34e8 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/ParserException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class ParserException extends RuntimeException implements Exception +{ +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/PathExistsButIsNotDirectoryException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/PathExistsButIsNotDirectoryException.php new file mode 100644 index 000000000..54bd73f58 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/PathExistsButIsNotDirectoryException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use function sprintf; +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class PathExistsButIsNotDirectoryException extends RuntimeException implements Exception +{ + public function __construct(string $path) + { + parent::__construct(sprintf('"%s" exists but is not a directory', $path)); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/PcovNotAvailableException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/PcovNotAvailableException.php new file mode 100644 index 000000000..2f0a66e5a --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/PcovNotAvailableException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class PcovNotAvailableException extends RuntimeException implements Exception +{ + public function __construct() + { + parent::__construct('The PCOV extension is not available'); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/PhpdbgNotAvailableException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/PhpdbgNotAvailableException.php new file mode 100644 index 000000000..bfb183d5c --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/PhpdbgNotAvailableException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class PhpdbgNotAvailableException extends RuntimeException implements Exception +{ + public function __construct() + { + parent::__construct('The PHPDBG SAPI is not available'); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/ReflectionException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/ReflectionException.php new file mode 100644 index 000000000..78db430be --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/ReflectionException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class ReflectionException extends RuntimeException implements Exception +{ +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/ReportAlreadyFinalizedException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/ReportAlreadyFinalizedException.php new file mode 100644 index 000000000..0481f1610 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/ReportAlreadyFinalizedException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class ReportAlreadyFinalizedException extends RuntimeException implements Exception +{ + public function __construct() + { + parent::__construct('The code coverage report has already been finalized'); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/RuntimeException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/RuntimeException.php deleted file mode 100644 index 608650d88..000000000 --- a/app/vendor/phpunit/php-code-coverage/src/Exception/RuntimeException.php +++ /dev/null @@ -1,14 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage; - -class RuntimeException extends \RuntimeException implements Exception -{ -} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/StaticAnalysisCacheNotConfiguredException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/StaticAnalysisCacheNotConfiguredException.php new file mode 100644 index 000000000..fd58fd6b6 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/StaticAnalysisCacheNotConfiguredException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class StaticAnalysisCacheNotConfiguredException extends RuntimeException implements Exception +{ +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/TestIdMissingException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/TestIdMissingException.php new file mode 100644 index 000000000..4cc3e0c2b --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/TestIdMissingException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class TestIdMissingException extends RuntimeException implements Exception +{ + public function __construct() + { + parent::__construct('Test ID is missing'); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php index ef219b53f..cb7a975f7 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php @@ -1,6 +1,6 @@ * @@ -9,15 +9,14 @@ */ namespace SebastianBergmann\CodeCoverage; -/** - * Exception that is raised when code is unintentionally covered. - */ -final class UnintentionallyCoveredCodeException extends RuntimeException +use RuntimeException; + +final class UnintentionallyCoveredCodeException extends RuntimeException implements Exception { /** * @var array */ - private $unintentionallyCoveredUnits = []; + private $unintentionallyCoveredUnits; public function __construct(array $unintentionallyCoveredUnits) { diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/WriteOperationFailedException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/WriteOperationFailedException.php new file mode 100644 index 000000000..be549e17b --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/WriteOperationFailedException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use function sprintf; +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class WriteOperationFailedException extends RuntimeException implements Exception +{ + public function __construct(string $path) + { + parent::__construct(sprintf('Cannot write to "%s"', $path)); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/WrongXdebugVersionException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/WrongXdebugVersionException.php new file mode 100644 index 000000000..6e8f10a92 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/WrongXdebugVersionException.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class WrongXdebugVersionException extends RuntimeException implements Exception +{ +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/Xdebug2NotEnabledException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/Xdebug2NotEnabledException.php new file mode 100644 index 000000000..3039e77c0 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/Xdebug2NotEnabledException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class Xdebug2NotEnabledException extends RuntimeException implements Exception +{ + public function __construct() + { + parent::__construct('xdebug.coverage_enable=On has to be set'); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/Xdebug3NotEnabledException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/Xdebug3NotEnabledException.php new file mode 100644 index 000000000..5d3b106ce --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/Xdebug3NotEnabledException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class Xdebug3NotEnabledException extends RuntimeException implements Exception +{ + public function __construct() + { + parent::__construct('XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set'); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/XdebugNotAvailableException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/XdebugNotAvailableException.php new file mode 100644 index 000000000..1622c5a63 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/XdebugNotAvailableException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use RuntimeException; +use SebastianBergmann\CodeCoverage\Exception; + +final class XdebugNotAvailableException extends RuntimeException implements Exception +{ + public function __construct() + { + parent::__construct('The Xdebug extension is not available'); + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Exception/XmlException.php b/app/vendor/phpunit/php-code-coverage/src/Exception/XmlException.php new file mode 100644 index 000000000..31e4623df --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Exception/XmlException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use RuntimeException; + +final class XmlException extends RuntimeException implements Exception +{ +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Filter.php b/app/vendor/phpunit/php-code-coverage/src/Filter.php index b3c2d2d69..ef0a2c62f 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Filter.php +++ b/app/vendor/phpunit/php-code-coverage/src/Filter.php @@ -1,6 +1,6 @@ * @@ -9,166 +9,114 @@ */ namespace SebastianBergmann\CodeCoverage; +use function array_keys; +use function is_file; +use function realpath; +use function strpos; use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; -/** - * Filter for whitelisting of code coverage information. - */ final class Filter { /** - * Source files that are whitelisted. - * - * @var array + * @psalm-var array */ - private $whitelistedFiles = []; + private $files = []; /** - * Remembers the result of the `is_file()` calls. - * - * @var bool[] + * @psalm-var array */ - private $isFileCallsCache = []; + private $isFileCache = []; - /** - * Adds a directory to the whitelist (recursively). - */ - public function addDirectoryToWhitelist(string $directory, string $suffix = '.php', string $prefix = ''): void + public function includeDirectory(string $directory, string $suffix = '.php', string $prefix = ''): void { - $facade = new FileIteratorFacade; - $files = $facade->getFilesAsArray($directory, $suffix, $prefix); - - foreach ($files as $file) { - $this->addFileToWhitelist($file); + foreach ((new FileIteratorFacade)->getFilesAsArray($directory, $suffix, $prefix) as $file) { + $this->includeFile($file); } } /** - * Adds a file to the whitelist. + * @psalm-param list $files */ - public function addFileToWhitelist(string $filename): void + public function includeFiles(array $filenames): void { - $filename = \realpath($filename); + foreach ($filenames as $filename) { + $this->includeFile($filename); + } + } + + public function includeFile(string $filename): void + { + $filename = realpath($filename); if (!$filename) { return; } - $this->whitelistedFiles[$filename] = true; + $this->files[$filename] = true; } - /** - * Adds files to the whitelist. - * - * @param string[] $files - */ - public function addFilesToWhitelist(array $files): void + public function excludeDirectory(string $directory, string $suffix = '.php', string $prefix = ''): void { - foreach ($files as $file) { - $this->addFileToWhitelist($file); + foreach ((new FileIteratorFacade)->getFilesAsArray($directory, $suffix, $prefix) as $file) { + $this->excludeFile($file); } } - /** - * Removes a directory from the whitelist (recursively). - */ - public function removeDirectoryFromWhitelist(string $directory, string $suffix = '.php', string $prefix = ''): void + public function excludeFile(string $filename): void { - $facade = new FileIteratorFacade; - $files = $facade->getFilesAsArray($directory, $suffix, $prefix); + $filename = realpath($filename); - foreach ($files as $file) { - $this->removeFileFromWhitelist($file); - } - } - - /** - * Removes a file from the whitelist. - */ - public function removeFileFromWhitelist(string $filename): void - { - $filename = \realpath($filename); - - if (!$filename || !isset($this->whitelistedFiles[$filename])) { + if (!$filename || !isset($this->files[$filename])) { return; } - unset($this->whitelistedFiles[$filename]); + unset($this->files[$filename]); } - /** - * Checks whether a filename is a real filename. - */ public function isFile(string $filename): bool { - if (isset($this->isFileCallsCache[$filename])) { - return $this->isFileCallsCache[$filename]; + if (isset($this->isFileCache[$filename])) { + return $this->isFileCache[$filename]; } if ($filename === '-' || - \strpos($filename, 'vfs://') === 0 || - \strpos($filename, 'xdebug://debug-eval') !== false || - \strpos($filename, 'eval()\'d code') !== false || - \strpos($filename, 'runtime-created function') !== false || - \strpos($filename, 'runkit created function') !== false || - \strpos($filename, 'assert code') !== false || - \strpos($filename, 'regexp code') !== false || - \strpos($filename, 'Standard input code') !== false) { + strpos($filename, 'vfs://') === 0 || + strpos($filename, 'xdebug://debug-eval') !== false || + strpos($filename, 'eval()\'d code') !== false || + strpos($filename, 'runtime-created function') !== false || + strpos($filename, 'runkit created function') !== false || + strpos($filename, 'assert code') !== false || + strpos($filename, 'regexp code') !== false || + strpos($filename, 'Standard input code') !== false) { $isFile = false; } else { - $isFile = \file_exists($filename); + $isFile = is_file($filename); } - $this->isFileCallsCache[$filename] = $isFile; + $this->isFileCache[$filename] = $isFile; return $isFile; } - /** - * Checks whether or not a file is filtered. - */ - public function isFiltered(string $filename): bool + public function isExcluded(string $filename): bool { if (!$this->isFile($filename)) { return true; } - return !isset($this->whitelistedFiles[$filename]); + return !isset($this->files[$filename]); } /** - * Returns the list of whitelisted files. - * - * @return string[] + * @psalm-return list */ - public function getWhitelist(): array + public function files(): array { - return \array_keys($this->whitelistedFiles); + return array_keys($this->files); } - /** - * Returns whether this filter has a whitelist. - */ - public function hasWhitelist(): bool - { - return !empty($this->whitelistedFiles); - } - - /** - * Returns the whitelisted files. - * - * @return string[] - */ - public function getWhitelistedFiles(): array - { - return $this->whitelistedFiles; - } - - /** - * Sets the whitelisted files. - */ - public function setWhitelistedFiles(array $whitelistedFiles): void + public function isEmpty(): bool { - $this->whitelistedFiles = $whitelistedFiles; + return empty($this->files); } } diff --git a/app/vendor/phpunit/php-code-coverage/src/Node/AbstractNode.php b/app/vendor/phpunit/php-code-coverage/src/Node/AbstractNode.php index 116a09f17..bfa672902 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Node/AbstractNode.php +++ b/app/vendor/phpunit/php-code-coverage/src/Node/AbstractNode.php @@ -1,6 +1,6 @@ * @@ -9,12 +9,18 @@ */ namespace SebastianBergmann\CodeCoverage\Node; -use SebastianBergmann\CodeCoverage\Util; +use const DIRECTORY_SEPARATOR; +use function array_merge; +use function str_replace; +use function substr; +use Countable; +use SebastianBergmann\CodeCoverage\Percentage; +use SebastianBergmann\LinesOfCode\LinesOfCode; /** - * Base class for nodes in the code coverage information tree. + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -abstract class AbstractNode implements \Countable +abstract class AbstractNode implements Countable { /** * @var string @@ -24,12 +30,12 @@ abstract class AbstractNode implements \Countable /** * @var string */ - private $path; + private $pathAsString; /** * @var array */ - private $pathArray; + private $pathAsArray; /** * @var AbstractNode @@ -43,31 +49,31 @@ abstract class AbstractNode implements \Countable public function __construct(string $name, self $parent = null) { - if (\substr($name, -1) == \DIRECTORY_SEPARATOR) { - $name = \substr($name, 0, -1); + if (substr($name, -1) === DIRECTORY_SEPARATOR) { + $name = substr($name, 0, -1); } $this->name = $name; $this->parent = $parent; } - public function getName(): string + public function name(): string { return $this->name; } - public function getId(): string + public function id(): string { if ($this->id === null) { - $parent = $this->getParent(); + $parent = $this->parent(); if ($parent === null) { $this->id = 'index'; } else { - $parentId = $parent->getId(); + $parentId = $parent->id(); if ($parentId === 'index') { - $this->id = \str_replace(':', '_', $this->name); + $this->id = str_replace(':', '_', $this->name); } else { $this->id = $parentId . '/' . $this->name; } @@ -77,252 +83,169 @@ public function getId(): string return $this->id; } - public function getPath(): string + public function pathAsString(): string { - if ($this->path === null) { - if ($this->parent === null || $this->parent->getPath() === null || $this->parent->getPath() === false) { - $this->path = $this->name; + if ($this->pathAsString === null) { + if ($this->parent === null) { + $this->pathAsString = $this->name; } else { - $this->path = $this->parent->getPath() . \DIRECTORY_SEPARATOR . $this->name; + $this->pathAsString = $this->parent->pathAsString() . DIRECTORY_SEPARATOR . $this->name; } } - return $this->path; + return $this->pathAsString; } - public function getPathAsArray(): array + public function pathAsArray(): array { - if ($this->pathArray === null) { + if ($this->pathAsArray === null) { if ($this->parent === null) { - $this->pathArray = []; + $this->pathAsArray = []; } else { - $this->pathArray = $this->parent->getPathAsArray(); + $this->pathAsArray = $this->parent->pathAsArray(); } - $this->pathArray[] = $this; + $this->pathAsArray[] = $this; } - return $this->pathArray; + return $this->pathAsArray; } - public function getParent(): ?self + public function parent(): ?self { return $this->parent; } - /** - * Returns the percentage of classes that has been tested. - * - * @return int|string - */ - public function getTestedClassesPercent(bool $asString = true) + public function percentageOfTestedClasses(): Percentage { - return Util::percent( - $this->getNumTestedClasses(), - $this->getNumClasses(), - $asString + return Percentage::fromFractionAndTotal( + $this->numberOfTestedClasses(), + $this->numberOfClasses(), ); } - /** - * Returns the percentage of traits that has been tested. - * - * @return int|string - */ - public function getTestedTraitsPercent(bool $asString = true) + public function percentageOfTestedTraits(): Percentage { - return Util::percent( - $this->getNumTestedTraits(), - $this->getNumTraits(), - $asString + return Percentage::fromFractionAndTotal( + $this->numberOfTestedTraits(), + $this->numberOfTraits(), ); } - /** - * Returns the percentage of classes and traits that has been tested. - * - * @return int|string - */ - public function getTestedClassesAndTraitsPercent(bool $asString = true) + public function percentageOfTestedClassesAndTraits(): Percentage { - return Util::percent( - $this->getNumTestedClassesAndTraits(), - $this->getNumClassesAndTraits(), - $asString + return Percentage::fromFractionAndTotal( + $this->numberOfTestedClassesAndTraits(), + $this->numberOfClassesAndTraits(), ); } - /** - * Returns the percentage of functions that has been tested. - * - * @return int|string - */ - public function getTestedFunctionsPercent(bool $asString = true) + public function percentageOfTestedFunctions(): Percentage { - return Util::percent( - $this->getNumTestedFunctions(), - $this->getNumFunctions(), - $asString + return Percentage::fromFractionAndTotal( + $this->numberOfTestedFunctions(), + $this->numberOfFunctions(), ); } - /** - * Returns the percentage of methods that has been tested. - * - * @return int|string - */ - public function getTestedMethodsPercent(bool $asString = true) + public function percentageOfTestedMethods(): Percentage { - return Util::percent( - $this->getNumTestedMethods(), - $this->getNumMethods(), - $asString + return Percentage::fromFractionAndTotal( + $this->numberOfTestedMethods(), + $this->numberOfMethods(), ); } - /** - * Returns the percentage of functions and methods that has been tested. - * - * @return int|string - */ - public function getTestedFunctionsAndMethodsPercent(bool $asString = true) + public function percentageOfTestedFunctionsAndMethods(): Percentage { - return Util::percent( - $this->getNumTestedFunctionsAndMethods(), - $this->getNumFunctionsAndMethods(), - $asString + return Percentage::fromFractionAndTotal( + $this->numberOfTestedFunctionsAndMethods(), + $this->numberOfFunctionsAndMethods(), ); } - /** - * Returns the percentage of executed lines. - * - * @return int|string - */ - public function getLineExecutedPercent(bool $asString = true) + public function percentageOfExecutedLines(): Percentage { - return Util::percent( - $this->getNumExecutedLines(), - $this->getNumExecutableLines(), - $asString + return Percentage::fromFractionAndTotal( + $this->numberOfExecutedLines(), + $this->numberOfExecutableLines(), ); } - /** - * Returns the number of classes and traits. - */ - public function getNumClassesAndTraits(): int + public function percentageOfExecutedBranches(): Percentage { - return $this->getNumClasses() + $this->getNumTraits(); + return Percentage::fromFractionAndTotal( + $this->numberOfExecutedBranches(), + $this->numberOfExecutableBranches() + ); } - /** - * Returns the number of tested classes and traits. - */ - public function getNumTestedClassesAndTraits(): int + public function percentageOfExecutedPaths(): Percentage { - return $this->getNumTestedClasses() + $this->getNumTestedTraits(); + return Percentage::fromFractionAndTotal( + $this->numberOfExecutedPaths(), + $this->numberOfExecutablePaths() + ); } - /** - * Returns the classes and traits of this node. - */ - public function getClassesAndTraits(): array + public function numberOfClassesAndTraits(): int { - return \array_merge($this->getClasses(), $this->getTraits()); + return $this->numberOfClasses() + $this->numberOfTraits(); } - /** - * Returns the number of functions and methods. - */ - public function getNumFunctionsAndMethods(): int + public function numberOfTestedClassesAndTraits(): int { - return $this->getNumFunctions() + $this->getNumMethods(); + return $this->numberOfTestedClasses() + $this->numberOfTestedTraits(); } - /** - * Returns the number of tested functions and methods. - */ - public function getNumTestedFunctionsAndMethods(): int + public function classesAndTraits(): array { - return $this->getNumTestedFunctions() + $this->getNumTestedMethods(); + return array_merge($this->classes(), $this->traits()); } - /** - * Returns the functions and methods of this node. - */ - public function getFunctionsAndMethods(): array + public function numberOfFunctionsAndMethods(): int { - return \array_merge($this->getFunctions(), $this->getMethods()); + return $this->numberOfFunctions() + $this->numberOfMethods(); } - /** - * Returns the classes of this node. - */ - abstract public function getClasses(): array; + public function numberOfTestedFunctionsAndMethods(): int + { + return $this->numberOfTestedFunctions() + $this->numberOfTestedMethods(); + } - /** - * Returns the traits of this node. - */ - abstract public function getTraits(): array; + abstract public function classes(): array; - /** - * Returns the functions of this node. - */ - abstract public function getFunctions(): array; + abstract public function traits(): array; - /** - * Returns the LOC/CLOC/NCLOC of this node. - */ - abstract public function getLinesOfCode(): array; + abstract public function functions(): array; - /** - * Returns the number of executable lines. - */ - abstract public function getNumExecutableLines(): int; + abstract public function linesOfCode(): LinesOfCode; - /** - * Returns the number of executed lines. - */ - abstract public function getNumExecutedLines(): int; + abstract public function numberOfExecutableLines(): int; - /** - * Returns the number of classes. - */ - abstract public function getNumClasses(): int; + abstract public function numberOfExecutedLines(): int; - /** - * Returns the number of tested classes. - */ - abstract public function getNumTestedClasses(): int; + abstract public function numberOfExecutableBranches(): int; - /** - * Returns the number of traits. - */ - abstract public function getNumTraits(): int; + abstract public function numberOfExecutedBranches(): int; - /** - * Returns the number of tested traits. - */ - abstract public function getNumTestedTraits(): int; + abstract public function numberOfExecutablePaths(): int; - /** - * Returns the number of methods. - */ - abstract public function getNumMethods(): int; + abstract public function numberOfExecutedPaths(): int; - /** - * Returns the number of tested methods. - */ - abstract public function getNumTestedMethods(): int; + abstract public function numberOfClasses(): int; - /** - * Returns the number of functions. - */ - abstract public function getNumFunctions(): int; + abstract public function numberOfTestedClasses(): int; - /** - * Returns the number of tested functions. - */ - abstract public function getNumTestedFunctions(): int; + abstract public function numberOfTraits(): int; + + abstract public function numberOfTestedTraits(): int; + + abstract public function numberOfMethods(): int; + + abstract public function numberOfTestedMethods(): int; + + abstract public function numberOfFunctions(): int; + + abstract public function numberOfTestedFunctions(): int; } diff --git a/app/vendor/phpunit/php-code-coverage/src/Node/Builder.php b/app/vendor/phpunit/php-code-coverage/src/Node/Builder.php index 5e34bccfc..c2aa1d180 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Node/Builder.php +++ b/app/vendor/phpunit/php-code-coverage/src/Node/Builder.php @@ -1,6 +1,6 @@ * @@ -9,14 +9,40 @@ */ namespace SebastianBergmann\CodeCoverage\Node; +use const DIRECTORY_SEPARATOR; +use function array_shift; +use function basename; +use function count; +use function dirname; +use function explode; +use function implode; +use function is_file; +use function str_replace; +use function strpos; +use function substr; use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData; +use SebastianBergmann\CodeCoverage\StaticAnalysis\CoveredFileAnalyser; +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ final class Builder { + /** + * @var CoveredFileAnalyser + */ + private $coveredFileAnalyser; + + public function __construct(CoveredFileAnalyser $coveredFileAnalyser) + { + $this->coveredFileAnalyser = $coveredFileAnalyser; + } + public function build(CodeCoverage $coverage): Directory { - $files = $coverage->getData(); - $commonPath = $this->reducePaths($files); + $data = clone $coverage->getData(); // clone because path munging is destructive to the original data + $commonPath = $this->reducePaths($data); $root = new Directory( $commonPath, null @@ -24,28 +50,41 @@ public function build(CodeCoverage $coverage): Directory $this->addItems( $root, - $this->buildDirectoryStructure($files), - $coverage->getTests(), - $coverage->getCacheTokens() + $this->buildDirectoryStructure($data), + $coverage->getTests() ); return $root; } - private function addItems(Directory $root, array $items, array $tests, bool $cacheTokens): void + private function addItems(Directory $root, array $items, array $tests): void { foreach ($items as $key => $value) { $key = (string) $key; - if (\substr($key, -2) === '/f') { - $key = \substr($key, 0, -2); - - if (\file_exists($root->getPath() . \DIRECTORY_SEPARATOR . $key)) { - $root->addFile($key, $value, $tests, $cacheTokens); + if (substr($key, -2) === '/f') { + $key = substr($key, 0, -2); + $filename = $root->pathAsString() . DIRECTORY_SEPARATOR . $key; + + if (is_file($filename)) { + $root->addFile( + new File( + $key, + $root, + $value['lineCoverage'], + $value['functionCoverage'], + $tests, + $this->coveredFileAnalyser->classesIn($filename), + $this->coveredFileAnalyser->traitsIn($filename), + $this->coveredFileAnalyser->functionsIn($filename), + $this->coveredFileAnalyser->linesOfCodeFor($filename) + ) + ); } } else { $child = $root->addDirectory($key); - $this->addItems($child, $value, $tests, $cacheTokens); + + $this->addItems($child, $value, $tests); } } } @@ -90,14 +129,14 @@ private function addItems(Directory $root, array $items, array $tests, bool $cac * ) *
*/ - private function buildDirectoryStructure(array $files): array + private function buildDirectoryStructure(ProcessedCodeCoverageData $data): array { $result = []; - foreach ($files as $path => $file) { - $path = \explode(\DIRECTORY_SEPARATOR, $path); + foreach ($data->coveredFiles() as $originalPath) { + $path = explode(DIRECTORY_SEPARATOR, $originalPath); $pointer = &$result; - $max = \count($path); + $max = count($path); for ($i = 0; $i < $max; $i++) { $type = ''; @@ -109,7 +148,10 @@ private function buildDirectoryStructure(array $files): array $pointer = &$pointer[$path[$i] . $type]; } - $pointer = $file; + $pointer = [ + 'lineCoverage' => $data->lineCoverage()[$originalPath] ?? [], + 'functionCoverage' => $data->functionCoverage()[$originalPath] ?? [], + ]; } return $result; @@ -152,41 +194,39 @@ private function buildDirectoryStructure(array $files): array * ) * */ - private function reducePaths(array &$files): string + private function reducePaths(ProcessedCodeCoverageData $coverage): string { - if (empty($files)) { + if (empty($coverage->coveredFiles())) { return '.'; } $commonPath = ''; - $paths = \array_keys($files); + $paths = $coverage->coveredFiles(); - if (\count($files) === 1) { - $commonPath = \dirname($paths[0]) . \DIRECTORY_SEPARATOR; - $files[\basename($paths[0])] = $files[$paths[0]]; - - unset($files[$paths[0]]); + if (count($paths) === 1) { + $commonPath = dirname($paths[0]) . DIRECTORY_SEPARATOR; + $coverage->renameFile($paths[0], basename($paths[0])); return $commonPath; } - $max = \count($paths); + $max = count($paths); for ($i = 0; $i < $max; $i++) { // strip phar:// prefixes - if (\strpos($paths[$i], 'phar://') === 0) { - $paths[$i] = \substr($paths[$i], 7); - $paths[$i] = \str_replace('/', \DIRECTORY_SEPARATOR, $paths[$i]); + if (strpos($paths[$i], 'phar://') === 0) { + $paths[$i] = substr($paths[$i], 7); + $paths[$i] = str_replace('/', DIRECTORY_SEPARATOR, $paths[$i]); } - $paths[$i] = \explode(\DIRECTORY_SEPARATOR, $paths[$i]); + $paths[$i] = explode(DIRECTORY_SEPARATOR, $paths[$i]); if (empty($paths[$i][0])) { - $paths[$i][0] = \DIRECTORY_SEPARATOR; + $paths[$i][0] = DIRECTORY_SEPARATOR; } } $done = false; - $max = \count($paths); + $max = count($paths); while (!$done) { for ($i = 0; $i < $max - 1; $i++) { @@ -202,26 +242,23 @@ private function reducePaths(array &$files): string if (!$done) { $commonPath .= $paths[0][0]; - if ($paths[0][0] !== \DIRECTORY_SEPARATOR) { - $commonPath .= \DIRECTORY_SEPARATOR; + if ($paths[0][0] !== DIRECTORY_SEPARATOR) { + $commonPath .= DIRECTORY_SEPARATOR; } for ($i = 0; $i < $max; $i++) { - \array_shift($paths[$i]); + array_shift($paths[$i]); } } } - $original = \array_keys($files); - $max = \count($original); + $original = $coverage->coveredFiles(); + $max = count($original); for ($i = 0; $i < $max; $i++) { - $files[\implode(\DIRECTORY_SEPARATOR, $paths[$i])] = $files[$original[$i]]; - unset($files[$original[$i]]); + $coverage->renameFile($original[$i], implode(DIRECTORY_SEPARATOR, $paths[$i])); } - \ksort($files); - - return \substr($commonPath, 0, -1); + return substr($commonPath, 0, -1); } } diff --git a/app/vendor/phpunit/php-code-coverage/src/Node/Directory.php b/app/vendor/phpunit/php-code-coverage/src/Node/Directory.php index 7f1b5b228..572cc2a4e 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Node/Directory.php +++ b/app/vendor/phpunit/php-code-coverage/src/Node/Directory.php @@ -1,6 +1,6 @@ * @@ -9,12 +9,16 @@ */ namespace SebastianBergmann\CodeCoverage\Node; -use SebastianBergmann\CodeCoverage\InvalidArgumentException; +use function array_merge; +use function count; +use IteratorAggregate; +use RecursiveIteratorIterator; +use SebastianBergmann\LinesOfCode\LinesOfCode; /** - * Represents a directory in the code coverage information tree. + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class Directory extends AbstractNode implements \IteratorAggregate +final class Directory extends AbstractNode implements IteratorAggregate { /** * @var AbstractNode[] @@ -47,7 +51,7 @@ final class Directory extends AbstractNode implements \IteratorAggregate private $functions; /** - * @var array + * @var LinesOfCode */ private $linesOfCode; @@ -66,6 +70,26 @@ final class Directory extends AbstractNode implements \IteratorAggregate */ private $numExecutedLines = -1; + /** + * @var int + */ + private $numExecutableBranches = -1; + + /** + * @var int + */ + private $numExecutedBranches = -1; + + /** + * @var int + */ + private $numExecutablePaths = -1; + + /** + * @var int + */ + private $numExecutedPaths = -1; + /** * @var int */ @@ -106,100 +130,70 @@ final class Directory extends AbstractNode implements \IteratorAggregate */ private $numTestedFunctions = -1; - /** - * Returns the number of files in/under this node. - */ public function count(): int { if ($this->numFiles === -1) { $this->numFiles = 0; foreach ($this->children as $child) { - $this->numFiles += \count($child); + $this->numFiles += count($child); } } return $this->numFiles; } - /** - * Returns an iterator for this node. - */ - public function getIterator(): \RecursiveIteratorIterator + public function getIterator(): RecursiveIteratorIterator { - return new \RecursiveIteratorIterator( + return new RecursiveIteratorIterator( new Iterator($this), - \RecursiveIteratorIterator::SELF_FIRST + RecursiveIteratorIterator::SELF_FIRST ); } - /** - * Adds a new directory. - */ public function addDirectory(string $name): self { $directory = new self($name, $this); $this->children[] = $directory; - $this->directories[] = &$this->children[\count($this->children) - 1]; + $this->directories[] = &$this->children[count($this->children) - 1]; return $directory; } - /** - * Adds a new file. - * - * @throws InvalidArgumentException - */ - public function addFile(string $name, array $coverageData, array $testData, bool $cacheTokens): File + public function addFile(File $file): void { - $file = new File($name, $this, $coverageData, $testData, $cacheTokens); - $this->children[] = $file; - $this->files[] = &$this->children[\count($this->children) - 1]; + $this->files[] = &$this->children[count($this->children) - 1]; $this->numExecutableLines = -1; $this->numExecutedLines = -1; - - return $file; } - /** - * Returns the directories in this directory. - */ - public function getDirectories(): array + public function directories(): array { return $this->directories; } - /** - * Returns the files in this directory. - */ - public function getFiles(): array + public function files(): array { return $this->files; } - /** - * Returns the child nodes of this node. - */ - public function getChildNodes(): array + public function children(): array { return $this->children; } - /** - * Returns the classes of this node. - */ - public function getClasses(): array + public function classes(): array { if ($this->classes === null) { $this->classes = []; foreach ($this->children as $child) { - $this->classes = \array_merge( + $this->classes = array_merge( $this->classes, - $child->getClasses() + $child->classes() ); } } @@ -207,18 +201,15 @@ public function getClasses(): array return $this->classes; } - /** - * Returns the traits of this node. - */ - public function getTraits(): array + public function traits(): array { if ($this->traits === null) { $this->traits = []; foreach ($this->children as $child) { - $this->traits = \array_merge( + $this->traits = array_merge( $this->traits, - $child->getTraits() + $child->traits() ); } } @@ -226,18 +217,15 @@ public function getTraits(): array return $this->traits; } - /** - * Returns the functions of this node. - */ - public function getFunctions(): array + public function functions(): array { if ($this->functions === null) { $this->functions = []; foreach ($this->children as $child) { - $this->functions = \array_merge( + $this->functions = array_merge( $this->functions, - $child->getFunctions() + $child->functions() ); } } @@ -245,180 +233,195 @@ public function getFunctions(): array return $this->functions; } - /** - * Returns the LOC/CLOC/NCLOC of this node. - */ - public function getLinesOfCode(): array + public function linesOfCode(): LinesOfCode { if ($this->linesOfCode === null) { - $this->linesOfCode = ['loc' => 0, 'cloc' => 0, 'ncloc' => 0]; + $this->linesOfCode = new LinesOfCode(0, 0, 0, 0); foreach ($this->children as $child) { - $linesOfCode = $child->getLinesOfCode(); - - $this->linesOfCode['loc'] += $linesOfCode['loc']; - $this->linesOfCode['cloc'] += $linesOfCode['cloc']; - $this->linesOfCode['ncloc'] += $linesOfCode['ncloc']; + $this->linesOfCode = $this->linesOfCode->plus($child->linesOfCode()); } } return $this->linesOfCode; } - /** - * Returns the number of executable lines. - */ - public function getNumExecutableLines(): int + public function numberOfExecutableLines(): int { if ($this->numExecutableLines === -1) { $this->numExecutableLines = 0; foreach ($this->children as $child) { - $this->numExecutableLines += $child->getNumExecutableLines(); + $this->numExecutableLines += $child->numberOfExecutableLines(); } } return $this->numExecutableLines; } - /** - * Returns the number of executed lines. - */ - public function getNumExecutedLines(): int + public function numberOfExecutedLines(): int { if ($this->numExecutedLines === -1) { $this->numExecutedLines = 0; foreach ($this->children as $child) { - $this->numExecutedLines += $child->getNumExecutedLines(); + $this->numExecutedLines += $child->numberOfExecutedLines(); } } return $this->numExecutedLines; } - /** - * Returns the number of classes. - */ - public function getNumClasses(): int + public function numberOfExecutableBranches(): int + { + if ($this->numExecutableBranches === -1) { + $this->numExecutableBranches = 0; + + foreach ($this->children as $child) { + $this->numExecutableBranches += $child->numberOfExecutableBranches(); + } + } + + return $this->numExecutableBranches; + } + + public function numberOfExecutedBranches(): int + { + if ($this->numExecutedBranches === -1) { + $this->numExecutedBranches = 0; + + foreach ($this->children as $child) { + $this->numExecutedBranches += $child->numberOfExecutedBranches(); + } + } + + return $this->numExecutedBranches; + } + + public function numberOfExecutablePaths(): int + { + if ($this->numExecutablePaths === -1) { + $this->numExecutablePaths = 0; + + foreach ($this->children as $child) { + $this->numExecutablePaths += $child->numberOfExecutablePaths(); + } + } + + return $this->numExecutablePaths; + } + + public function numberOfExecutedPaths(): int + { + if ($this->numExecutedPaths === -1) { + $this->numExecutedPaths = 0; + + foreach ($this->children as $child) { + $this->numExecutedPaths += $child->numberOfExecutedPaths(); + } + } + + return $this->numExecutedPaths; + } + + public function numberOfClasses(): int { if ($this->numClasses === -1) { $this->numClasses = 0; foreach ($this->children as $child) { - $this->numClasses += $child->getNumClasses(); + $this->numClasses += $child->numberOfClasses(); } } return $this->numClasses; } - /** - * Returns the number of tested classes. - */ - public function getNumTestedClasses(): int + public function numberOfTestedClasses(): int { if ($this->numTestedClasses === -1) { $this->numTestedClasses = 0; foreach ($this->children as $child) { - $this->numTestedClasses += $child->getNumTestedClasses(); + $this->numTestedClasses += $child->numberOfTestedClasses(); } } return $this->numTestedClasses; } - /** - * Returns the number of traits. - */ - public function getNumTraits(): int + public function numberOfTraits(): int { if ($this->numTraits === -1) { $this->numTraits = 0; foreach ($this->children as $child) { - $this->numTraits += $child->getNumTraits(); + $this->numTraits += $child->numberOfTraits(); } } return $this->numTraits; } - /** - * Returns the number of tested traits. - */ - public function getNumTestedTraits(): int + public function numberOfTestedTraits(): int { if ($this->numTestedTraits === -1) { $this->numTestedTraits = 0; foreach ($this->children as $child) { - $this->numTestedTraits += $child->getNumTestedTraits(); + $this->numTestedTraits += $child->numberOfTestedTraits(); } } return $this->numTestedTraits; } - /** - * Returns the number of methods. - */ - public function getNumMethods(): int + public function numberOfMethods(): int { if ($this->numMethods === -1) { $this->numMethods = 0; foreach ($this->children as $child) { - $this->numMethods += $child->getNumMethods(); + $this->numMethods += $child->numberOfMethods(); } } return $this->numMethods; } - /** - * Returns the number of tested methods. - */ - public function getNumTestedMethods(): int + public function numberOfTestedMethods(): int { if ($this->numTestedMethods === -1) { $this->numTestedMethods = 0; foreach ($this->children as $child) { - $this->numTestedMethods += $child->getNumTestedMethods(); + $this->numTestedMethods += $child->numberOfTestedMethods(); } } return $this->numTestedMethods; } - /** - * Returns the number of functions. - */ - public function getNumFunctions(): int + public function numberOfFunctions(): int { if ($this->numFunctions === -1) { $this->numFunctions = 0; foreach ($this->children as $child) { - $this->numFunctions += $child->getNumFunctions(); + $this->numFunctions += $child->numberOfFunctions(); } } return $this->numFunctions; } - /** - * Returns the number of tested functions. - */ - public function getNumTestedFunctions(): int + public function numberOfTestedFunctions(): int { if ($this->numTestedFunctions === -1) { $this->numTestedFunctions = 0; foreach ($this->children as $child) { - $this->numTestedFunctions += $child->getNumTestedFunctions(); + $this->numTestedFunctions += $child->numberOfTestedFunctions(); } } diff --git a/app/vendor/phpunit/php-code-coverage/src/Node/File.php b/app/vendor/phpunit/php-code-coverage/src/Node/File.php index 840d11937..290ae731a 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Node/File.php +++ b/app/vendor/phpunit/php-code-coverage/src/Node/File.php @@ -1,6 +1,6 @@ * @@ -9,15 +9,26 @@ */ namespace SebastianBergmann\CodeCoverage\Node; +use function array_filter; +use function count; +use function range; +use SebastianBergmann\CodeCoverage\CrapIndex; +use SebastianBergmann\LinesOfCode\LinesOfCode; + /** - * Represents a file in the code coverage information tree. + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ final class File extends AbstractNode { /** * @var array */ - private $coverageData; + private $lineCoverageData; + + /** + * @var array + */ + private $functionCoverageData; /** * @var array @@ -34,6 +45,26 @@ final class File extends AbstractNode */ private $numExecutedLines = 0; + /** + * @var int + */ + private $numExecutableBranches = 0; + + /** + * @var int + */ + private $numExecutedBranches = 0; + + /** + * @var int + */ + private $numExecutablePaths = 0; + + /** + * @var int + */ + private $numExecutedPaths = 0; + /** * @var array */ @@ -50,9 +81,9 @@ final class File extends AbstractNode private $functions = []; /** - * @var array + * @var LinesOfCode */ - private $linesOfCode = []; + private $linesOfCode; /** * @var int @@ -89,103 +120,94 @@ final class File extends AbstractNode */ private $numTestedFunctions; - /** - * @var bool - */ - private $cacheTokens; - /** * @var array */ private $codeUnitsByLine = []; - public function __construct(string $name, AbstractNode $parent, array $coverageData, array $testData, bool $cacheTokens) + public function __construct(string $name, AbstractNode $parent, array $lineCoverageData, array $functionCoverageData, array $testData, array $classes, array $traits, array $functions, LinesOfCode $linesOfCode) { parent::__construct($name, $parent); - $this->coverageData = $coverageData; - $this->testData = $testData; - $this->cacheTokens = $cacheTokens; + $this->lineCoverageData = $lineCoverageData; + $this->functionCoverageData = $functionCoverageData; + $this->testData = $testData; + $this->linesOfCode = $linesOfCode; - $this->calculateStatistics(); + $this->calculateStatistics($classes, $traits, $functions); } - /** - * Returns the number of files in/under this node. - */ public function count(): int { return 1; } - /** - * Returns the code coverage data of this node. - */ - public function getCoverageData(): array + public function lineCoverageData(): array { - return $this->coverageData; + return $this->lineCoverageData; } - /** - * Returns the test data of this node. - */ - public function getTestData(): array + public function functionCoverageData(): array + { + return $this->functionCoverageData; + } + + public function testData(): array { return $this->testData; } - /** - * Returns the classes of this node. - */ - public function getClasses(): array + public function classes(): array { return $this->classes; } - /** - * Returns the traits of this node. - */ - public function getTraits(): array + public function traits(): array { return $this->traits; } - /** - * Returns the functions of this node. - */ - public function getFunctions(): array + public function functions(): array { return $this->functions; } - /** - * Returns the LOC/CLOC/NCLOC of this node. - */ - public function getLinesOfCode(): array + public function linesOfCode(): LinesOfCode { return $this->linesOfCode; } - /** - * Returns the number of executable lines. - */ - public function getNumExecutableLines(): int + public function numberOfExecutableLines(): int { return $this->numExecutableLines; } - /** - * Returns the number of executed lines. - */ - public function getNumExecutedLines(): int + public function numberOfExecutedLines(): int { return $this->numExecutedLines; } - /** - * Returns the number of classes. - */ - public function getNumClasses(): int + public function numberOfExecutableBranches(): int + { + return $this->numExecutableBranches; + } + + public function numberOfExecutedBranches(): int + { + return $this->numExecutedBranches; + } + + public function numberOfExecutablePaths(): int + { + return $this->numExecutablePaths; + } + + public function numberOfExecutedPaths(): int + { + return $this->numExecutedPaths; + } + + public function numberOfClasses(): int { if ($this->numClasses === null) { $this->numClasses = 0; @@ -204,18 +226,12 @@ public function getNumClasses(): int return $this->numClasses; } - /** - * Returns the number of tested classes. - */ - public function getNumTestedClasses(): int + public function numberOfTestedClasses(): int { return $this->numTestedClasses; } - /** - * Returns the number of traits. - */ - public function getNumTraits(): int + public function numberOfTraits(): int { if ($this->numTraits === null) { $this->numTraits = 0; @@ -234,18 +250,12 @@ public function getNumTraits(): int return $this->numTraits; } - /** - * Returns the number of tested traits. - */ - public function getNumTestedTraits(): int + public function numberOfTestedTraits(): int { return $this->numTestedTraits; } - /** - * Returns the number of methods. - */ - public function getNumMethods(): int + public function numberOfMethods(): int { if ($this->numMethods === null) { $this->numMethods = 0; @@ -270,10 +280,7 @@ public function getNumMethods(): int return $this->numMethods; } - /** - * Returns the number of tested methods. - */ - public function getNumTestedMethods(): int + public function numberOfTestedMethods(): int { if ($this->numTestedMethods === null) { $this->numTestedMethods = 0; @@ -300,18 +307,12 @@ public function getNumTestedMethods(): int return $this->numTestedMethods; } - /** - * Returns the number of functions. - */ - public function getNumFunctions(): int + public function numberOfFunctions(): int { - return \count($this->functions); + return count($this->functions); } - /** - * Returns the number of tested functions. - */ - public function getNumTestedFunctions(): int + public function numberOfTestedFunctions(): int { if ($this->numTestedFunctions === null) { $this->numTestedFunctions = 0; @@ -327,32 +328,18 @@ public function getNumTestedFunctions(): int return $this->numTestedFunctions; } - private function calculateStatistics(): void + private function calculateStatistics(array $classes, array $traits, array $functions): void { - if ($this->cacheTokens) { - $tokens = \PHP_Token_Stream_CachingFactory::get($this->getPath()); - } else { - $tokens = new \PHP_Token_Stream($this->getPath()); - } - - $this->linesOfCode = $tokens->getLinesOfCode(); - - foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) { + foreach (range(1, $this->linesOfCode->linesOfCode()) as $lineNumber) { $this->codeUnitsByLine[$lineNumber] = []; } - try { - $this->processClasses($tokens); - $this->processTraits($tokens); - $this->processFunctions($tokens); - } catch (\OutOfBoundsException $e) { - // This can happen with PHP_Token_Stream if the file is syntactically invalid, - // and probably affects a file that wasn't executed. - } - unset($tokens); + $this->processClasses($classes); + $this->processTraits($traits); + $this->processFunctions($functions); - foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) { - if (isset($this->coverageData[$lineNumber])) { + foreach (range(1, $this->linesOfCode->linesOfCode()) as $lineNumber) { + if (isset($this->lineCoverageData[$lineNumber])) { foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) { $codeUnit['executableLines']++; } @@ -361,7 +348,7 @@ private function calculateStatistics(): void $this->numExecutableLines++; - if (\count($this->coverageData[$lineNumber]) > 0) { + if (count($this->lineCoverageData[$lineNumber]) > 0) { foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) { $codeUnit['executedLines']++; } @@ -375,134 +362,111 @@ private function calculateStatistics(): void foreach ($this->traits as &$trait) { foreach ($trait['methods'] as &$method) { - if ($method['executableLines'] > 0) { - $method['coverage'] = ($method['executedLines'] / - $method['executableLines']) * 100; - } else { - $method['coverage'] = 100; - } + $methodLineCoverage = $method['executableLines'] ? ($method['executedLines'] / $method['executableLines']) * 100 : 100; + $methodBranchCoverage = $method['executableBranches'] ? ($method['executedBranches'] / $method['executableBranches']) * 100 : 0; + $methodPathCoverage = $method['executablePaths'] ? ($method['executedPaths'] / $method['executablePaths']) * 100 : 0; - $method['crap'] = $this->crap( - $method['ccn'], - $method['coverage'] - ); + $method['coverage'] = $methodBranchCoverage ?: $methodLineCoverage; + $method['crap'] = (new CrapIndex($method['ccn'], $methodPathCoverage ?: $methodLineCoverage))->asString(); $trait['ccn'] += $method['ccn']; } unset($method); - if ($trait['executableLines'] > 0) { - $trait['coverage'] = ($trait['executedLines'] / - $trait['executableLines']) * 100; + $traitLineCoverage = $trait['executableLines'] ? ($trait['executedLines'] / $trait['executableLines']) * 100 : 100; + $traitBranchCoverage = $trait['executableBranches'] ? ($trait['executedBranches'] / $trait['executableBranches']) * 100 : 0; + $traitPathCoverage = $trait['executablePaths'] ? ($trait['executedPaths'] / $trait['executablePaths']) * 100 : 0; - if ($trait['coverage'] === 100) { - $this->numTestedClasses++; - } - } else { - $trait['coverage'] = 100; - } + $trait['coverage'] = $traitBranchCoverage ?: $traitLineCoverage; + $trait['crap'] = (new CrapIndex($trait['ccn'], $traitPathCoverage ?: $traitLineCoverage))->asString(); - $trait['crap'] = $this->crap( - $trait['ccn'], - $trait['coverage'] - ); + if ($trait['executableLines'] > 0 && $trait['coverage'] === 100) { + $this->numTestedClasses++; + } } unset($trait); foreach ($this->classes as &$class) { foreach ($class['methods'] as &$method) { - if ($method['executableLines'] > 0) { - $method['coverage'] = ($method['executedLines'] / - $method['executableLines']) * 100; - } else { - $method['coverage'] = 100; - } + $methodLineCoverage = $method['executableLines'] ? ($method['executedLines'] / $method['executableLines']) * 100 : 100; + $methodBranchCoverage = $method['executableBranches'] ? ($method['executedBranches'] / $method['executableBranches']) * 100 : 0; + $methodPathCoverage = $method['executablePaths'] ? ($method['executedPaths'] / $method['executablePaths']) * 100 : 0; - $method['crap'] = $this->crap( - $method['ccn'], - $method['coverage'] - ); + $method['coverage'] = $methodBranchCoverage ?: $methodLineCoverage; + $method['crap'] = (new CrapIndex($method['ccn'], $methodPathCoverage ?: $methodLineCoverage))->asString(); $class['ccn'] += $method['ccn']; } unset($method); - if ($class['executableLines'] > 0) { - $class['coverage'] = ($class['executedLines'] / - $class['executableLines']) * 100; + $classLineCoverage = $class['executableLines'] ? ($class['executedLines'] / $class['executableLines']) * 100 : 100; + $classBranchCoverage = $class['executableBranches'] ? ($class['executedBranches'] / $class['executableBranches']) * 100 : 0; + $classPathCoverage = $class['executablePaths'] ? ($class['executedPaths'] / $class['executablePaths']) * 100 : 0; - if ($class['coverage'] === 100) { - $this->numTestedClasses++; - } - } else { - $class['coverage'] = 100; - } + $class['coverage'] = $classBranchCoverage ?: $classLineCoverage; + $class['crap'] = (new CrapIndex($class['ccn'], $classPathCoverage ?: $classLineCoverage))->asString(); - $class['crap'] = $this->crap( - $class['ccn'], - $class['coverage'] - ); + if ($class['executableLines'] > 0 && $class['coverage'] === 100) { + $this->numTestedClasses++; + } } unset($class); foreach ($this->functions as &$function) { - if ($function['executableLines'] > 0) { - $function['coverage'] = ($function['executedLines'] / - $function['executableLines']) * 100; - } else { - $function['coverage'] = 100; - } + $functionLineCoverage = $function['executableLines'] ? ($function['executedLines'] / $function['executableLines']) * 100 : 100; + $functionBranchCoverage = $function['executableBranches'] ? ($function['executedBranches'] / $function['executableBranches']) * 100 : 0; + $functionPathCoverage = $function['executablePaths'] ? ($function['executedPaths'] / $function['executablePaths']) * 100 : 0; + + $function['coverage'] = $functionBranchCoverage ?: $functionLineCoverage; + $function['crap'] = (new CrapIndex($function['ccn'], $functionPathCoverage ?: $functionLineCoverage))->asString(); if ($function['coverage'] === 100) { $this->numTestedFunctions++; } - - $function['crap'] = $this->crap( - $function['ccn'], - $function['coverage'] - ); } } - private function processClasses(\PHP_Token_Stream $tokens): void + private function processClasses(array $classes): void { - $classes = $tokens->getClasses(); - $link = $this->getId() . '.html#'; + $link = $this->id() . '.html#'; foreach ($classes as $className => $class) { - if (\strpos($className, 'anonymous') === 0) { - continue; - } - - if (!empty($class['package']['namespace'])) { - $className = $class['package']['namespace'] . '\\' . $className; - } - $this->classes[$className] = [ - 'className' => $className, - 'methods' => [], - 'startLine' => $class['startLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => 0, - 'coverage' => 0, - 'crap' => 0, - 'package' => $class['package'], - 'link' => $link . $class['startLine'], + 'className' => $className, + 'namespace' => $class['namespace'], + 'methods' => [], + 'startLine' => $class['startLine'], + 'executableLines' => 0, + 'executedLines' => 0, + 'executableBranches' => 0, + 'executedBranches' => 0, + 'executablePaths' => 0, + 'executedPaths' => 0, + 'ccn' => 0, + 'coverage' => 0, + 'crap' => 0, + 'link' => $link . $class['startLine'], ]; foreach ($class['methods'] as $methodName => $method) { - if (\strpos($methodName, 'anonymous') === 0) { - continue; - } + $methodData = $this->newMethod($className, $methodName, $method, $link); + $this->classes[$className]['methods'][$methodName] = $methodData; + + $this->classes[$className]['executableBranches'] += $methodData['executableBranches']; + $this->classes[$className]['executedBranches'] += $methodData['executedBranches']; + $this->classes[$className]['executablePaths'] += $methodData['executablePaths']; + $this->classes[$className]['executedPaths'] += $methodData['executedPaths']; - $this->classes[$className]['methods'][$methodName] = $this->newMethod($methodName, $method, $link); + $this->numExecutableBranches += $methodData['executableBranches']; + $this->numExecutedBranches += $methodData['executedBranches']; + $this->numExecutablePaths += $methodData['executablePaths']; + $this->numExecutedPaths += $methodData['executedPaths']; - foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) { + foreach (range($method['startLine'], $method['endLine']) as $lineNumber) { $this->codeUnitsByLine[$lineNumber] = [ &$this->classes[$className], &$this->classes[$className]['methods'][$methodName], @@ -512,33 +476,43 @@ private function processClasses(\PHP_Token_Stream $tokens): void } } - private function processTraits(\PHP_Token_Stream $tokens): void + private function processTraits(array $traits): void { - $traits = $tokens->getTraits(); - $link = $this->getId() . '.html#'; + $link = $this->id() . '.html#'; foreach ($traits as $traitName => $trait) { $this->traits[$traitName] = [ - 'traitName' => $traitName, - 'methods' => [], - 'startLine' => $trait['startLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => 0, - 'coverage' => 0, - 'crap' => 0, - 'package' => $trait['package'], - 'link' => $link . $trait['startLine'], + 'traitName' => $traitName, + 'namespace' => $trait['namespace'], + 'methods' => [], + 'startLine' => $trait['startLine'], + 'executableLines' => 0, + 'executedLines' => 0, + 'executableBranches' => 0, + 'executedBranches' => 0, + 'executablePaths' => 0, + 'executedPaths' => 0, + 'ccn' => 0, + 'coverage' => 0, + 'crap' => 0, + 'link' => $link . $trait['startLine'], ]; foreach ($trait['methods'] as $methodName => $method) { - if (\strpos($methodName, 'anonymous') === 0) { - continue; - } + $methodData = $this->newMethod($traitName, $methodName, $method, $link); + $this->traits[$traitName]['methods'][$methodName] = $methodData; + + $this->traits[$traitName]['executableBranches'] += $methodData['executableBranches']; + $this->traits[$traitName]['executedBranches'] += $methodData['executedBranches']; + $this->traits[$traitName]['executablePaths'] += $methodData['executablePaths']; + $this->traits[$traitName]['executedPaths'] += $methodData['executedPaths']; - $this->traits[$traitName]['methods'][$methodName] = $this->newMethod($methodName, $method, $link); + $this->numExecutableBranches += $methodData['executableBranches']; + $this->numExecutedBranches += $methodData['executedBranches']; + $this->numExecutablePaths += $methodData['executablePaths']; + $this->numExecutedPaths += $methodData['executedPaths']; - foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) { + foreach (range($method['startLine'], $method['endLine']) as $lineNumber) { $this->codeUnitsByLine[$lineNumber] = [ &$this->traits[$traitName], &$this->traits[$traitName]['methods'][$methodName], @@ -548,64 +522,122 @@ private function processTraits(\PHP_Token_Stream $tokens): void } } - private function processFunctions(\PHP_Token_Stream $tokens): void + private function processFunctions(array $functions): void { - $functions = $tokens->getFunctions(); - $link = $this->getId() . '.html#'; + $link = $this->id() . '.html#'; foreach ($functions as $functionName => $function) { - if (\strpos($functionName, 'anonymous') === 0) { - continue; - } - $this->functions[$functionName] = [ - 'functionName' => $functionName, - 'signature' => $function['signature'], - 'startLine' => $function['startLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => $function['ccn'], - 'coverage' => 0, - 'crap' => 0, - 'link' => $link . $function['startLine'], + 'functionName' => $functionName, + 'namespace' => $function['namespace'], + 'signature' => $function['signature'], + 'startLine' => $function['startLine'], + 'endLine' => $function['endLine'], + 'executableLines' => 0, + 'executedLines' => 0, + 'executableBranches' => 0, + 'executedBranches' => 0, + 'executablePaths' => 0, + 'executedPaths' => 0, + 'ccn' => $function['ccn'], + 'coverage' => 0, + 'crap' => 0, + 'link' => $link . $function['startLine'], ]; - foreach (\range($function['startLine'], $function['endLine']) as $lineNumber) { + foreach (range($function['startLine'], $function['endLine']) as $lineNumber) { $this->codeUnitsByLine[$lineNumber] = [&$this->functions[$functionName]]; } + + if (isset($this->functionCoverageData[$functionName]['branches'])) { + $this->functions[$functionName]['executableBranches'] = count( + $this->functionCoverageData[$functionName]['branches'] + ); + + $this->functions[$functionName]['executedBranches'] = count( + array_filter( + $this->functionCoverageData[$functionName]['branches'], + static function (array $branch) { + return (bool) $branch['hit']; + } + ) + ); + } + + if (isset($this->functionCoverageData[$functionName]['paths'])) { + $this->functions[$functionName]['executablePaths'] = count( + $this->functionCoverageData[$functionName]['paths'] + ); + + $this->functions[$functionName]['executedPaths'] = count( + array_filter( + $this->functionCoverageData[$functionName]['paths'], + static function (array $path) { + return (bool) $path['hit']; + } + ) + ); + } + + $this->numExecutableBranches += $this->functions[$functionName]['executableBranches']; + $this->numExecutedBranches += $this->functions[$functionName]['executedBranches']; + $this->numExecutablePaths += $this->functions[$functionName]['executablePaths']; + $this->numExecutedPaths += $this->functions[$functionName]['executedPaths']; } } - private function crap(int $ccn, float $coverage): string + private function newMethod(string $className, string $methodName, array $method, string $link): array { - if ($coverage === 0.0) { - return (string) ($ccn ** 2 + $ccn); - } + $methodData = [ + 'methodName' => $methodName, + 'visibility' => $method['visibility'], + 'signature' => $method['signature'], + 'startLine' => $method['startLine'], + 'endLine' => $method['endLine'], + 'executableLines' => 0, + 'executedLines' => 0, + 'executableBranches' => 0, + 'executedBranches' => 0, + 'executablePaths' => 0, + 'executedPaths' => 0, + 'ccn' => $method['ccn'], + 'coverage' => 0, + 'crap' => 0, + 'link' => $link . $method['startLine'], + ]; + + $key = $className . '->' . $methodName; - if ($coverage >= 95) { - return (string) $ccn; + if (isset($this->functionCoverageData[$key]['branches'])) { + $methodData['executableBranches'] = count( + $this->functionCoverageData[$key]['branches'] + ); + + $methodData['executedBranches'] = count( + array_filter( + $this->functionCoverageData[$key]['branches'], + static function (array $branch) { + return (bool) $branch['hit']; + } + ) + ); } - return \sprintf( - '%01.2F', - $ccn ** 2 * (1 - $coverage / 100) ** 3 + $ccn - ); - } + if (isset($this->functionCoverageData[$key]['paths'])) { + $methodData['executablePaths'] = count( + $this->functionCoverageData[$key]['paths'] + ); - private function newMethod(string $methodName, array $method, string $link): array - { - return [ - 'methodName' => $methodName, - 'visibility' => $method['visibility'], - 'signature' => $method['signature'], - 'startLine' => $method['startLine'], - 'endLine' => $method['endLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => $method['ccn'], - 'coverage' => 0, - 'crap' => 0, - 'link' => $link . $method['startLine'], - ]; + $methodData['executedPaths'] = count( + array_filter( + $this->functionCoverageData[$key]['paths'], + static function (array $path) { + return (bool) $path['hit']; + } + ) + ); + } + + return $methodData; } } diff --git a/app/vendor/phpunit/php-code-coverage/src/Node/Iterator.php b/app/vendor/phpunit/php-code-coverage/src/Node/Iterator.php index f2dd9a720..d0a5a0654 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Node/Iterator.php +++ b/app/vendor/phpunit/php-code-coverage/src/Node/Iterator.php @@ -1,6 +1,6 @@ * @@ -9,10 +9,13 @@ */ namespace SebastianBergmann\CodeCoverage\Node; +use function count; +use RecursiveIterator; + /** - * Recursive iterator for node object graphs. + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ -final class Iterator implements \RecursiveIterator +final class Iterator implements RecursiveIterator { /** * @var int @@ -26,7 +29,7 @@ final class Iterator implements \RecursiveIterator public function __construct(Directory $node) { - $this->nodes = $node->getChildNodes(); + $this->nodes = $node->children(); } /** @@ -42,7 +45,7 @@ public function rewind(): void */ public function valid(): bool { - return $this->position < \count($this->nodes); + return $this->position < count($this->nodes); } /** @@ -56,7 +59,7 @@ public function key(): int /** * Returns the current element. */ - public function current(): AbstractNode + public function current(): ?AbstractNode { return $this->valid() ? $this->nodes[$this->position] : null; } diff --git a/app/vendor/phpunit/php-code-coverage/src/Percentage.php b/app/vendor/phpunit/php-code-coverage/src/Percentage.php new file mode 100644 index 000000000..b5ac38845 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Percentage.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use function sprintf; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class Percentage +{ + /** + * @var float + */ + private $fraction; + + /** + * @var float + */ + private $total; + + public static function fromFractionAndTotal(float $fraction, float $total): self + { + return new self($fraction, $total); + } + + private function __construct(float $fraction, float $total) + { + $this->fraction = $fraction; + $this->total = $total; + } + + public function asFloat(): float + { + if ($this->total > 0) { + return ($this->fraction / $this->total) * 100; + } + + return 100.0; + } + + public function asString(): string + { + if ($this->total > 0) { + return sprintf('%01.2F%%', $this->asFloat()); + } + + return ''; + } + + public function asFixedWidthString(): string + { + if ($this->total > 0) { + return sprintf('%6.2F%%', $this->asFloat()); + } + + return ''; + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php b/app/vendor/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php new file mode 100644 index 000000000..1ed29ad52 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php @@ -0,0 +1,255 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use function array_key_exists; +use function array_keys; +use function array_merge; +use function array_unique; +use function count; +use function is_array; +use function ksort; +use SebastianBergmann\CodeCoverage\Driver\Driver; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class ProcessedCodeCoverageData +{ + /** + * Line coverage data. + * An array of filenames, each having an array of linenumbers, each executable line having an array of testcase ids. + * + * @var array + */ + private $lineCoverage = []; + + /** + * Function coverage data. + * Maintains base format of raw data (@see https://xdebug.org/docs/code_coverage), but each 'hit' entry is an array + * of testcase ids. + * + * @var array + */ + private $functionCoverage = []; + + public function initializeUnseenData(RawCodeCoverageData $rawData): void + { + foreach ($rawData->lineCoverage() as $file => $lines) { + if (!isset($this->lineCoverage[$file])) { + $this->lineCoverage[$file] = []; + + foreach ($lines as $k => $v) { + $this->lineCoverage[$file][$k] = $v === Driver::LINE_NOT_EXECUTABLE ? null : []; + } + } + } + + foreach ($rawData->functionCoverage() as $file => $functions) { + foreach ($functions as $functionName => $functionData) { + if (isset($this->functionCoverage[$file][$functionName])) { + $this->initPreviouslySeenFunction($file, $functionName, $functionData); + } else { + $this->initPreviouslyUnseenFunction($file, $functionName, $functionData); + } + } + } + } + + public function markCodeAsExecutedByTestCase(string $testCaseId, RawCodeCoverageData $executedCode): void + { + foreach ($executedCode->lineCoverage() as $file => $lines) { + foreach ($lines as $k => $v) { + if ($v === Driver::LINE_EXECUTED) { + $this->lineCoverage[$file][$k][] = $testCaseId; + } + } + } + + foreach ($executedCode->functionCoverage() as $file => $functions) { + foreach ($functions as $functionName => $functionData) { + foreach ($functionData['branches'] as $branchId => $branchData) { + if ($branchData['hit'] === Driver::BRANCH_HIT) { + $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'][] = $testCaseId; + } + } + + foreach ($functionData['paths'] as $pathId => $pathData) { + if ($pathData['hit'] === Driver::BRANCH_HIT) { + $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'][] = $testCaseId; + } + } + } + } + } + + public function setLineCoverage(array $lineCoverage): void + { + $this->lineCoverage = $lineCoverage; + } + + public function lineCoverage(): array + { + ksort($this->lineCoverage); + + return $this->lineCoverage; + } + + public function setFunctionCoverage(array $functionCoverage): void + { + $this->functionCoverage = $functionCoverage; + } + + public function functionCoverage(): array + { + ksort($this->functionCoverage); + + return $this->functionCoverage; + } + + public function coveredFiles(): array + { + ksort($this->lineCoverage); + + return array_keys($this->lineCoverage); + } + + public function renameFile(string $oldFile, string $newFile): void + { + $this->lineCoverage[$newFile] = $this->lineCoverage[$oldFile]; + + if (isset($this->functionCoverage[$oldFile])) { + $this->functionCoverage[$newFile] = $this->functionCoverage[$oldFile]; + } + + unset($this->lineCoverage[$oldFile], $this->functionCoverage[$oldFile]); + } + + public function merge(self $newData): void + { + foreach ($newData->lineCoverage as $file => $lines) { + if (!isset($this->lineCoverage[$file])) { + $this->lineCoverage[$file] = $lines; + + continue; + } + + // we should compare the lines if any of two contains data + $compareLineNumbers = array_unique( + array_merge( + array_keys($this->lineCoverage[$file]), + array_keys($newData->lineCoverage[$file]) + ) + ); + + foreach ($compareLineNumbers as $line) { + $thatPriority = $this->priorityForLine($newData->lineCoverage[$file], $line); + $thisPriority = $this->priorityForLine($this->lineCoverage[$file], $line); + + if ($thatPriority > $thisPriority) { + $this->lineCoverage[$file][$line] = $newData->lineCoverage[$file][$line]; + } elseif ($thatPriority === $thisPriority && is_array($this->lineCoverage[$file][$line])) { + $this->lineCoverage[$file][$line] = array_unique( + array_merge($this->lineCoverage[$file][$line], $newData->lineCoverage[$file][$line]) + ); + } + } + } + + foreach ($newData->functionCoverage as $file => $functions) { + if (!isset($this->functionCoverage[$file])) { + $this->functionCoverage[$file] = $functions; + + continue; + } + + foreach ($functions as $functionName => $functionData) { + if (isset($this->functionCoverage[$file][$functionName])) { + $this->initPreviouslySeenFunction($file, $functionName, $functionData); + } else { + $this->initPreviouslyUnseenFunction($file, $functionName, $functionData); + } + + foreach ($functionData['branches'] as $branchId => $branchData) { + $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'], $branchData['hit'])); + } + + foreach ($functionData['paths'] as $pathId => $pathData) { + $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'], $pathData['hit'])); + } + } + } + } + + /** + * Determine the priority for a line. + * + * 1 = the line is not set + * 2 = the line has not been tested + * 3 = the line is dead code + * 4 = the line has been tested + * + * During a merge, a higher number is better. + */ + private function priorityForLine(array $data, int $line): int + { + if (!array_key_exists($line, $data)) { + return 1; + } + + if (is_array($data[$line]) && count($data[$line]) === 0) { + return 2; + } + + if ($data[$line] === null) { + return 3; + } + + return 4; + } + + /** + * For a function we have never seen before, copy all data over and simply init the 'hit' array. + */ + private function initPreviouslyUnseenFunction(string $file, string $functionName, array $functionData): void + { + $this->functionCoverage[$file][$functionName] = $functionData; + + foreach (array_keys($functionData['branches']) as $branchId) { + $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = []; + } + + foreach (array_keys($functionData['paths']) as $pathId) { + $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = []; + } + } + + /** + * For a function we have seen before, only copy over and init the 'hit' array for any unseen branches and paths. + * Techniques such as mocking and where the contents of a file are different vary during tests (e.g. compiling + * containers) mean that the functions inside a file cannot be relied upon to be static. + */ + private function initPreviouslySeenFunction(string $file, string $functionName, array $functionData): void + { + foreach ($functionData['branches'] as $branchId => $branchData) { + if (!isset($this->functionCoverage[$file][$functionName]['branches'][$branchId])) { + $this->functionCoverage[$file][$functionName]['branches'][$branchId] = $branchData; + $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = []; + } + } + + foreach ($functionData['paths'] as $pathId => $pathData) { + if (!isset($this->functionCoverage[$file][$functionName]['paths'][$pathId])) { + $this->functionCoverage[$file][$functionName]['paths'][$pathId] = $pathData; + $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = []; + } + } + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/RawCodeCoverageData.php b/app/vendor/phpunit/php-code-coverage/src/RawCodeCoverageData.php new file mode 100644 index 000000000..ae5044ffe --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/RawCodeCoverageData.php @@ -0,0 +1,227 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use function array_diff; +use function array_diff_key; +use function array_flip; +use function array_intersect; +use function array_intersect_key; +use function count; +use function file; +use function in_array; +use function range; +use SebastianBergmann\CodeCoverage\Driver\Driver; +use SebastianBergmann\CodeCoverage\StaticAnalysis\UncoveredFileAnalyser; + +/** + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage + */ +final class RawCodeCoverageData +{ + /** + * @var array> + */ + private static $emptyLineCache = []; + + /** + * @var array + * + * @see https://xdebug.org/docs/code_coverage for format + */ + private $lineCoverage; + + /** + * @var array + * + * @see https://xdebug.org/docs/code_coverage for format + */ + private $functionCoverage; + + public static function fromXdebugWithoutPathCoverage(array $rawCoverage): self + { + return new self($rawCoverage, []); + } + + public static function fromXdebugWithPathCoverage(array $rawCoverage): self + { + $lineCoverage = []; + $functionCoverage = []; + + foreach ($rawCoverage as $file => $fileCoverageData) { + $lineCoverage[$file] = $fileCoverageData['lines']; + $functionCoverage[$file] = $fileCoverageData['functions']; + } + + return new self($lineCoverage, $functionCoverage); + } + + public static function fromXdebugWithMixedCoverage(array $rawCoverage): self + { + $lineCoverage = []; + $functionCoverage = []; + + foreach ($rawCoverage as $file => $fileCoverageData) { + if (!isset($fileCoverageData['functions'])) { + // Current file does not have functions, so line coverage + // is stored in $fileCoverageData, not in $fileCoverageData['lines'] + $lineCoverage[$file] = $fileCoverageData; + + continue; + } + + $lineCoverage[$file] = $fileCoverageData['lines']; + $functionCoverage[$file] = $fileCoverageData['functions']; + } + + return new self($lineCoverage, $functionCoverage); + } + + public static function fromUncoveredFile(string $filename, UncoveredFileAnalyser $uncoveredFileAnalyser): self + { + $lineCoverage = []; + + foreach ($uncoveredFileAnalyser->executableLinesIn($filename) as $line) { + $lineCoverage[$line] = Driver::LINE_NOT_EXECUTED; + } + + return new self([$filename => $lineCoverage], []); + } + + private function __construct(array $lineCoverage, array $functionCoverage) + { + $this->lineCoverage = $lineCoverage; + $this->functionCoverage = $functionCoverage; + + $this->skipEmptyLines(); + } + + public function clear(): void + { + $this->lineCoverage = $this->functionCoverage = []; + } + + public function lineCoverage(): array + { + return $this->lineCoverage; + } + + public function functionCoverage(): array + { + return $this->functionCoverage; + } + + public function removeCoverageDataForFile(string $filename): void + { + unset($this->lineCoverage[$filename], $this->functionCoverage[$filename]); + } + + /** + * @param int[] $lines + */ + public function keepCoverageDataOnlyForLines(string $filename, array $lines): void + { + if (!isset($this->lineCoverage[$filename])) { + return; + } + + $this->lineCoverage[$filename] = array_intersect_key( + $this->lineCoverage[$filename], + array_flip($lines) + ); + + if (isset($this->functionCoverage[$filename])) { + foreach ($this->functionCoverage[$filename] as $functionName => $functionData) { + foreach ($functionData['branches'] as $branchId => $branch) { + if (count(array_diff(range($branch['line_start'], $branch['line_end']), $lines)) > 0) { + unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]); + + foreach ($functionData['paths'] as $pathId => $path) { + if (in_array($branchId, $path['path'], true)) { + unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]); + } + } + } + } + } + } + } + + /** + * @param int[] $lines + */ + public function removeCoverageDataForLines(string $filename, array $lines): void + { + if (empty($lines)) { + return; + } + + if (!isset($this->lineCoverage[$filename])) { + return; + } + + $this->lineCoverage[$filename] = array_diff_key( + $this->lineCoverage[$filename], + array_flip($lines) + ); + + if (isset($this->functionCoverage[$filename])) { + foreach ($this->functionCoverage[$filename] as $functionName => $functionData) { + foreach ($functionData['branches'] as $branchId => $branch) { + if (count(array_intersect($lines, range($branch['line_start'], $branch['line_end']))) > 0) { + unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]); + + foreach ($functionData['paths'] as $pathId => $path) { + if (in_array($branchId, $path['path'], true)) { + unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]); + } + } + } + } + } + } + } + + /** + * At the end of a file, the PHP interpreter always sees an implicit return. Where this occurs in a file that has + * e.g. a class definition, that line cannot be invoked from a test and results in confusing coverage. This engine + * implementation detail therefore needs to be masked which is done here by simply ensuring that all empty lines + * are skipped over for coverage purposes. + * + * @see https://github.com/sebastianbergmann/php-code-coverage/issues/799 + */ + private function skipEmptyLines(): void + { + foreach ($this->lineCoverage as $filename => $coverage) { + foreach ($this->getEmptyLinesForFile($filename) as $emptyLine) { + unset($this->lineCoverage[$filename][$emptyLine]); + } + } + } + + private function getEmptyLinesForFile(string $filename): array + { + if (!isset(self::$emptyLineCache[$filename])) { + self::$emptyLineCache[$filename] = []; + + if (is_file($filename)) { + $sourceLines = explode("\n", file_get_contents($filename)); + + foreach ($sourceLines as $line => $source) { + if (trim($source) === '') { + self::$emptyLineCache[$filename][] = ($line + 1); + } + } + } + } + + return self::$emptyLineCache[$filename]; + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Report/Clover.php b/app/vendor/phpunit/php-code-coverage/src/Report/Clover.php index e0f893ce2..4634fbafd 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Report/Clover.php +++ b/app/vendor/phpunit/php-code-coverage/src/Report/Clover.php @@ -1,6 +1,6 @@ * @@ -9,31 +9,40 @@ */ namespace SebastianBergmann\CodeCoverage\Report; +use function count; +use function dirname; +use function file_put_contents; +use function is_string; +use function ksort; +use function max; +use function range; +use function time; +use DOMDocument; use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Directory; +use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; use SebastianBergmann\CodeCoverage\Node\File; -use SebastianBergmann\CodeCoverage\RuntimeException; -/** - * Generates a Clover XML logfile from a code coverage object. - */ final class Clover { /** - * @throws \RuntimeException + * @throws WriteOperationFailedException */ public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string { - $xmlDocument = new \DOMDocument('1.0', 'UTF-8'); + $time = (string) time(); + + $xmlDocument = new DOMDocument('1.0', 'UTF-8'); $xmlDocument->formatOutput = true; $xmlCoverage = $xmlDocument->createElement('coverage'); - $xmlCoverage->setAttribute('generated', (string) $_SERVER['REQUEST_TIME']); + $xmlCoverage->setAttribute('generated', $time); $xmlDocument->appendChild($xmlCoverage); $xmlProject = $xmlDocument->createElement('project'); - $xmlProject->setAttribute('timestamp', (string) $_SERVER['REQUEST_TIME']); + $xmlProject->setAttribute('timestamp', $time); - if (\is_string($name)) { + if (is_string($name)) { $xmlProject->setAttribute('name', $name); } @@ -50,10 +59,10 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string /* @var File $item */ $xmlFile = $xmlDocument->createElement('file'); - $xmlFile->setAttribute('name', $item->getPath()); + $xmlFile->setAttribute('name', $item->pathAsString()); - $classes = $item->getClassesAndTraits(); - $coverageData = $item->getCoverageData(); + $classes = $item->classesAndTraits(); + $coverageData = $item->lineCoverageData(); $lines = []; $namespace = 'global'; @@ -78,19 +87,19 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string $methodCount = 0; - foreach (\range($method['startLine'], $method['endLine']) as $line) { + foreach (range($method['startLine'], $method['endLine']) as $line) { if (isset($coverageData[$line]) && ($coverageData[$line] !== null)) { - $methodCount = \max($methodCount, \count($coverageData[$line])); + $methodCount = max($methodCount, count($coverageData[$line])); } } $lines[$method['startLine']] = [ - 'ccn' => $method['ccn'], - 'count' => $methodCount, - 'crap' => $method['crap'], - 'type' => 'method', - 'visibility' => $method['visibility'], - 'name' => $methodName, + 'ccn' => $method['ccn'], + 'count' => $methodCount, + 'crap' => $method['crap'], + 'type' => 'method', + 'visibility' => $method['visibility'], + 'name' => $methodName, ]; } @@ -136,12 +145,12 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string $xmlMetrics->setAttribute('complexity', (string) $class['ccn']); $xmlMetrics->setAttribute('methods', (string) $classMethods); $xmlMetrics->setAttribute('coveredmethods', (string) $coveredMethods); - $xmlMetrics->setAttribute('conditionals', '0'); - $xmlMetrics->setAttribute('coveredconditionals', '0'); + $xmlMetrics->setAttribute('conditionals', (string) $class['executableBranches']); + $xmlMetrics->setAttribute('coveredconditionals', (string) $class['executedBranches']); $xmlMetrics->setAttribute('statements', (string) $classStatements); $xmlMetrics->setAttribute('coveredstatements', (string) $coveredClassStatements); - $xmlMetrics->setAttribute('elements', (string) ($classMethods + $classStatements /* + conditionals */)); - $xmlMetrics->setAttribute('coveredelements', (string) ($coveredMethods + $coveredClassStatements /* + coveredconditionals */)); + $xmlMetrics->setAttribute('elements', (string) ($classMethods + $classStatements + $class['executableBranches'])); + $xmlMetrics->setAttribute('coveredelements', (string) ($coveredMethods + $coveredClassStatements + $class['executedBranches'])); $xmlClass->appendChild($xmlMetrics); } @@ -151,11 +160,11 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string } $lines[$line] = [ - 'count' => \count($data), 'type' => 'stmt', + 'count' => count($data), 'type' => 'stmt', ]; } - \ksort($lines); + ksort($lines); foreach ($lines as $line => $data) { $xmlLine = $xmlDocument->createElement('line'); @@ -182,20 +191,20 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string $xmlFile->appendChild($xmlLine); } - $linesOfCode = $item->getLinesOfCode(); + $linesOfCode = $item->linesOfCode(); $xmlMetrics = $xmlDocument->createElement('metrics'); - $xmlMetrics->setAttribute('loc', (string) $linesOfCode['loc']); - $xmlMetrics->setAttribute('ncloc', (string) $linesOfCode['ncloc']); - $xmlMetrics->setAttribute('classes', (string) $item->getNumClassesAndTraits()); - $xmlMetrics->setAttribute('methods', (string) $item->getNumMethods()); - $xmlMetrics->setAttribute('coveredmethods', (string) $item->getNumTestedMethods()); - $xmlMetrics->setAttribute('conditionals', '0'); - $xmlMetrics->setAttribute('coveredconditionals', '0'); - $xmlMetrics->setAttribute('statements', (string) $item->getNumExecutableLines()); - $xmlMetrics->setAttribute('coveredstatements', (string) $item->getNumExecutedLines()); - $xmlMetrics->setAttribute('elements', (string) ($item->getNumMethods() + $item->getNumExecutableLines() /* + conditionals */)); - $xmlMetrics->setAttribute('coveredelements', (string) ($item->getNumTestedMethods() + $item->getNumExecutedLines() /* + coveredconditionals */)); + $xmlMetrics->setAttribute('loc', (string) $linesOfCode->linesOfCode()); + $xmlMetrics->setAttribute('ncloc', (string) $linesOfCode->nonCommentLinesOfCode()); + $xmlMetrics->setAttribute('classes', (string) $item->numberOfClassesAndTraits()); + $xmlMetrics->setAttribute('methods', (string) $item->numberOfMethods()); + $xmlMetrics->setAttribute('coveredmethods', (string) $item->numberOfTestedMethods()); + $xmlMetrics->setAttribute('conditionals', (string) $item->numberOfExecutableBranches()); + $xmlMetrics->setAttribute('coveredconditionals', (string) $item->numberOfExecutedBranches()); + $xmlMetrics->setAttribute('statements', (string) $item->numberOfExecutableLines()); + $xmlMetrics->setAttribute('coveredstatements', (string) $item->numberOfExecutedLines()); + $xmlMetrics->setAttribute('elements', (string) ($item->numberOfMethods() + $item->numberOfExecutableLines() + $item->numberOfExecutableBranches())); + $xmlMetrics->setAttribute('coveredelements', (string) ($item->numberOfTestedMethods() + $item->numberOfExecutedLines() + $item->numberOfExecutedBranches())); $xmlFile->appendChild($xmlMetrics); if ($namespace === 'global') { @@ -214,45 +223,33 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string } } - $linesOfCode = $report->getLinesOfCode(); + $linesOfCode = $report->linesOfCode(); $xmlMetrics = $xmlDocument->createElement('metrics'); - $xmlMetrics->setAttribute('files', (string) \count($report)); - $xmlMetrics->setAttribute('loc', (string) $linesOfCode['loc']); - $xmlMetrics->setAttribute('ncloc', (string) $linesOfCode['ncloc']); - $xmlMetrics->setAttribute('classes', (string) $report->getNumClassesAndTraits()); - $xmlMetrics->setAttribute('methods', (string) $report->getNumMethods()); - $xmlMetrics->setAttribute('coveredmethods', (string) $report->getNumTestedMethods()); - $xmlMetrics->setAttribute('conditionals', '0'); - $xmlMetrics->setAttribute('coveredconditionals', '0'); - $xmlMetrics->setAttribute('statements', (string) $report->getNumExecutableLines()); - $xmlMetrics->setAttribute('coveredstatements', (string) $report->getNumExecutedLines()); - $xmlMetrics->setAttribute('elements', (string) ($report->getNumMethods() + $report->getNumExecutableLines() /* + conditionals */)); - $xmlMetrics->setAttribute('coveredelements', (string) ($report->getNumTestedMethods() + $report->getNumExecutedLines() /* + coveredconditionals */)); + $xmlMetrics->setAttribute('files', (string) count($report)); + $xmlMetrics->setAttribute('loc', (string) $linesOfCode->linesOfCode()); + $xmlMetrics->setAttribute('ncloc', (string) $linesOfCode->nonCommentLinesOfCode()); + $xmlMetrics->setAttribute('classes', (string) $report->numberOfClassesAndTraits()); + $xmlMetrics->setAttribute('methods', (string) $report->numberOfMethods()); + $xmlMetrics->setAttribute('coveredmethods', (string) $report->numberOfTestedMethods()); + $xmlMetrics->setAttribute('conditionals', (string) $report->numberOfExecutableBranches()); + $xmlMetrics->setAttribute('coveredconditionals', (string) $report->numberOfExecutedBranches()); + $xmlMetrics->setAttribute('statements', (string) $report->numberOfExecutableLines()); + $xmlMetrics->setAttribute('coveredstatements', (string) $report->numberOfExecutedLines()); + $xmlMetrics->setAttribute('elements', (string) ($report->numberOfMethods() + $report->numberOfExecutableLines() + $report->numberOfExecutableBranches())); + $xmlMetrics->setAttribute('coveredelements', (string) ($report->numberOfTestedMethods() + $report->numberOfExecutedLines() + $report->numberOfExecutedBranches())); $xmlProject->appendChild($xmlMetrics); $buffer = $xmlDocument->saveXML(); if ($target !== null) { - if (!$this->createDirectory(\dirname($target))) { - throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target))); - } + Directory::create(dirname($target)); - if (@\file_put_contents($target, $buffer) === false) { - throw new RuntimeException( - \sprintf( - 'Could not write to "%s', - $target - ) - ); + if (@file_put_contents($target, $buffer) === false) { + throw new WriteOperationFailedException($target); } } return $buffer; } - - private function createDirectory(string $directory): bool - { - return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); - } } diff --git a/app/vendor/phpunit/php-code-coverage/src/Report/Cobertura.php b/app/vendor/phpunit/php-code-coverage/src/Report/Cobertura.php new file mode 100644 index 000000000..e89484f65 --- /dev/null +++ b/app/vendor/phpunit/php-code-coverage/src/Report/Cobertura.php @@ -0,0 +1,304 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use function count; +use function dirname; +use function file_put_contents; +use function range; +use function time; +use DOMImplementation; +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Directory; +use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; +use SebastianBergmann\CodeCoverage\Node\File; + +final class Cobertura +{ + /** + * @throws WriteOperationFailedException + */ + public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string + { + $time = (string) time(); + + $report = $coverage->getReport(); + + $implementation = new DOMImplementation; + + $documentType = $implementation->createDocumentType( + 'coverage', + '', + 'http://cobertura.sourceforge.net/xml/coverage-04.dtd' + ); + + $document = $implementation->createDocument('', '', $documentType); + $document->xmlVersion = '1.0'; + $document->encoding = 'UTF-8'; + $document->formatOutput = true; + + $coverageElement = $document->createElement('coverage'); + + $linesValid = $report->numberOfExecutableLines(); + $linesCovered = $report->numberOfExecutedLines(); + $lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid); + $coverageElement->setAttribute('line-rate', (string) $lineRate); + + $branchesValid = $report->numberOfExecutableBranches(); + $branchesCovered = $report->numberOfExecutedBranches(); + $branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid); + $coverageElement->setAttribute('branch-rate', (string) $branchRate); + + $coverageElement->setAttribute('lines-covered', (string) $report->numberOfExecutedLines()); + $coverageElement->setAttribute('lines-valid', (string) $report->numberOfExecutableLines()); + $coverageElement->setAttribute('branches-covered', (string) $report->numberOfExecutedBranches()); + $coverageElement->setAttribute('branches-valid', (string) $report->numberOfExecutableBranches()); + $coverageElement->setAttribute('complexity', ''); + $coverageElement->setAttribute('version', '0.4'); + $coverageElement->setAttribute('timestamp', $time); + + $document->appendChild($coverageElement); + + $sourcesElement = $document->createElement('sources'); + $coverageElement->appendChild($sourcesElement); + + $sourceElement = $document->createElement('source', $report->pathAsString()); + $sourcesElement->appendChild($sourceElement); + + $packagesElement = $document->createElement('packages'); + $coverageElement->appendChild($packagesElement); + + $complexity = 0; + + foreach ($report as $item) { + if (!$item instanceof File) { + continue; + } + + $packageElement = $document->createElement('package'); + $packageComplexity = 0; + $packageName = $name ?? ''; + + $packageElement->setAttribute('name', $packageName); + + $linesValid = $item->numberOfExecutableLines(); + $linesCovered = $item->numberOfExecutedLines(); + $lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid); + + $packageElement->setAttribute('line-rate', (string) $lineRate); + + $branchesValid = $item->numberOfExecutableBranches(); + $branchesCovered = $item->numberOfExecutedBranches(); + $branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid); + + $packageElement->setAttribute('branch-rate', (string) $branchRate); + + $packageElement->setAttribute('complexity', ''); + $packagesElement->appendChild($packageElement); + + $classesElement = $document->createElement('classes'); + + $packageElement->appendChild($classesElement); + + $classes = $item->classesAndTraits(); + $coverageData = $item->lineCoverageData(); + + foreach ($classes as $className => $class) { + $complexity += $class['ccn']; + $packageComplexity += $class['ccn']; + + if (!empty($class['package']['namespace'])) { + $className = $class['package']['namespace'] . '\\' . $className; + } + + $linesValid = $class['executableLines']; + $linesCovered = $class['executedLines']; + $lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid); + + $branchesValid = $class['executableBranches']; + $branchesCovered = $class['executedBranches']; + $branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid); + + $classElement = $document->createElement('class'); + + $classElement->setAttribute('name', $className); + $classElement->setAttribute('filename', str_replace($report->pathAsString() . DIRECTORY_SEPARATOR, '', $item->pathAsString())); + $classElement->setAttribute('line-rate', (string) $lineRate); + $classElement->setAttribute('branch-rate', (string) $branchRate); + $classElement->setAttribute('complexity', (string) $class['ccn']); + + $classesElement->appendChild($classElement); + + $methodsElement = $document->createElement('methods'); + + $classElement->appendChild($methodsElement); + + $classLinesElement = $document->createElement('lines'); + + $classElement->appendChild($classLinesElement); + + foreach ($class['methods'] as $methodName => $method) { + if ($method['executableLines'] === 0) { + continue; + } + + preg_match("/\((.*?)\)/", $method['signature'], $signature); + + $linesValid = $method['executableLines']; + $linesCovered = $method['executedLines']; + $lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid); + + $branchesValid = $method['executableBranches']; + $branchesCovered = $method['executedBranches']; + $branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid); + + $methodElement = $document->createElement('method'); + + $methodElement->setAttribute('name', $methodName); + $methodElement->setAttribute('signature', $signature[1]); + $methodElement->setAttribute('line-rate', (string) $lineRate); + $methodElement->setAttribute('branch-rate', (string) $branchRate); + $methodElement->setAttribute('complexity', (string) $method['ccn']); + + $methodLinesElement = $document->createElement('lines'); + + $methodElement->appendChild($methodLinesElement); + + foreach (range($method['startLine'], $method['endLine']) as $line) { + if (!isset($coverageData[$line]) || $coverageData[$line] === null) { + continue; + } + $methodLineElement = $document->createElement('line'); + + $methodLineElement->setAttribute('number', (string) $line); + $methodLineElement->setAttribute('hits', (string) count($coverageData[$line])); + + $methodLinesElement->appendChild($methodLineElement); + + $classLineElement = $methodLineElement->cloneNode(); + + $classLinesElement->appendChild($classLineElement); + } + + $methodsElement->appendChild($methodElement); + } + } + + if ($report->numberOfFunctions() === 0) { + $packageElement->setAttribute('complexity', (string) $packageComplexity); + + continue; + } + + $functionsComplexity = 0; + $functionsLinesValid = 0; + $functionsLinesCovered = 0; + $functionsBranchesValid = 0; + $functionsBranchesCovered = 0; + + $classElement = $document->createElement('class'); + $classElement->setAttribute('name', basename($item->pathAsString())); + $classElement->setAttribute('filename', str_replace($report->pathAsString() . DIRECTORY_SEPARATOR, '', $item->pathAsString())); + + $methodsElement = $document->createElement('methods'); + + $classElement->appendChild($methodsElement); + + $classLinesElement = $document->createElement('lines'); + + $classElement->appendChild($classLinesElement); + + $functions = $report->functions(); + + foreach ($functions as $functionName => $function) { + if ($function['executableLines'] === 0) { + continue; + } + + $complexity += $function['ccn']; + $packageComplexity += $function['ccn']; + $functionsComplexity += $function['ccn']; + + $linesValid = $function['executableLines']; + $linesCovered = $function['executedLines']; + $lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid); + + $functionsLinesValid += $linesValid; + $functionsLinesCovered += $linesCovered; + + $branchesValid = $function['executableBranches']; + $branchesCovered = $function['executedBranches']; + $branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid); + + $functionsBranchesValid += $branchesValid; + $functionsBranchesCovered += $branchesValid; + + $methodElement = $document->createElement('method'); + + $methodElement->setAttribute('name', $functionName); + $methodElement->setAttribute('signature', $function['signature']); + $methodElement->setAttribute('line-rate', (string) $lineRate); + $methodElement->setAttribute('branch-rate', (string) $branchRate); + $methodElement->setAttribute('complexity', (string) $function['ccn']); + + $methodLinesElement = $document->createElement('lines'); + + $methodElement->appendChild($methodLinesElement); + + foreach (range($function['startLine'], $function['endLine']) as $line) { + if (!isset($coverageData[$line]) || $coverageData[$line] === null) { + continue; + } + $methodLineElement = $document->createElement('line'); + + $methodLineElement->setAttribute('number', (string) $line); + $methodLineElement->setAttribute('hits', (string) count($coverageData[$line])); + + $methodLinesElement->appendChild($methodLineElement); + + $classLineElement = $methodLineElement->cloneNode(); + + $classLinesElement->appendChild($classLineElement); + } + + $methodsElement->appendChild($methodElement); + } + + $packageElement->setAttribute('complexity', (string) $packageComplexity); + + if ($functionsLinesValid === 0) { + continue; + } + + $lineRate = $functionsLinesCovered / $functionsLinesValid; + $branchRate = $functionsBranchesValid === 0 ? 0 : ($functionsBranchesCovered / $functionsBranchesValid); + + $classElement->setAttribute('line-rate', (string) $lineRate); + $classElement->setAttribute('branch-rate', (string) $branchRate); + $classElement->setAttribute('complexity', (string) $functionsComplexity); + + $classesElement->appendChild($classElement); + } + + $coverageElement->setAttribute('complexity', (string) $complexity); + + $buffer = $document->saveXML(); + + if ($target !== null) { + Directory::create(dirname($target)); + + if (@file_put_contents($target, $buffer) === false) { + throw new WriteOperationFailedException($target); + } + } + + return $buffer; + } +} diff --git a/app/vendor/phpunit/php-code-coverage/src/Report/Crap4j.php b/app/vendor/phpunit/php-code-coverage/src/Report/Crap4j.php index 6713be0c8..bed16bcdd 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Report/Crap4j.php +++ b/app/vendor/phpunit/php-code-coverage/src/Report/Crap4j.php @@ -1,6 +1,6 @@ * @@ -9,9 +9,17 @@ */ namespace SebastianBergmann\CodeCoverage\Report; +use function date; +use function dirname; +use function file_put_contents; +use function htmlspecialchars; +use function is_string; +use function round; +use DOMDocument; use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Directory; +use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException; use SebastianBergmann\CodeCoverage\Node\File; -use SebastianBergmann\CodeCoverage\RuntimeException; final class Crap4j { @@ -26,19 +34,19 @@ public function __construct(int $threshold = 30) } /** - * @throws \RuntimeException + * @throws WriteOperationFailedException */ public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string { - $document = new \DOMDocument('1.0', 'UTF-8'); + $document = new DOMDocument('1.0', 'UTF-8'); $document->formatOutput = true; $root = $document->createElement('crap_result'); $document->appendChild($root); - $project = $document->createElement('project', \is_string($name) ? $name : ''); + $project = $document->createElement('project', is_string($name) ? $name : ''); $root->appendChild($project); - $root->appendChild($document->createElement('timestamp', \date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']))); + $root->appendChild($document->createElement('timestamp', date('Y-m-d H:i:s'))); $stats = $document->createElement('stats'); $methodsNode = $document->createElement('methods'); @@ -59,13 +67,13 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string } $file = $document->createElement('file'); - $file->setAttribute('name', $item->getPath()); + $file->setAttribute('name', $item->pathAsString()); - $classes = $item->getClassesAndTraits(); + $classes = $item->classesAndTraits(); foreach ($classes as $className => $class) { foreach ($class['methods'] as $methodName => $method) { - $crapLoad = $this->getCrapLoad($method['crap'], $method['ccn'], $method['coverage']); + $crapLoad = $this->crapLoad((float) $method['crap'], $method['ccn'], $method['coverage']); $fullCrap += $method['crap']; $fullCrapLoad += $crapLoad; @@ -77,19 +85,19 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string $methodNode = $document->createElement('method'); - if (!empty($class['package']['namespace'])) { - $namespace = $class['package']['namespace']; + if (!empty($class['namespace'])) { + $namespace = $class['namespace']; } $methodNode->appendChild($document->createElement('package', $namespace)); $methodNode->appendChild($document->createElement('className', $className)); $methodNode->appendChild($document->createElement('methodName', $methodName)); - $methodNode->appendChild($document->createElement('methodSignature', \htmlspecialchars($method['signature']))); - $methodNode->appendChild($document->createElement('fullMethod', \htmlspecialchars($method['signature']))); - $methodNode->appendChild($document->createElement('crap', (string) $this->roundValue($method['crap']))); + $methodNode->appendChild($document->createElement('methodSignature', htmlspecialchars($method['signature']))); + $methodNode->appendChild($document->createElement('fullMethod', htmlspecialchars($method['signature']))); + $methodNode->appendChild($document->createElement('crap', (string) $this->roundValue((float) $method['crap']))); $methodNode->appendChild($document->createElement('complexity', (string) $method['ccn'])); $methodNode->appendChild($document->createElement('coverage', (string) $this->roundValue($method['coverage']))); - $methodNode->appendChild($document->createElement('crapLoad', (string) \round($crapLoad))); + $methodNode->appendChild($document->createElement('crapLoad', (string) round($crapLoad))); $methodsNode->appendChild($methodNode); } @@ -99,7 +107,7 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string $stats->appendChild($document->createElement('name', 'Method Crap Stats')); $stats->appendChild($document->createElement('methodCount', (string) $fullMethodCount)); $stats->appendChild($document->createElement('crapMethodCount', (string) $fullCrapMethodCount)); - $stats->appendChild($document->createElement('crapLoad', (string) \round($fullCrapLoad))); + $stats->appendChild($document->createElement('crapLoad', (string) round($fullCrapLoad))); $stats->appendChild($document->createElement('totalCrap', (string) $fullCrap)); $crapMethodPercent = 0; @@ -116,29 +124,17 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string $buffer = $document->saveXML(); if ($target !== null) { - if (!$this->createDirectory(\dirname($target))) { - throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target))); - } + Directory::create(dirname($target)); - if (@\file_put_contents($target, $buffer) === false) { - throw new RuntimeException( - \sprintf( - 'Could not write to "%s', - $target - ) - ); + if (@file_put_contents($target, $buffer) === false) { + throw new WriteOperationFailedException($target); } } return $buffer; } - /** - * @param float $crapValue - * @param int $cyclomaticComplexity - * @param float $coveragePercent - */ - private function getCrapLoad($crapValue, $cyclomaticComplexity, $coveragePercent): float + private function crapLoad(float $crapValue, int $cyclomaticComplexity, float $coveragePercent): float { $crapLoad = 0; @@ -150,16 +146,8 @@ private function getCrapLoad($crapValue, $cyclomaticComplexity, $coveragePercent return $crapLoad; } - /** - * @param float $value - */ - private function roundValue($value): float - { - return \round($value, 2); - } - - private function createDirectory(string $directory): bool + private function roundValue(float $value): float { - return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); + return round($value, 2); } } diff --git a/app/vendor/phpunit/php-code-coverage/src/Report/Html/Facade.php b/app/vendor/phpunit/php-code-coverage/src/Report/Html/Facade.php index 318b49a5a..8fe95d40b 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Report/Html/Facade.php +++ b/app/vendor/phpunit/php-code-coverage/src/Report/Html/Facade.php @@ -1,6 +1,6 @@ * @@ -9,13 +9,15 @@ */ namespace SebastianBergmann\CodeCoverage\Report\Html; +use const DIRECTORY_SEPARATOR; +use function copy; +use function date; +use function dirname; +use function substr; use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Directory as DirectoryUtil; use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; -use SebastianBergmann\CodeCoverage\RuntimeException; -/** - * Generates an HTML report from a code coverage object. - */ final class Facade { /** @@ -46,28 +48,19 @@ public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, s $this->templatePath = __DIR__ . '/Renderer/Template/'; } - /** - * @throws RuntimeException - * @throws \InvalidArgumentException - * @throws \RuntimeException - */ public function process(CodeCoverage $coverage, string $target): void { - $target = $this->getDirectory($target); + $target = $this->directory($target); $report = $coverage->getReport(); - - if (!isset($_SERVER['REQUEST_TIME'])) { - $_SERVER['REQUEST_TIME'] = \time(); - } - - $date = \date('D M j G:i:s T Y', $_SERVER['REQUEST_TIME']); + $date = date('D M j G:i:s T Y'); $dashboard = new Dashboard( $this->templatePath, $this->generator, $date, $this->lowUpperBound, - $this->highLowerBound + $this->highLowerBound, + $coverage->collectsBranchAndPathCoverage() ); $directory = new Directory( @@ -75,7 +68,8 @@ public function process(CodeCoverage $coverage, string $target): void $this->generator, $date, $this->lowUpperBound, - $this->highLowerBound + $this->highLowerBound, + $coverage->collectsBranchAndPathCoverage() ); $file = new File( @@ -83,85 +77,64 @@ public function process(CodeCoverage $coverage, string $target): void $this->generator, $date, $this->lowUpperBound, - $this->highLowerBound + $this->highLowerBound, + $coverage->collectsBranchAndPathCoverage() ); $directory->render($report, $target . 'index.html'); $dashboard->render($report, $target . 'dashboard.html'); foreach ($report as $node) { - $id = $node->getId(); + $id = $node->id(); if ($node instanceof DirectoryNode) { - if (!$this->createDirectory($target . $id)) { - throw new \RuntimeException(\sprintf('Directory "%s" was not created', $target . $id)); - } + DirectoryUtil::create($target . $id); $directory->render($node, $target . $id . '/index.html'); $dashboard->render($node, $target . $id . '/dashboard.html'); } else { - $dir = \dirname($target . $id); + $dir = dirname($target . $id); - if (!$this->createDirectory($dir)) { - throw new \RuntimeException(\sprintf('Directory "%s" was not created', $dir)); - } + DirectoryUtil::create($dir); - $file->render($node, $target . $id . '.html'); + $file->render($node, $target . $id); } } $this->copyFiles($target); } - /** - * @throws RuntimeException - */ private function copyFiles(string $target): void { - $dir = $this->getDirectory($target . '_css'); - - \copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css'); - \copy($this->templatePath . 'css/nv.d3.min.css', $dir . 'nv.d3.min.css'); - \copy($this->templatePath . 'css/style.css', $dir . 'style.css'); - \copy($this->templatePath . 'css/custom.css', $dir . 'custom.css'); - \copy($this->templatePath . 'css/octicons.css', $dir . 'octicons.css'); - - $dir = $this->getDirectory($target . '_icons'); - \copy($this->templatePath . 'icons/file-code.svg', $dir . 'file-code.svg'); - \copy($this->templatePath . 'icons/file-directory.svg', $dir . 'file-directory.svg'); - - $dir = $this->getDirectory($target . '_js'); - \copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js'); - \copy($this->templatePath . 'js/popper.min.js', $dir . 'popper.min.js'); - \copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js'); - \copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js'); - \copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js'); - \copy($this->templatePath . 'js/file.js', $dir . 'file.js'); + $dir = $this->directory($target . '_css'); + + copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css'); + copy($this->templatePath . 'css/nv.d3.min.css', $dir . 'nv.d3.min.css'); + copy($this->templatePath . 'css/style.css', $dir . 'style.css'); + copy($this->templatePath . 'css/custom.css', $dir . 'custom.css'); + copy($this->templatePath . 'css/octicons.css', $dir . 'octicons.css'); + + $dir = $this->directory($target . '_icons'); + copy($this->templatePath . 'icons/file-code.svg', $dir . 'file-code.svg'); + copy($this->templatePath . 'icons/file-directory.svg', $dir . 'file-directory.svg'); + + $dir = $this->directory($target . '_js'); + copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js'); + copy($this->templatePath . 'js/popper.min.js', $dir . 'popper.min.js'); + copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js'); + copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js'); + copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js'); + copy($this->templatePath . 'js/file.js', $dir . 'file.js'); } - /** - * @throws RuntimeException - */ - private function getDirectory(string $directory): string + private function directory(string $directory): string { - if (\substr($directory, -1, 1) != \DIRECTORY_SEPARATOR) { - $directory .= \DIRECTORY_SEPARATOR; + if (substr($directory, -1, 1) != DIRECTORY_SEPARATOR) { + $directory .= DIRECTORY_SEPARATOR; } - if (!$this->createDirectory($directory)) { - throw new RuntimeException( - \sprintf( - 'Directory "%s" does not exist.', - $directory - ) - ); - } + DirectoryUtil::create($directory); return $directory; } - - private function createDirectory(string $directory): bool - { - return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); - } } diff --git a/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer.php b/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer.php index 2a9024c0e..fe285b186 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer.php +++ b/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer.php @@ -1,6 +1,6 @@ * @@ -9,14 +9,20 @@ */ namespace SebastianBergmann\CodeCoverage\Report\Html; +use function array_pop; +use function count; +use function sprintf; +use function str_repeat; +use function substr_count; use SebastianBergmann\CodeCoverage\Node\AbstractNode; use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; use SebastianBergmann\CodeCoverage\Node\File as FileNode; use SebastianBergmann\CodeCoverage\Version; use SebastianBergmann\Environment\Runtime; +use SebastianBergmann\Template\Template; /** - * Base class for node renderers. + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ abstract class Renderer { @@ -45,32 +51,38 @@ abstract class Renderer */ protected $highLowerBound; + /** + * @var bool + */ + protected $hasBranchCoverage; + /** * @var string */ protected $version; - public function __construct(string $templatePath, string $generator, string $date, int $lowUpperBound, int $highLowerBound) + public function __construct(string $templatePath, string $generator, string $date, int $lowUpperBound, int $highLowerBound, bool $hasBranchCoverage) { - $this->templatePath = $templatePath; - $this->generator = $generator; - $this->date = $date; - $this->lowUpperBound = $lowUpperBound; - $this->highLowerBound = $highLowerBound; - $this->version = Version::id(); + $this->templatePath = $templatePath; + $this->generator = $generator; + $this->date = $date; + $this->lowUpperBound = $lowUpperBound; + $this->highLowerBound = $highLowerBound; + $this->version = Version::id(); + $this->hasBranchCoverage = $hasBranchCoverage; } - protected function renderItemTemplate(\Text_Template $template, array $data): string + protected function renderItemTemplate(Template $template, array $data): string { - $numSeparator = ' / '; + $numSeparator = ' / '; if (isset($data['numClasses']) && $data['numClasses'] > 0) { - $classesLevel = $this->getColorLevel($data['testedClassesPercent']); + $classesLevel = $this->colorLevel($data['testedClassesPercent']); $classesNumber = $data['numTestedClasses'] . $numSeparator . $data['numClasses']; - $classesBar = $this->getCoverageBar( + $classesBar = $this->coverageBar( $data['testedClassesPercent'] ); } else { @@ -81,12 +93,12 @@ protected function renderItemTemplate(\Text_Template $template, array $data): st } if ($data['numMethods'] > 0) { - $methodsLevel = $this->getColorLevel($data['testedMethodsPercent']); + $methodsLevel = $this->colorLevel($data['testedMethodsPercent']); $methodsNumber = $data['numTestedMethods'] . $numSeparator . $data['numMethods']; - $methodsBar = $this->getCoverageBar( + $methodsBar = $this->coverageBar( $data['testedMethodsPercent'] ); } else { @@ -97,12 +109,12 @@ protected function renderItemTemplate(\Text_Template $template, array $data): st } if ($data['numExecutableLines'] > 0) { - $linesLevel = $this->getColorLevel($data['linesExecutedPercent']); + $linesLevel = $this->colorLevel($data['linesExecutedPercent']); $linesNumber = $data['numExecutedLines'] . $numSeparator . $data['numExecutableLines']; - $linesBar = $this->getCoverageBar( + $linesBar = $this->coverageBar( $data['linesExecutedPercent'] ); } else { @@ -112,40 +124,80 @@ protected function renderItemTemplate(\Text_Template $template, array $data): st $data['linesExecutedPercentAsString'] = 'n/a'; } + if ($data['numExecutablePaths'] > 0) { + $pathsLevel = $this->colorLevel($data['pathsExecutedPercent']); + + $pathsNumber = $data['numExecutedPaths'] . $numSeparator . + $data['numExecutablePaths']; + + $pathsBar = $this->coverageBar( + $data['pathsExecutedPercent'] + ); + } else { + $pathsLevel = ''; + $pathsNumber = '0' . $numSeparator . '0'; + $pathsBar = ''; + $data['pathsExecutedPercentAsString'] = 'n/a'; + } + + if ($data['numExecutableBranches'] > 0) { + $branchesLevel = $this->colorLevel($data['branchesExecutedPercent']); + + $branchesNumber = $data['numExecutedBranches'] . $numSeparator . + $data['numExecutableBranches']; + + $branchesBar = $this->coverageBar( + $data['branchesExecutedPercent'] + ); + } else { + $branchesLevel = ''; + $branchesNumber = '0' . $numSeparator . '0'; + $branchesBar = ''; + $data['branchesExecutedPercentAsString'] = 'n/a'; + } + $template->setVar( [ - 'icon' => $data['icon'] ?? '', - 'crap' => $data['crap'] ?? '', - 'name' => $data['name'], - 'lines_bar' => $linesBar, - 'lines_executed_percent' => $data['linesExecutedPercentAsString'], - 'lines_level' => $linesLevel, - 'lines_number' => $linesNumber, - 'methods_bar' => $methodsBar, - 'methods_tested_percent' => $data['testedMethodsPercentAsString'], - 'methods_level' => $methodsLevel, - 'methods_number' => $methodsNumber, - 'classes_bar' => $classesBar, - 'classes_tested_percent' => $data['testedClassesPercentAsString'] ?? '', - 'classes_level' => $classesLevel, - 'classes_number' => $classesNumber, + 'icon' => $data['icon'] ?? '', + 'crap' => $data['crap'] ?? '', + 'name' => $data['name'], + 'lines_bar' => $linesBar, + 'lines_executed_percent' => $data['linesExecutedPercentAsString'], + 'lines_level' => $linesLevel, + 'lines_number' => $linesNumber, + 'paths_bar' => $pathsBar, + 'paths_executed_percent' => $data['pathsExecutedPercentAsString'], + 'paths_level' => $pathsLevel, + 'paths_number' => $pathsNumber, + 'branches_bar' => $branchesBar, + 'branches_executed_percent' => $data['branchesExecutedPercentAsString'], + 'branches_level' => $branchesLevel, + 'branches_number' => $branchesNumber, + 'methods_bar' => $methodsBar, + 'methods_tested_percent' => $data['testedMethodsPercentAsString'], + 'methods_level' => $methodsLevel, + 'methods_number' => $methodsNumber, + 'classes_bar' => $classesBar, + 'classes_tested_percent' => $data['testedClassesPercentAsString'] ?? '', + 'classes_level' => $classesLevel, + 'classes_number' => $classesNumber, ] ); return $template->render(); } - protected function setCommonTemplateVariables(\Text_Template $template, AbstractNode $node): void + protected function setCommonTemplateVariables(Template $template, AbstractNode $node): void { $template->setVar( [ - 'id' => $node->getId(), - 'full_path' => $node->getPath(), - 'path_to_root' => $this->getPathToRoot($node), - 'breadcrumbs' => $this->getBreadcrumbs($node), + 'id' => $node->id(), + 'full_path' => $node->pathAsString(), + 'path_to_root' => $this->pathToRoot($node), + 'breadcrumbs' => $this->breadcrumbs($node), 'date' => $this->date, 'version' => $this->version, - 'runtime' => $this->getRuntimeString(), + 'runtime' => $this->runtimeString(), 'generator' => $this->generator, 'low_upper_bound' => $this->lowUpperBound, 'high_lower_bound' => $this->highLowerBound, @@ -153,40 +205,40 @@ protected function setCommonTemplateVariables(\Text_Template $template, Abstract ); } - protected function getBreadcrumbs(AbstractNode $node): string + protected function breadcrumbs(AbstractNode $node): string { $breadcrumbs = ''; - $path = $node->getPathAsArray(); + $path = $node->pathAsArray(); $pathToRoot = []; - $max = \count($path); + $max = count($path); if ($node instanceof FileNode) { $max--; } for ($i = 0; $i < $max; $i++) { - $pathToRoot[] = \str_repeat('../', $i); + $pathToRoot[] = str_repeat('../', $i); } foreach ($path as $step) { if ($step !== $node) { - $breadcrumbs .= $this->getInactiveBreadcrumb( + $breadcrumbs .= $this->inactiveBreadcrumb( $step, - \array_pop($pathToRoot) + array_pop($pathToRoot) ); } else { - $breadcrumbs .= $this->getActiveBreadcrumb($step); + $breadcrumbs .= $this->activeBreadcrumb($step); } } return $breadcrumbs; } - protected function getActiveBreadcrumb(AbstractNode $node): string + protected function activeBreadcrumb(AbstractNode $node): string { - $buffer = \sprintf( + $buffer = sprintf( ' ' . "\n", - $node->getName() + $node->name() ); if ($node instanceof DirectoryNode) { @@ -196,44 +248,45 @@ protected function getActiveBreadcrumb(AbstractNode $node): string return $buffer; } - protected function getInactiveBreadcrumb(AbstractNode $node, string $pathToRoot): string + protected function inactiveBreadcrumb(AbstractNode $node, string $pathToRoot): string { - return \sprintf( + return sprintf( ' ' . "\n", $pathToRoot, - $node->getName() + $node->name() ); } - protected function getPathToRoot(AbstractNode $node): string + protected function pathToRoot(AbstractNode $node): string { - $id = $node->getId(); - $depth = \substr_count($id, '/'); + $id = $node->id(); + $depth = substr_count($id, '/'); if ($id !== 'index' && $node instanceof DirectoryNode) { $depth++; } - return \str_repeat('../', $depth); + return str_repeat('../', $depth); } - protected function getCoverageBar(float $percent): string + protected function coverageBar(float $percent): string { - $level = $this->getColorLevel($percent); + $level = $this->colorLevel($percent); - $template = new \Text_Template( - $this->templatePath . 'coverage_bar.html', + $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'coverage_bar_branch.html' : 'coverage_bar.html'); + $template = new Template( + $templateName, '{{', '}}' ); - $template->setVar(['level' => $level, 'percent' => \sprintf('%.2F', $percent)]); + $template->setVar(['level' => $level, 'percent' => sprintf('%.2F', $percent)]); return $template->render(); } - protected function getColorLevel(float $percent): string + protected function colorLevel(float $percent): string { if ($percent <= $this->lowUpperBound) { return 'danger'; @@ -247,31 +300,15 @@ protected function getColorLevel(float $percent): string return 'success'; } - private function getRuntimeString(): string + private function runtimeString(): string { $runtime = new Runtime; - $buffer = \sprintf( + return sprintf( '%s %s', $runtime->getVendorUrl(), $runtime->getName(), $runtime->getVersion() ); - - if ($runtime->hasXdebug() && !$runtime->hasPHPDBGCodeCoverage()) { - $buffer .= \sprintf( - ' with Xdebug %s', - \phpversion('xdebug') - ); - } - - if ($runtime->hasPCOV() && !$runtime->hasPHPDBGCodeCoverage()) { - $buffer .= \sprintf( - ' with PCOV %s', - \phpversion('pcov') - ); - } - - return $buffer; } } diff --git a/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.php b/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.php index cc801b678..b44870b53 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.php +++ b/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.php @@ -1,6 +1,6 @@ * @@ -9,30 +9,37 @@ */ namespace SebastianBergmann\CodeCoverage\Report\Html; +use function array_values; +use function arsort; +use function asort; +use function count; +use function explode; +use function floor; +use function json_encode; +use function sprintf; +use function str_replace; use SebastianBergmann\CodeCoverage\Node\AbstractNode; use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +use SebastianBergmann\Template\Template; /** - * Renders the dashboard for a directory node. + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ final class Dashboard extends Renderer { - /** - * @throws \InvalidArgumentException - * @throws \RuntimeException - */ public function render(DirectoryNode $node, string $file): void { - $classes = $node->getClassesAndTraits(); - $template = new \Text_Template( - $this->templatePath . 'dashboard.html', + $classes = $node->classesAndTraits(); + $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'dashboard_branch.html' : 'dashboard.html'); + $template = new Template( + $templateName, '{{', '}}' ); $this->setCommonTemplateVariables($template, $node); - $baseLink = $node->getId() . '/'; + $baseLink = $node->id() . '/'; $complexity = $this->complexity($classes, $baseLink); $coverageDistribution = $this->coverageDistribution($classes); $insufficientCoverage = $this->insufficientCoverage($classes, $baseLink); @@ -54,10 +61,19 @@ public function render(DirectoryNode $node, string $file): void $template->renderTo($file); } + protected function activeBreadcrumb(AbstractNode $node): string + { + return sprintf( + ' ' . "\n" . + ' ' . "\n", + $node->name() + ); + } + /** * Returns the data for the Class/Method Complexity charts. */ - protected function complexity(array $classes, string $baseLink): array + private function complexity(array $classes, string $baseLink): array { $result = ['class' => [], 'method' => []]; @@ -70,9 +86,9 @@ protected function complexity(array $classes, string $baseLink): array $result['method'][] = [ $method['coverage'], $method['ccn'], - \sprintf( + sprintf( '%s', - \str_replace($baseLink, '', $method['link']), + str_replace($baseLink, '', $method['link']), $methodName ), ]; @@ -81,24 +97,24 @@ protected function complexity(array $classes, string $baseLink): array $result['class'][] = [ $class['coverage'], $class['ccn'], - \sprintf( + sprintf( '%s', - \str_replace($baseLink, '', $class['link']), + str_replace($baseLink, '', $class['link']), $className ), ]; } return [ - 'class' => \json_encode($result['class']), - 'method' => \json_encode($result['method']), + 'class' => json_encode($result['class']), + 'method' => json_encode($result['method']), ]; } /** * Returns the data for the Class / Method Coverage Distribution chart. */ - protected function coverageDistribution(array $classes): array + private function coverageDistribution(array $classes): array { $result = [ 'class' => [ @@ -138,7 +154,7 @@ protected function coverageDistribution(array $classes): array } elseif ($method['coverage'] === 100) { $result['method']['100%']++; } else { - $key = \floor($method['coverage'] / 10) * 10; + $key = floor($method['coverage'] / 10) * 10; $key = $key . '-' . ($key + 10) . '%'; $result['method'][$key]++; } @@ -149,22 +165,22 @@ protected function coverageDistribution(array $classes): array } elseif ($class['coverage'] === 100) { $result['class']['100%']++; } else { - $key = \floor($class['coverage'] / 10) * 10; + $key = floor($class['coverage'] / 10) * 10; $key = $key . '-' . ($key + 10) . '%'; $result['class'][$key]++; } } return [ - 'class' => \json_encode(\array_values($result['class'])), - 'method' => \json_encode(\array_values($result['method'])), + 'class' => json_encode(array_values($result['class'])), + 'method' => json_encode(array_values($result['method'])), ]; } /** * Returns the classes / methods with insufficient coverage. */ - protected function insufficientCoverage(array $classes, string $baseLink): array + private function insufficientCoverage(array $classes, string $baseLink): array { $leastTestedClasses = []; $leastTestedMethods = []; @@ -188,24 +204,24 @@ protected function insufficientCoverage(array $classes, string $baseLink): array } } - \asort($leastTestedClasses); - \asort($leastTestedMethods); + asort($leastTestedClasses); + asort($leastTestedMethods); foreach ($leastTestedClasses as $className => $coverage) { - $result['class'] .= \sprintf( + $result['class'] .= sprintf( ' %s%d%%' . "\n", - \str_replace($baseLink, '', $classes[$className]['link']), + str_replace($baseLink, '', $classes[$className]['link']), $className, $coverage ); } foreach ($leastTestedMethods as $methodName => $coverage) { - [$class, $method] = \explode('::', $methodName); + [$class, $method] = explode('::', $methodName); - $result['method'] .= \sprintf( + $result['method'] .= sprintf( ' %s%d%%' . "\n", - \str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), + str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), $methodName, $method, $coverage @@ -218,7 +234,7 @@ protected function insufficientCoverage(array $classes, string $baseLink): array /** * Returns the project risks according to the CRAP index. */ - protected function projectRisks(array $classes, string $baseLink): array + private function projectRisks(array $classes, string $baseLink): array { $classRisks = []; $methodRisks = []; @@ -238,29 +254,29 @@ protected function projectRisks(array $classes, string $baseLink): array } if ($class['coverage'] < $this->highLowerBound && - $class['ccn'] > \count($class['methods'])) { + $class['ccn'] > count($class['methods'])) { $classRisks[$className] = $class['crap']; } } - \arsort($classRisks); - \arsort($methodRisks); + arsort($classRisks); + arsort($methodRisks); foreach ($classRisks as $className => $crap) { - $result['class'] .= \sprintf( + $result['class'] .= sprintf( ' %s%d' . "\n", - \str_replace($baseLink, '', $classes[$className]['link']), + str_replace($baseLink, '', $classes[$className]['link']), $className, $crap ); } foreach ($methodRisks as $methodName => $crap) { - [$class, $method] = \explode('::', $methodName); + [$class, $method] = explode('::', $methodName); - $result['method'] .= \sprintf( + $result['method'] .= sprintf( ' %s%d' . "\n", - \str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), + str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), $methodName, $method, $crap @@ -269,13 +285,4 @@ protected function projectRisks(array $classes, string $baseLink): array return $result; } - - protected function getActiveBreadcrumb(AbstractNode $node): string - { - return \sprintf( - ' ' . "\n" . - ' ' . "\n", - $node->getName() - ); - } } diff --git a/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php b/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php index c2f086079..faacbc31d 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php +++ b/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php @@ -1,6 +1,6 @@ * @@ -9,37 +9,38 @@ */ namespace SebastianBergmann\CodeCoverage\Report\Html; +use function count; +use function sprintf; +use function str_repeat; use SebastianBergmann\CodeCoverage\Node\AbstractNode as Node; use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +use SebastianBergmann\Template\Template; /** - * Renders a directory node. + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ final class Directory extends Renderer { - /** - * @throws \InvalidArgumentException - * @throws \RuntimeException - */ public function render(DirectoryNode $node, string $file): void { - $template = new \Text_Template($this->templatePath . 'directory.html', '{{', '}}'); + $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'directory_branch.html' : 'directory.html'); + $template = new Template($templateName, '{{', '}}'); $this->setCommonTemplateVariables($template, $node); $items = $this->renderItem($node, true); - foreach ($node->getDirectories() as $item) { + foreach ($node->directories() as $item) { $items .= $this->renderItem($item); } - foreach ($node->getFiles() as $item) { + foreach ($node->files() as $item) { $items .= $this->renderItem($item); } $template->setVar( [ - 'id' => $node->getId(), + 'id' => $node->id(), 'items' => $items, ] ); @@ -47,51 +48,65 @@ public function render(DirectoryNode $node, string $file): void $template->renderTo($file); } - protected function renderItem(Node $node, bool $total = false): string + private function renderItem(Node $node, bool $total = false): string { $data = [ - 'numClasses' => $node->getNumClassesAndTraits(), - 'numTestedClasses' => $node->getNumTestedClassesAndTraits(), - 'numMethods' => $node->getNumFunctionsAndMethods(), - 'numTestedMethods' => $node->getNumTestedFunctionsAndMethods(), - 'linesExecutedPercent' => $node->getLineExecutedPercent(false), - 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(), - 'numExecutedLines' => $node->getNumExecutedLines(), - 'numExecutableLines' => $node->getNumExecutableLines(), - 'testedMethodsPercent' => $node->getTestedFunctionsAndMethodsPercent(false), - 'testedMethodsPercentAsString' => $node->getTestedFunctionsAndMethodsPercent(), - 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(false), - 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(), + 'numClasses' => $node->numberOfClassesAndTraits(), + 'numTestedClasses' => $node->numberOfTestedClassesAndTraits(), + 'numMethods' => $node->numberOfFunctionsAndMethods(), + 'numTestedMethods' => $node->numberOfTestedFunctionsAndMethods(), + 'linesExecutedPercent' => $node->percentageOfExecutedLines()->asFloat(), + 'linesExecutedPercentAsString' => $node->percentageOfExecutedLines()->asString(), + 'numExecutedLines' => $node->numberOfExecutedLines(), + 'numExecutableLines' => $node->numberOfExecutableLines(), + 'branchesExecutedPercent' => $node->percentageOfExecutedBranches()->asFloat(), + 'branchesExecutedPercentAsString' => $node->percentageOfExecutedBranches()->asString(), + 'numExecutedBranches' => $node->numberOfExecutedBranches(), + 'numExecutableBranches' => $node->numberOfExecutableBranches(), + 'pathsExecutedPercent' => $node->percentageOfExecutedPaths()->asFloat(), + 'pathsExecutedPercentAsString' => $node->percentageOfExecutedPaths()->asString(), + 'numExecutedPaths' => $node->numberOfExecutedPaths(), + 'numExecutablePaths' => $node->numberOfExecutablePaths(), + 'testedMethodsPercent' => $node->percentageOfTestedFunctionsAndMethods()->asFloat(), + 'testedMethodsPercentAsString' => $node->percentageOfTestedFunctionsAndMethods()->asString(), + 'testedClassesPercent' => $node->percentageOfTestedClassesAndTraits()->asFloat(), + 'testedClassesPercentAsString' => $node->percentageOfTestedClassesAndTraits()->asString(), ]; if ($total) { $data['name'] = 'Total'; } else { + $up = str_repeat('../', count($node->pathAsArray()) - 2); + $data['icon'] = sprintf('', $up); + if ($node instanceof DirectoryNode) { - $data['name'] = \sprintf( + $data['name'] = sprintf( '%s', - $node->getName(), - $node->getName() + $node->name(), + $node->name() + ); + $data['icon'] = sprintf('', $up); + } elseif ($this->hasBranchCoverage) { + $data['name'] = sprintf( + '%s [line] [branch] [path]', + $node->name(), + $node->name(), + $node->name(), + $node->name() ); - - $up = \str_repeat('../', \count($node->getPathAsArray()) - 2); - - $data['icon'] = \sprintf('', $up); } else { - $data['name'] = \sprintf( + $data['name'] = sprintf( '%s', - $node->getName(), - $node->getName() + $node->name(), + $node->name() ); - - $up = \str_repeat('../', \count($node->getPathAsArray()) - 2); - - $data['icon'] = \sprintf('', $up); } } + $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'directory_item_branch.html' : 'directory_item.html'); + return $this->renderItemTemplate( - new \Text_Template($this->templatePath . 'directory_item.html', '{{', '}}'), + new Template($templateName, '{{', '}}'), $data ); } diff --git a/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/File.php b/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/File.php index f0604bf21..88dbfa669 100644 --- a/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/File.php +++ b/app/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/File.php @@ -1,6 +1,6 @@ * @@ -9,44 +9,166 @@ */ namespace SebastianBergmann\CodeCoverage\Report\Html; +use const ENT_COMPAT; +use const ENT_HTML401; +use const ENT_SUBSTITUTE; +use const T_ABSTRACT; +use const T_ARRAY; +use const T_AS; +use const T_BREAK; +use const T_CALLABLE; +use const T_CASE; +use const T_CATCH; +use const T_CLASS; +use const T_CLONE; +use const T_COMMENT; +use const T_CONST; +use const T_CONTINUE; +use const T_DECLARE; +use const T_DEFAULT; +use const T_DO; +use const T_DOC_COMMENT; +use const T_ECHO; +use const T_ELSE; +use const T_ELSEIF; +use const T_EMPTY; +use const T_ENDDECLARE; +use const T_ENDFOR; +use const T_ENDFOREACH; +use const T_ENDIF; +use const T_ENDSWITCH; +use const T_ENDWHILE; +use const T_EVAL; +use const T_EXIT; +use const T_EXTENDS; +use const T_FINAL; +use const T_FINALLY; +use const T_FOR; +use const T_FOREACH; +use const T_FUNCTION; +use const T_GLOBAL; +use const T_GOTO; +use const T_HALT_COMPILER; +use const T_IF; +use const T_IMPLEMENTS; +use const T_INCLUDE; +use const T_INCLUDE_ONCE; +use const T_INLINE_HTML; +use const T_INSTANCEOF; +use const T_INSTEADOF; +use const T_INTERFACE; +use const T_ISSET; +use const T_LIST; +use const T_NAMESPACE; +use const T_NEW; +use const T_PRINT; +use const T_PRIVATE; +use const T_PROTECTED; +use const T_PUBLIC; +use const T_REQUIRE; +use const T_REQUIRE_ONCE; +use const T_RETURN; +use const T_STATIC; +use const T_SWITCH; +use const T_THROW; +use const T_TRAIT; +use const T_TRY; +use const T_UNSET; +use const T_USE; +use const T_VAR; +use const T_WHILE; +use const T_YIELD; +use const T_YIELD_FROM; +use function array_key_exists; +use function array_pop; +use function array_unique; +use function constant; +use function count; +use function defined; +use function explode; +use function file_get_contents; +use function htmlspecialchars; +use function is_string; +use function sprintf; +use function str_replace; +use function substr; +use function token_get_all; +use function trim; +use PHPUnit\Runner\BaseTestRunner; use SebastianBergmann\CodeCoverage\Node\File as FileNode; -use SebastianBergmann\CodeCoverage\Util; +use SebastianBergmann\CodeCoverage\Percentage; +use SebastianBergmann\Template\Template; /** - * Renders a file node. + * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage */ final class File extends Renderer { /** - * @var int + * @psalm-var array */ - private $htmlSpecialCharsFlags = \ENT_COMPAT | \ENT_HTML401 | \ENT_SUBSTITUTE; + private static $keywordTokens = []; /** - * @throws \RuntimeException + * @var array + */ + private static $formattedSourceCache = []; + + /** + * @var int */ + private $htmlSpecialCharsFlags = ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE; + public function render(FileNode $node, string $file): void { - $template = new \Text_Template($this->templatePath . 'file.html', '{{', '}}'); + $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'file_branch.html' : 'file.html'); + $template = new Template($templateName, '{{', '}}'); + $this->setCommonTemplateVariables($template, $node); $template->setVar( [ - 'items' => $this->renderItems($node), - 'lines' => $this->renderSource($node), + 'items' => $this->renderItems($node), + 'lines' => $this->renderSourceWithLineCoverage($node), + 'legend' => '

ExecutedNot ExecutedDead Code

', + 'structure' => '', ] ); - $this->setCommonTemplateVariables($template, $node); + $template->renderTo($file . '.html'); + + if ($this->hasBranchCoverage) { + $template->setVar( + [ + 'items' => $this->renderItems($node), + 'lines' => $this->renderSourceWithBranchCoverage($node), + 'legend' => '

Fully coveredPartially coveredNot covered

', + 'structure' => $this->renderBranchStructure($node), + ] + ); - $template->renderTo($file); + $template->renderTo($file . '_branch.html'); + + $template->setVar( + [ + 'items' => $this->renderItems($node), + 'lines' => $this->renderSourceWithPathCoverage($node), + 'legend' => '

Fully coveredPartially coveredNot covered

', + 'structure' => $this->renderPathStructure($node), + ] + ); + + $template->renderTo($file . '_path.html'); + } } - protected function renderItems(FileNode $node): string + private function renderItems(FileNode $node): string { - $template = new \Text_Template($this->templatePath . 'file_item.html', '{{', '}}'); + $templateName = $this->templatePath . ($this->hasBranchCoverage ? 'file_item_branch.html' : 'file_item.html'); + $template = new Template($templateName, '{{', '}}'); - $methodItemTemplate = new \Text_Template( - $this->templatePath . 'method_item.html', + $methodTemplateName = $this->templatePath . ($this->hasBranchCoverage ? 'method_item_branch.html' : 'method_item.html'); + $methodItemTemplate = new Template( + $methodTemplateName, '{{', '}}' ); @@ -54,36 +176,44 @@ protected function renderItems(FileNode $node): string $items = $this->renderItemTemplate( $template, [ - 'name' => 'Total', - 'numClasses' => $node->getNumClassesAndTraits(), - 'numTestedClasses' => $node->getNumTestedClassesAndTraits(), - 'numMethods' => $node->getNumFunctionsAndMethods(), - 'numTestedMethods' => $node->getNumTestedFunctionsAndMethods(), - 'linesExecutedPercent' => $node->getLineExecutedPercent(false), - 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(), - 'numExecutedLines' => $node->getNumExecutedLines(), - 'numExecutableLines' => $node->getNumExecutableLines(), - 'testedMethodsPercent' => $node->getTestedFunctionsAndMethodsPercent(false), - 'testedMethodsPercentAsString' => $node->getTestedFunctionsAndMethodsPercent(), - 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(false), - 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(), - 'crap' => 'CRAP', + 'name' => 'Total', + 'numClasses' => $node->numberOfClassesAndTraits(), + 'numTestedClasses' => $node->numberOfTestedClassesAndTraits(), + 'numMethods' => $node->numberOfFunctionsAndMethods(), + 'numTestedMethods' => $node->numberOfTestedFunctionsAndMethods(), + 'linesExecutedPercent' => $node->percentageOfExecutedLines()->asFloat(), + 'linesExecutedPercentAsString' => $node->percentageOfExecutedLines()->asString(), + 'numExecutedLines' => $node->numberOfExecutedLines(), + 'numExecutableLines' => $node->numberOfExecutableLines(), + 'branchesExecutedPercent' => $node->percentageOfExecutedBranches()->asFloat(), + 'branchesExecutedPercentAsString' => $node->percentageOfExecutedBranches()->asString(), + 'numExecutedBranches' => $node->numberOfExecutedBranches(), + 'numExecutableBranches' => $node->numberOfExecutableBranches(), + 'pathsExecutedPercent' => $node->percentageOfExecutedPaths()->asFloat(), + 'pathsExecutedPercentAsString' => $node->percentageOfExecutedPaths()->asString(), + 'numExecutedPaths' => $node->numberOfExecutedPaths(), + 'numExecutablePaths' => $node->numberOfExecutablePaths(), + 'testedMethodsPercent' => $node->percentageOfTestedFunctionsAndMethods()->asFloat(), + 'testedMethodsPercentAsString' => $node->percentageOfTestedFunctionsAndMethods()->asString(), + 'testedClassesPercent' => $node->percentageOfTestedClassesAndTraits()->asFloat(), + 'testedClassesPercentAsString' => $node->percentageOfTestedClassesAndTraits()->asString(), + 'crap' => 'CRAP', ] ); $items .= $this->renderFunctionItems( - $node->getFunctions(), + $node->functions(), $methodItemTemplate ); $items .= $this->renderTraitOrClassItems( - $node->getTraits(), + $node->traits(), $template, $methodItemTemplate ); $items .= $this->renderTraitOrClassItems( - $node->getClasses(), + $node->classes(), $template, $methodItemTemplate ); @@ -91,7 +221,7 @@ protected function renderItems(FileNode $node): string return $items; } - protected function renderTraitOrClassItems(array $items, \Text_Template $template, \Text_Template $methodItemTemplate): string + private function renderTraitOrClassItems(array $items, Template $template, Template $methodItemTemplate): string { $buffer = ''; @@ -115,52 +245,70 @@ protected function renderTraitOrClassItems(array $items, \Text_Template $templat if ($item['executableLines'] > 0) { $numClasses = 1; - $numTestedClasses = $numTestedMethods == $numMethods ? 1 : 0; - $linesExecutedPercentAsString = Util::percent( + $numTestedClasses = $numTestedMethods === $numMethods ? 1 : 0; + $linesExecutedPercentAsString = Percentage::fromFractionAndTotal( $item['executedLines'], - $item['executableLines'], - true - ); + $item['executableLines'] + )->asString(); + $branchesExecutedPercentAsString = Percentage::fromFractionAndTotal( + $item['executedBranches'], + $item['executableBranches'] + )->asString(); + $pathsExecutedPercentAsString = Percentage::fromFractionAndTotal( + $item['executedPaths'], + $item['executablePaths'] + )->asString(); } else { - $numClasses = 'n/a'; - $numTestedClasses = 'n/a'; - $linesExecutedPercentAsString = 'n/a'; + $numClasses = 0; + $numTestedClasses = 0; + $linesExecutedPercentAsString = 'n/a'; + $branchesExecutedPercentAsString = 'n/a'; + $pathsExecutedPercentAsString = 'n/a'; } + $testedMethodsPercentage = Percentage::fromFractionAndTotal( + $numTestedMethods, + $numMethods + ); + + $testedClassesPercentage = Percentage::fromFractionAndTotal( + $numTestedMethods === $numMethods ? 1 : 0, + 1 + ); + $buffer .= $this->renderItemTemplate( $template, [ - 'name' => $this->abbreviateClassName($name), - 'numClasses' => $numClasses, - 'numTestedClasses' => $numTestedClasses, - 'numMethods' => $numMethods, - 'numTestedMethods' => $numTestedMethods, - 'linesExecutedPercent' => Util::percent( + 'name' => $this->abbreviateClassName($name), + 'numClasses' => $numClasses, + 'numTestedClasses' => $numTestedClasses, + 'numMethods' => $numMethods, + 'numTestedMethods' => $numTestedMethods, + 'linesExecutedPercent' => Percentage::fromFractionAndTotal( $item['executedLines'], $item['executableLines'], - false - ), + )->asFloat(), 'linesExecutedPercentAsString' => $linesExecutedPercentAsString, 'numExecutedLines' => $item['executedLines'], 'numExecutableLines' => $item['executableLines'], - 'testedMethodsPercent' => Util::percent( - $numTestedMethods, - $numMethods - ), - 'testedMethodsPercentAsString' => Util::percent( - $numTestedMethods, - $numMethods, - true - ), - 'testedClassesPercent' => Util::percent( - $numTestedMethods == $numMethods ? 1 : 0, - 1 - ), - 'testedClassesPercentAsString' => Util::percent( - $numTestedMethods == $numMethods ? 1 : 0, - 1, - true - ), + 'branchesExecutedPercent' => Percentage::fromFractionAndTotal( + $item['executedBranches'], + $item['executableBranches'], + )->asFloat(), + 'branchesExecutedPercentAsString' => $branchesExecutedPercentAsString, + 'numExecutedBranches' => $item['executedBranches'], + 'numExecutableBranches' => $item['executableBranches'], + 'pathsExecutedPercent' => Percentage::fromFractionAndTotal( + $item['executedPaths'], + $item['executablePaths'] + )->asFloat(), + 'pathsExecutedPercentAsString' => $pathsExecutedPercentAsString, + 'numExecutedPaths' => $item['executedPaths'], + 'numExecutablePaths' => $item['executablePaths'], + 'testedMethodsPercent' => $testedMethodsPercentage->asFloat(), + 'testedMethodsPercentAsString' => $testedMethodsPercentage->asString(), + 'testedClassesPercent' => $testedClassesPercentage->asFloat(), + 'testedClassesPercentAsString' => $testedClassesPercentage->asString(), 'crap' => $item['crap'], ] ); @@ -177,7 +325,7 @@ protected function renderTraitOrClassItems(array $items, \Text_Template $templat return $buffer; } - protected function renderFunctionItems(array $functions, \Text_Template $template): string + private function renderFunctionItems(array $functions, Template $template): string { if (empty($functions)) { return ''; @@ -195,7 +343,7 @@ protected function renderFunctionItems(array $functions, \Text_Template $templat return $buffer; } - protected function renderFunctionOrMethodItem(\Text_Template $template, array $item, string $indent = ''): string + private function renderFunctionOrMethodItem(Template $template, array $item, string $indent = ''): string { $numMethods = 0; $numTestedMethods = 0; @@ -208,48 +356,65 @@ protected function renderFunctionOrMethodItem(\Text_Template $template, array $i } } + $executedLinesPercentage = Percentage::fromFractionAndTotal( + $item['executedLines'], + $item['executableLines'] + ); + + $executedBranchesPercentage = Percentage::fromFractionAndTotal( + $item['executedBranches'], + $item['executableBranches'] + ); + + $executedPathsPercentage = Percentage::fromFractionAndTotal( + $item['executedPaths'], + $item['executablePaths'] + ); + + $testedMethodsPercentage = Percentage::fromFractionAndTotal( + $numTestedMethods, + 1 + ); + return $this->renderItemTemplate( $template, [ - 'name' => \sprintf( + 'name' => sprintf( '%s%s', $indent, $item['startLine'], - \htmlspecialchars($item['signature'], $this->htmlSpecialCharsFlags), + htmlspecialchars($item['signature'], $this->htmlSpecialCharsFlags), $item['functionName'] ?? $item['methodName'] ), - 'numMethods' => $numMethods, - 'numTestedMethods' => $numTestedMethods, - 'linesExecutedPercent' => Util::percent( - $item['executedLines'], - $item['executableLines'] - ), - 'linesExecutedPercentAsString' => Util::percent( - $item['executedLines'], - $item['executableLines'], - true - ), - 'numExecutedLines' => $item['executedLines'], - 'numExecutableLines' => $item['executableLines'], - 'testedMethodsPercent' => Util::percent( - $numTestedMethods, - 1 - ), - 'testedMethodsPercentAsString' => Util::percent( - $numTestedMethods, - 1, - true - ), - 'crap' => $item['crap'], + 'numMethods' => $numMethods, + 'numTestedMethods' => $numTestedMethods, + 'linesExecutedPercent' => $executedLinesPercentage->asFloat(), + 'linesExecutedPercentAsString' => $executedLinesPercentage->asString(), + 'numExecutedLines' => $item['executedLines'], + 'numExecutableLines' => $item['executableLines'], + 'branchesExecutedPercent' => $executedBranchesPercentage->asFloat(), + 'branchesExecutedPercentAsString' => $executedBranchesPercentage->asString(), + 'numExecutedBranches' => $item['executedBranches'], + 'numExecutableBranches' => $item['executableBranches'], + 'pathsExecutedPercent' => $executedPathsPercentage->asFloat(), + 'pathsExecutedPercentAsString' => $executedPathsPercentage->asString(), + 'numExecutedPaths' => $item['executedPaths'], + 'numExecutablePaths' => $item['executablePaths'], + 'testedMethodsPercent' => $testedMethodsPercentage->asFloat(), + 'testedMethodsPercentAsString' => $testedMethodsPercentage->asString(), + 'crap' => $item['crap'], ] ); } - protected function renderSource(FileNode $node): string + private function renderSourceWithLineCoverage(FileNode $node): string { - $coverageData = $node->getCoverageData(); - $testData = $node->getTestData(); - $codeLines = $this->loadFile($node->getPath()); + $linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}'); + $singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}'); + + $coverageData = $node->lineCoverageData(); + $testData = $node->testData(); + $codeLines = $this->loadFile($node->pathAsString()); $lines = ''; $i = 1; @@ -258,136 +423,485 @@ protected function renderSource(FileNode $node): string $popoverContent = ''; $popoverTitle = ''; - if (\array_key_exists($i, $coverageData)) { - $numTests = ($coverageData[$i] ? \count($coverageData[$i]) : 0); + if (array_key_exists($i, $coverageData)) { + $numTests = ($coverageData[$i] ? count($coverageData[$i]) : 0); if ($coverageData[$i] === null) { - $trClass = ' class="warning"'; - } elseif ($numTests == 0) { - $trClass = ' class="danger"'; + $trClass = 'warning'; + } elseif ($numTests === 0) { + $trClass = 'danger'; } else { - $lineCss = 'covered-by-large-tests'; - $popoverContent = '
    '; - if ($numTests > 1) { $popoverTitle = $numTests . ' tests cover line ' . $i; } else { $popoverTitle = '1 test covers line ' . $i; } + $lineCss = 'covered-by-large-tests'; + $popoverContent = '
      '; + foreach ($coverageData[$i] as $test) { - if ($lineCss == 'covered-by-large-tests' && $testData[$test]['size'] == 'medium') { + if ($lineCss === 'covered-by-large-tests' && $testData[$test]['size'] === 'medium') { $lineCss = 'covered-by-medium-tests'; - } elseif ($testData[$test]['size'] == 'small') { + } elseif ($testData[$test]['size'] === 'small') { $lineCss = 'covered-by-small-tests'; } - switch ($testData[$test]['status']) { - case 0: - switch ($testData[$test]['size']) { - case 'small': - $testCSS = ' class="covered-by-small-tests"'; + $popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]); + } + + $popoverContent .= '
    '; + $trClass = $lineCss . ' popin'; + } + } + + $popover = ''; + + if (!empty($popoverTitle)) { + $popover = sprintf( + ' data-title="%s" data-content="%s" data-placement="top" data-html="true"', + $popoverTitle, + htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags) + ); + } + + $lines .= $this->renderLine($singleLineTemplate, $i, $line, $trClass, $popover); + + $i++; + } + + $linesTemplate->setVar(['lines' => $lines]); + + return $linesTemplate->render(); + } + + private function renderSourceWithBranchCoverage(FileNode $node): string + { + $linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}'); + $singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}'); + + $functionCoverageData = $node->functionCoverageData(); + $testData = $node->testData(); + $codeLines = $this->loadFile($node->pathAsString()); + + $lineData = []; + + /** @var int $line */ + foreach (array_keys($codeLines) as $line) { + $lineData[$line + 1] = [ + 'includedInBranches' => 0, + 'includedInHitBranches' => 0, + 'tests' => [], + ]; + } + + foreach ($functionCoverageData as $method) { + foreach ($method['branches'] as $branch) { + foreach (range($branch['line_start'], $branch['line_end']) as $line) { + if (!isset($lineData[$line])) { // blank line at end of file is sometimes included here + continue; + } - break; + $lineData[$line]['includedInBranches']++; - case 'medium': - $testCSS = ' class="covered-by-medium-tests"'; + if ($branch['hit']) { + $lineData[$line]['includedInHitBranches']++; + $lineData[$line]['tests'] = array_unique(array_merge($lineData[$line]['tests'], $branch['hit'])); + } + } + } + } - break; + $lines = ''; + $i = 1; - default: - $testCSS = ' class="covered-by-large-tests"'; + /** @var string $line */ + foreach ($codeLines as $line) { + $trClass = ''; + $popover = ''; - break; - } + if ($lineData[$i]['includedInBranches'] > 0) { + $lineCss = 'success'; + + if ($lineData[$i]['includedInHitBranches'] === 0) { + $lineCss = 'danger'; + } elseif ($lineData[$i]['includedInHitBranches'] !== $lineData[$i]['includedInBranches']) { + $lineCss = 'warning'; + } - break; + $popoverContent = '
      '; - case 1: - case 2: - $testCSS = ' class="warning"'; + if (count($lineData[$i]['tests']) === 1) { + $popoverTitle = '1 test covers line ' . $i; + } else { + $popoverTitle = count($lineData[$i]['tests']) . ' tests cover line ' . $i; + } + $popoverTitle .= '. These are covering ' . $lineData[$i]['includedInHitBranches'] . ' out of the ' . $lineData[$i]['includedInBranches'] . ' code branches.'; - break; + foreach ($lineData[$i]['tests'] as $test) { + $popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]); + } - case 3: - $testCSS = ' class="danger"'; + $popoverContent .= '
    '; + $trClass = $lineCss . ' popin'; - break; + $popover = sprintf( + ' data-title="%s" data-content="%s" data-placement="top" data-html="true"', + $popoverTitle, + htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags) + ); + } - case 4: - $testCSS = ' class="danger"'; + $lines .= $this->renderLine($singleLineTemplate, $i, $line, $trClass, $popover); - break; + $i++; + } - default: - $testCSS = ''; + $linesTemplate->setVar(['lines' => $lines]); + + return $linesTemplate->render(); + } + + private function renderSourceWithPathCoverage(FileNode $node): string + { + $linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}'); + $singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}'); + + $functionCoverageData = $node->functionCoverageData(); + $testData = $node->testData(); + $codeLines = $this->loadFile($node->pathAsString()); + + $lineData = []; + + /** @var int $line */ + foreach (array_keys($codeLines) as $line) { + $lineData[$line + 1] = [ + 'includedInPaths' => [], + 'includedInHitPaths' => [], + 'tests' => [], + ]; + } + + foreach ($functionCoverageData as $method) { + foreach ($method['paths'] as $pathId => $path) { + foreach ($path['path'] as $branchTaken) { + foreach (range($method['branches'][$branchTaken]['line_start'], $method['branches'][$branchTaken]['line_end']) as $line) { + if (!isset($lineData[$line])) { + continue; } + $lineData[$line]['includedInPaths'][] = $pathId; - $popoverContent .= \sprintf( - '%s', - $testCSS, - \htmlspecialchars($test, $this->htmlSpecialCharsFlags) - ); + if ($path['hit']) { + $lineData[$line]['includedInHitPaths'][] = $pathId; + $lineData[$line]['tests'] = array_unique(array_merge($lineData[$line]['tests'], $path['hit'])); + } } + } + } + } - $popoverContent .= '
'; - $trClass = ' class="' . $lineCss . ' popin"'; + $lines = ''; + $i = 1; + + /** @var string $line */ + foreach ($codeLines as $line) { + $trClass = ''; + $popover = ''; + $includedInPathsCount = count(array_unique($lineData[$i]['includedInPaths'])); + $includedInHitPathsCount = count(array_unique($lineData[$i]['includedInHitPaths'])); + + if ($includedInPathsCount > 0) { + $lineCss = 'success'; + + if ($includedInHitPathsCount === 0) { + $lineCss = 'danger'; + } elseif ($includedInHitPathsCount !== $includedInPathsCount) { + $lineCss = 'warning'; + } + + $popoverContent = '
    '; + + if (count($lineData[$i]['tests']) === 1) { + $popoverTitle = '1 test covers line ' . $i; + } else { + $popoverTitle = count($lineData[$i]['tests']) . ' tests cover line ' . $i; + } + $popoverTitle .= '. These are covering ' . $includedInHitPathsCount . ' out of the ' . $includedInPathsCount . ' code paths.'; + + foreach ($lineData[$i]['tests'] as $test) { + $popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]); + } + + $popoverContent .= '
'; + $trClass = $lineCss . ' popin'; + + $popover = sprintf( + ' data-title="%s" data-content="%s" data-placement="top" data-html="true"', + $popoverTitle, + htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags) + ); + } + + $lines .= $this->renderLine($singleLineTemplate, $i, $line, $trClass, $popover); + + $i++; + } + + $linesTemplate->setVar(['lines' => $lines]); + + return $linesTemplate->render(); + } + + private function renderBranchStructure(FileNode $node): string + { + $branchesTemplate = new Template($this->templatePath . 'branches.html.dist', '{{', '}}'); + + $coverageData = $node->functionCoverageData(); + $testData = $node->testData(); + $codeLines = $this->loadFile($node->pathAsString()); + $branches = ''; + + ksort($coverageData); + + foreach ($coverageData as $methodName => $methodData) { + if (!$methodData['branches']) { + continue; + } + + $branchStructure = ''; + + foreach ($methodData['branches'] as $branch) { + $branchStructure .= $this->renderBranchLines($branch, $codeLines, $testData); + } + + if ($branchStructure !== '') { // don't show empty branches + $branches .= '
' . $this->abbreviateMethodName($methodName) . '
' . "\n"; + $branches .= $branchStructure; + } + } + + $branchesTemplate->setVar(['branches' => $branches]); + + return $branchesTemplate->render(); + } + + private function renderBranchLines(array $branch, array $codeLines, array $testData): string + { + $linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}'); + $singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}'); + + $lines = ''; + + $branchLines = range($branch['line_start'], $branch['line_end']); + sort($branchLines); // sometimes end_line < start_line + + /** @var int $line */ + foreach ($branchLines as $line) { + if (!isset($codeLines[$line])) { // blank line at end of file is sometimes included here + continue; + } + + $popoverContent = ''; + $popoverTitle = ''; + + $numTests = count($branch['hit']); + + if ($numTests === 0) { + $trClass = 'danger'; + } else { + $lineCss = 'covered-by-large-tests'; + $popoverContent = '