1
0
Fork 0

Update to composer/pcre 2.1 (#11189)

pull/11193/head
Jordi Boggiano 2022-11-17 11:34:54 +01:00 committed by GitHub
parent 5f1ec99e4a
commit bd6a5019b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 203 additions and 181 deletions

View File

@ -38,7 +38,7 @@
"symfony/finder": "^5.4 || ^6.0", "symfony/finder": "^5.4 || ^6.0",
"symfony/process": "^5.4 || ^6.0", "symfony/process": "^5.4 || ^6.0",
"react/promise": "^2.8", "react/promise": "^2.8",
"composer/pcre": "^2 || ^3", "composer/pcre": "^2.1 || ^3.1",
"symfony/polyfill-php73": "^1.24", "symfony/polyfill-php73": "^1.24",
"symfony/polyfill-php80": "^1.24", "symfony/polyfill-php80": "^1.24",
"symfony/polyfill-php81": "^1.24", "symfony/polyfill-php81": "^1.24",

138
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "2dc8cd355eee4bb6c9fb0d2e10ffb7c6", "content-hash": "63b34757131aff5b81d2ed8ce5933fe4",
"packages": [ "packages": [
{ {
"name": "composer/ca-bundle", "name": "composer/ca-bundle",
@ -226,16 +226,16 @@
}, },
{ {
"name": "composer/pcre", "name": "composer/pcre",
"version": "2.0.1", "version": "2.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/composer/pcre.git", "url": "https://github.com/composer/pcre.git",
"reference": "562ca94029b37bb04205e2abb43be5550d2945d7" "reference": "3fdb2807b31a78a40ad89570e30ec77466c98717"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/562ca94029b37bb04205e2abb43be5550d2945d7", "url": "https://api.github.com/repos/composer/pcre/zipball/3fdb2807b31a78a40ad89570e30ec77466c98717",
"reference": "562ca94029b37bb04205e2abb43be5550d2945d7", "reference": "3fdb2807b31a78a40ad89570e30ec77466c98717",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -277,7 +277,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/composer/pcre/issues", "issues": "https://github.com/composer/pcre/issues",
"source": "https://github.com/composer/pcre/tree/2.0.1" "source": "https://github.com/composer/pcre/tree/2.1.0"
}, },
"funding": [ "funding": [
{ {
@ -293,7 +293,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-03T15:56:01+00:00" "time": "2022-11-16T18:32:04+00:00"
}, },
{ {
"name": "composer/semver", "name": "composer/semver",
@ -1234,16 +1234,16 @@
}, },
{ {
"name": "symfony/polyfill-ctype", "name": "symfony/polyfill-ctype",
"version": "v1.26.0", "version": "v1.27.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git", "url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" "reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", "reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1258,7 +1258,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -1296,7 +1296,7 @@
"portable" "portable"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -1312,20 +1312,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-05-24T11:49:31+00:00" "time": "2022-11-03T14:55:06+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-grapheme", "name": "symfony/polyfill-intl-grapheme",
"version": "v1.26.0", "version": "v1.27.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "433d05519ce6990bf3530fba6957499d327395c2" "reference": "511a08c03c1960e08a883f4cffcacd219b758354"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354",
"reference": "433d05519ce6990bf3530fba6957499d327395c2", "reference": "511a08c03c1960e08a883f4cffcacd219b758354",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1337,7 +1337,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -1377,7 +1377,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -1393,20 +1393,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-05-24T11:49:31+00:00" "time": "2022-11-03T14:55:06+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-normalizer", "name": "symfony/polyfill-intl-normalizer",
"version": "v1.26.0", "version": "v1.27.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "219aa369ceff116e673852dce47c3a41794c14bd" "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"reference": "219aa369ceff116e673852dce47c3a41794c14bd", "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1418,7 +1418,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -1461,7 +1461,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -1477,20 +1477,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-05-24T11:49:31+00:00" "time": "2022-11-03T14:55:06+00:00"
}, },
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
"version": "v1.26.0", "version": "v1.27.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git", "url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1505,7 +1505,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -1544,7 +1544,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -1560,20 +1560,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-05-24T11:49:31+00:00" "time": "2022-11-03T14:55:06+00:00"
}, },
{ {
"name": "symfony/polyfill-php73", "name": "symfony/polyfill-php73",
"version": "v1.26.0", "version": "v1.27.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php73.git", "url": "https://github.com/symfony/polyfill-php73.git",
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85" "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85", "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9",
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85", "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1582,7 +1582,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -1623,7 +1623,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -1639,20 +1639,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-05-24T11:49:31+00:00" "time": "2022-11-03T14:55:06+00:00"
}, },
{ {
"name": "symfony/polyfill-php80", "name": "symfony/polyfill-php80",
"version": "v1.26.0", "version": "v1.27.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php80.git", "url": "https://github.com/symfony/polyfill-php80.git",
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1661,7 +1661,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -1706,7 +1706,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -1722,20 +1722,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-05-10T07:21:04+00:00" "time": "2022-11-03T14:55:06+00:00"
}, },
{ {
"name": "symfony/polyfill-php81", "name": "symfony/polyfill-php81",
"version": "v1.26.0", "version": "v1.27.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php81.git", "url": "https://github.com/symfony/polyfill-php81.git",
"reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1" "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1", "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a",
"reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1", "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1744,7 +1744,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -1785,7 +1785,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -1801,7 +1801,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-05-24T11:49:31+00:00" "time": "2022-11-03T14:55:06+00:00"
}, },
{ {
"name": "symfony/process", "name": "symfony/process",
@ -2038,16 +2038,16 @@
"packages-dev": [ "packages-dev": [
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.9.0", "version": "1.9.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "e08de53a5eec983de78a787a88e72518cf8fe43a" "reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/e08de53a5eec983de78a787a88e72518cf8fe43a", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d6fdf01c53978b6429f1393ba4afeca39cc68afa",
"reference": "e08de53a5eec983de78a787a88e72518cf8fe43a", "reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2077,7 +2077,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan/issues", "issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.9.0" "source": "https://github.com/phpstan/phpstan/tree/1.9.2"
}, },
"funding": [ "funding": [
{ {
@ -2093,7 +2093,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-03T07:26:48+00:00" "time": "2022-11-10T09:56:11+00:00"
}, },
{ {
"name": "phpstan/phpstan-deprecation-rules", "name": "phpstan/phpstan-deprecation-rules",
@ -2247,22 +2247,22 @@
}, },
{ {
"name": "phpstan/phpstan-symfony", "name": "phpstan/phpstan-symfony",
"version": "1.2.15", "version": "1.2.16",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan-symfony.git", "url": "https://github.com/phpstan/phpstan-symfony.git",
"reference": "7210072b7fd83bf62eb5f889d4da70b80c07f04c" "reference": "d6ea16206b1b645ded5b43736d8ef5ae1168eb55"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/7210072b7fd83bf62eb5f889d4da70b80c07f04c", "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/d6ea16206b1b645ded5b43736d8ef5ae1168eb55",
"reference": "7210072b7fd83bf62eb5f889d4da70b80c07f04c", "reference": "d6ea16206b1b645ded5b43736d8ef5ae1168eb55",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-simplexml": "*", "ext-simplexml": "*",
"php": "^7.2 || ^8.0", "php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.8.2" "phpstan/phpstan": "^1.9.1"
}, },
"conflict": { "conflict": {
"symfony/framework-bundle": "<3.0" "symfony/framework-bundle": "<3.0"
@ -2312,9 +2312,9 @@
"description": "Symfony Framework extensions and rules for PHPStan", "description": "Symfony Framework extensions and rules for PHPStan",
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan-symfony/issues", "issues": "https://github.com/phpstan/phpstan-symfony/issues",
"source": "https://github.com/phpstan/phpstan-symfony/tree/1.2.15" "source": "https://github.com/phpstan/phpstan-symfony/tree/1.2.16"
}, },
"time": "2022-11-03T15:03:30+00:00" "time": "2022-11-04T13:16:15+00:00"
}, },
{ {
"name": "symfony/phpunit-bridge", "name": "symfony/phpunit-bridge",

View File

@ -664,7 +664,7 @@ EOT
} }
// handle repositories // handle repositories
if (Preg::isMatch('/^repos?(?:itories)?\.(.+)/', $settingKey, $matches)) { if (Preg::isMatchStrictGroups('/^repos?(?:itories)?\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) { if ($input->getOption('unset')) {
$this->configSource->removeRepository($matches[1]); $this->configSource->removeRepository($matches[1]);

View File

@ -375,7 +375,7 @@ EOT
if (null === $stability) { if (null === $stability) {
if (null === $packageVersion) { if (null === $packageVersion) {
$stability = 'stable'; $stability = 'stable';
} elseif (Preg::isMatch('{^[^,\s]*?@('.implode('|', array_keys(BasePackage::$stabilities)).')$}i', $packageVersion, $match)) { } elseif (Preg::isMatchStrictGroups('{^[^,\s]*?@('.implode('|', array_keys(BasePackage::$stabilities)).')$}i', $packageVersion, $match)) {
$stability = $match[1]; $stability = $match[1];
} else { } else {
$stability = VersionParser::parseStability($packageVersion); $stability = VersionParser::parseStability($packageVersion);

View File

@ -163,7 +163,7 @@ EOT
$platformRepo = new PlatformRepository([], $platformOverrides); $platformRepo = new PlatformRepository([], $platformOverrides);
$phpPkg = $platformRepo->findPackage('php', '*'); $phpPkg = $platformRepo->findPackage('php', '*');
$phpVersion = $phpPkg->getPrettyVersion(); $phpVersion = $phpPkg->getPrettyVersion();
if ($phpPkg instanceof CompletePackageInterface && false !== strpos($phpPkg->getDescription(), 'overridden')) { if ($phpPkg instanceof CompletePackageInterface && str_contains((string) $phpPkg->getDescription(), 'overridden')) {
$phpVersion .= ' - ' . $phpPkg->getDescription(); $phpVersion .= ' - ' . $phpPkg->getDescription();
} }
@ -573,14 +573,14 @@ EOT
ob_start(); ob_start();
phpinfo(INFO_GENERAL); phpinfo(INFO_GENERAL);
$phpinfo = ob_get_clean(); $phpinfo = ob_get_clean();
if (Preg::isMatch('{Configure Command(?: *</td><td class="v">| *=> *)(.*?)(?:</td>|$)}m', $phpinfo, $match)) { if (is_string($phpinfo) && Preg::isMatchStrictGroups('{Configure Command(?: *</td><td class="v">| *=> *)(.*?)(?:</td>|$)}m', $phpinfo, $match)) {
$configure = $match[1]; $configure = $match[1];
if (false !== strpos($configure, '--enable-sigchild')) { if (str_contains($configure, '--enable-sigchild')) {
$warnings['sigchild'] = true; $warnings['sigchild'] = true;
} }
if (false !== strpos($configure, '--with-curlwrappers')) { if (str_contains($configure, '--with-curlwrappers')) {
$warnings['curlwrappers'] = true; $warnings['curlwrappers'] = true;
} }
} }

View File

@ -465,14 +465,15 @@ EOT
private function parseAuthorString(string $author): array private function parseAuthorString(string $author): array
{ {
if (Preg::isMatch('/^(?P<name>[- .,\p{L}\p{N}\p{Mn}\'"()]+)(?:\s+<(?P<email>.+?)>)?$/u', $author, $match)) { if (Preg::isMatch('/^(?P<name>[- .,\p{L}\p{N}\p{Mn}\'"()]+)(?:\s+<(?P<email>.+?)>)?$/u', $author, $match)) {
$hasEmail = isset($match['email']) && '' !== $match['email']; assert(is_string($match['name']));
if ($hasEmail && !$this->isValidEmail($match['email'])) {
if (null !== $match['email'] && !$this->isValidEmail($match['email'])) {
throw new \InvalidArgumentException('Invalid email "'.$match['email'].'"'); throw new \InvalidArgumentException('Invalid email "'.$match['email'].'"');
} }
return [ return [
'name' => trim($match['name']), 'name' => trim($match['name']),
'email' => $hasEmail ? $match['email'] : null, 'email' => $match['email'],
]; ];
} }
@ -536,7 +537,7 @@ EOT
if ($cmd->isSuccessful()) { if ($cmd->isSuccessful()) {
$this->gitConfig = []; $this->gitConfig = [];
Preg::matchAll('{^([^=]+)=(.*)$}m', $cmd->getOutput(), $matches); Preg::matchAllStrictGroups('{^([^=]+)=(.*)$}m', $cmd->getOutput(), $matches);
foreach ($matches[1] as $key => $match) { foreach ($matches[1] as $key => $match) {
$this->gitConfig[$match] = $matches[2][$key]; $this->gitConfig[$match] = $matches[2][$key];
} }

View File

@ -1369,7 +1369,7 @@ EOT
if ($targetVersion === null) { if ($targetVersion === null) {
if ($majorOnly && Preg::isMatch('{^(?P<zero_major>(?:0\.)+)?(?P<first_meaningful>\d+)\.}', $package->getVersion(), $match)) { if ($majorOnly && Preg::isMatch('{^(?P<zero_major>(?:0\.)+)?(?P<first_meaningful>\d+)\.}', $package->getVersion(), $match)) {
$targetVersion = '>='.$match['zero_major'].($match['first_meaningful'] + 1).',<9999999-dev'; $targetVersion = '>='.$match['zero_major'].(((int) $match['first_meaningful']) + 1).',<9999999-dev';
} }
if ($minorOnly) { if ($minorOnly) {

View File

@ -114,7 +114,7 @@ class Problem
$deduplicatableRuleTypes = [Rule::RULE_PACKAGE_REQUIRES, Rule::RULE_PACKAGE_CONFLICT]; $deduplicatableRuleTypes = [Rule::RULE_PACKAGE_REQUIRES, Rule::RULE_PACKAGE_CONFLICT];
foreach ($rules as $rule) { foreach ($rules as $rule) {
$message = $rule->getPrettyString($repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool); $message = $rule->getPrettyString($repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool);
if (in_array($rule->getReason(), $deduplicatableRuleTypes, true) && Preg::isMatch('{^(?P<package>\S+) (?P<version>\S+) (?P<type>requires|conflicts)}', $message, $m)) { if (in_array($rule->getReason(), $deduplicatableRuleTypes, true) && Preg::isMatchStrictGroups('{^(?P<package>\S+) (?P<version>\S+) (?P<type>requires|conflicts)}', $message, $m)) {
$template = Preg::replace('{^\S+ \S+ }', '%s%s ', $message); $template = Preg::replace('{^\S+ \S+ }', '%s%s ', $message);
$messages[] = $template; $messages[] = $template;
$templates[$template][$m[1]][$parser->normalize($m[2])] = $m[2]; $templates[$template][$m[1]][$parser->normalize($m[2])] = $m[2];

View File

@ -249,13 +249,13 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
} }
$refs = trim($output); $refs = trim($output);
if (!Preg::isMatch('{^([a-f0-9]+) HEAD$}mi', $refs, $match)) { if (!Preg::isMatchStrictGroups('{^([a-f0-9]+) HEAD$}mi', $refs, $match)) {
// could not match the HEAD for some reason // could not match the HEAD for some reason
return null; return null;
} }
$headRef = $match[1]; $headRef = $match[1];
if (!Preg::isMatchAll('{^'.$headRef.' refs/heads/(.+)$}mi', $refs, $matches)) { if (!Preg::isMatchAllStrictGroups('{^'.$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 // not on a branch, we are either on a not-modified tag or some sort of detached head, so skip this
return null; return null;
} }
@ -272,7 +272,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
// try to find matching branch names in remote repos // try to find matching branch names in remote repos
foreach ($candidateBranches as $candidate) { foreach ($candidateBranches as $candidate) {
if (Preg::isMatchAll('{^[a-f0-9]+ refs/remotes/((?:[^/]+)/'.preg_quote($candidate).')$}mi', $refs, $matches)) { if (Preg::isMatchAllStrictGroups('{^[a-f0-9]+ refs/remotes/((?:[^/]+)/'.preg_quote($candidate).')$}mi', $refs, $matches)) {
foreach ($matches[1] as $match) { foreach ($matches[1] as $match) {
$branch = $candidate; $branch = $candidate;
$remoteBranches[] = $match; $remoteBranches[] = $match;
@ -284,7 +284,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
// if it doesn't exist, then we assume it is an unpushed branch // 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 // this is bad as we have no reference point to do a diff so we just bail listing
// the branch as being unpushed // the branch as being unpushed
if (!$remoteBranches) { if (count($remoteBranches) === 0) {
$unpushedChanges = 'Branch ' . $branch . ' could not be found on any remote and appears to be unpushed'; $unpushedChanges = 'Branch ' . $branch . ' could not be found on any remote and appears to be unpushed';
$branchNotFoundError = true; $branchNotFoundError = true;
} else { } else {

View File

@ -202,7 +202,7 @@ class SvnDownloader extends VcsDownloader
} }
$urlPattern = '#<url>(.*)</url>#'; $urlPattern = '#<url>(.*)</url>#';
if (Preg::isMatch($urlPattern, $output, $matches)) { if (Preg::isMatchStrictGroups($urlPattern, $output, $matches)) {
$baseUrl = $matches[1]; $baseUrl = $matches[1];
} else { } else {
throw new \RuntimeException( throw new \RuntimeException(

View File

@ -131,7 +131,7 @@ class ZipDownloader extends ArchiveDownloader
if (!$warned7ZipLinux && !Platform::isWindows() && in_array($executable, ['7z', '7zz'], true)) { if (!$warned7ZipLinux && !Platform::isWindows() && in_array($executable, ['7z', '7zz'], true)) {
$warned7ZipLinux = true; $warned7ZipLinux = true;
if (0 === $this->process->execute($executable, $output)) { if (0 === $this->process->execute($executable, $output)) {
if (Preg::isMatch('{^\s*7-Zip(?: \[64\])? ([0-9.]+)}', $output, $match) && version_compare($match[1], '21.01', '<')) { if (Preg::isMatchStrictGroups('{^\s*7-Zip(?: \[64\])? ([0-9.]+)}', $output, $match) && version_compare($match[1], '21.01', '<')) {
$this->io->writeError(' <warning>Unzipping using '.$executable.' '.$match[1].' may result in incorrect file permissions. Install '.$executable.' 21.01+ or unzip to ensure you get correct permissions.</warning>'); $this->io->writeError(' <warning>Unzipping using '.$executable.' '.$match[1].' may result in incorrect file permissions. Install '.$executable.' 21.01+ or unzip to ensure you get correct permissions.</warning>');
} }
} }

View File

@ -347,7 +347,7 @@ class EventDispatcher
} }
// 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 // 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 // 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::isMatch('{^[^\'"\s/\\\\]+}', $pathAndArgs, $match); $matched = Preg::isMatchStrictGroups('{^[^\'"\s/\\\\]+}', $pathAndArgs, $match);
if ($matched && !file_exists($match[0])) { if ($matched && !file_exists($match[0])) {
$finder = new ExecutableFinder; $finder = new ExecutableFinder;
if ($pathToExec = $finder->find($match[0])) { if ($pathToExec = $finder->find($match[0])) {

View File

@ -140,7 +140,7 @@ class BinaryInstaller
$handle = fopen($bin, 'r'); $handle = fopen($bin, 'r');
$line = fgets($handle); $line = fgets($handle);
fclose($handle); fclose($handle);
if (Preg::isMatch('{^#!/(?:usr/bin/env )?(?:[^/]+/)*(.+)$}m', $line, $match)) { if (Preg::isMatchStrictGroups('{^#!/(?:usr/bin/env )?(?:[^/]+/)*(.+)$}m', (string) $line, $match)) {
return trim($match[1]); return trim($match[1]);
} }
@ -218,7 +218,7 @@ class BinaryInstaller
// which allows calling the proxy with a custom php process // which allows calling the proxy with a custom php process
if (Preg::isMatch('{^(#!.*\r?\n)?[\r\n\t ]*<\?php}', $binContents, $match)) { if (Preg::isMatch('{^(#!.*\r?\n)?[\r\n\t ]*<\?php}', $binContents, $match)) {
// carry over the existing shebang if present, otherwise add our own // carry over the existing shebang if present, otherwise add our own
$proxyCode = empty($match[1]) ? '#!/usr/bin/env php' : trim($match[1]); $proxyCode = $match[1] === null ? '#!/usr/bin/env php' : trim($match[1]);
$binPathExported = $this->filesystem->findShortestPathCode($link, $bin, false, true); $binPathExported = $this->filesystem->findShortestPathCode($link, $bin, false, true);
$streamProxyCode = $streamHint = ''; $streamProxyCode = $streamHint = '';
$globalsCode = '$GLOBALS[\'_composer_bin_dir\'] = __DIR__;'."\n"; $globalsCode = '$GLOBALS[\'_composer_bin_dir\'] = __DIR__;'."\n";
@ -237,7 +237,7 @@ class BinaryInstaller
$data = str_replace(\'__DIR__\', var_export(dirname($this->realpath), true), $data); $data = str_replace(\'__DIR__\', var_export(dirname($this->realpath), true), $data);
$data = str_replace(\'__FILE__\', var_export($this->realpath, true), $data);'; $data = str_replace(\'__FILE__\', var_export($this->realpath, true), $data);';
} }
if (trim($match[0]) !== '<?php') { if (trim((string) $match[0]) !== '<?php') {
$streamHint = ' using a stream wrapper to prevent the shebang from being output on PHP<8'."\n *"; $streamHint = ' using a stream wrapper to prevent the shebang from being output on PHP<8'."\n *";
$streamProxyCode = <<<STREAMPROXY $streamProxyCode = <<<STREAMPROXY
if (PHP_VERSION_ID < 80000) { if (PHP_VERSION_ID < 80000) {

View File

@ -21,10 +21,10 @@ use Composer\Repository\PlatformRepository;
class JsonManipulator class JsonManipulator
{ {
/** @var string */ /** @var string */
private static $DEFINES = '(?(DEFINE) private const DEFINES = '(?(DEFINE)
(?<number> -? (?= [1-9]|0(?!\d) ) \d++ (\.\d++)? ([eE] [+-]?+ \d++)? ) (?<number> -? (?= [1-9]|0(?!\d) ) \d++ (?:\.\d++)? (?:[eE] [+-]?+ \d++)? )
(?<boolean> true | false | null ) (?<boolean> true | false | null )
(?<string> " ([^"\\\\]*+ | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9A-Fa-f]{4} )* " ) (?<string> " (?:[^"\\\\]*+ | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9A-Fa-f]{4} )* " )
(?<array> \[ (?: (?&json) \s*+ (?: , (?&json) \s*+ )*+ )?+ \s*+ \] ) (?<array> \[ (?: (?&json) \s*+ (?: , (?&json) \s*+ )*+ )?+ \s*+ \] )
(?<pair> \s*+ (?&string) \s*+ : (?&json) \s*+ ) (?<pair> \s*+ (?&string) \s*+ : (?&json) \s*+ )
(?<object> \{ (?: (?&pair) (?: , (?&pair) )*+ )?+ \s*+ \} ) (?<object> \{ (?: (?&pair) (?: , (?&pair) )*+ )?+ \s*+ \} )
@ -66,26 +66,30 @@ class JsonManipulator
return $this->addMainKey($type, [$package => $constraint]); return $this->addMainKey($type, [$package => $constraint]);
} }
$regex = '{'.self::$DEFINES.'^(?P<start>\s*\{\s*(?:(?&string)\s*:\s*(?&json)\s*,\s*)*?)'. $regex = '{'.self::DEFINES.'^(?P<start>\s*\{\s*(?:(?&string)\s*:\s*(?&json)\s*,\s*)*?)'.
'(?P<property>'.preg_quote(JsonFile::encode($type)).'\s*:\s*)(?P<value>(?&json))(?P<end>.*)}sx'; '(?P<property>'.preg_quote(JsonFile::encode($type)).'\s*:\s*)(?P<value>(?&json))(?P<end>.*)}sx';
if (!Preg::isMatch($regex, $this->contents, $matches)) { if (!Preg::isMatch($regex, $this->contents, $matches)) {
return false; return false;
} }
assert(is_string($matches['start']));
assert(is_string($matches['value']));
assert(is_string($matches['end']));
$links = $matches['value']; $links = $matches['value'];
// try to find existing link // try to find existing link
$packageRegex = str_replace('/', '\\\\?/', preg_quote($package)); $packageRegex = str_replace('/', '\\\\?/', preg_quote($package));
$regex = '{'.self::$DEFINES.'"(?P<package>'.$packageRegex.')"(\s*:\s*)(?&string)}ix'; $regex = '{'.self::DEFINES.'"(?P<package>'.$packageRegex.')"(\s*:\s*)(?&string)}ix';
if (Preg::isMatch($regex, $links, $packageMatches)) { if (Preg::isMatch($regex, $links, $packageMatches)) {
assert(is_string($packageMatches['package']));
// update existing link // update existing link
$existingPackage = $packageMatches['package']; $existingPackage = $packageMatches['package'];
$packageRegex = str_replace('/', '\\\\?/', preg_quote($existingPackage)); $packageRegex = str_replace('/', '\\\\?/', preg_quote($existingPackage));
$links = Preg::replaceCallback('{'.self::$DEFINES.'"'.$packageRegex.'"(?P<separator>\s*:\s*)(?&string)}ix', static function ($m) use ($existingPackage, $constraint): string { $links = Preg::replaceCallback('{'.self::DEFINES.'"'.$packageRegex.'"(?P<separator>\s*:\s*)(?&string)}ix', static function ($m) use ($existingPackage, $constraint): string {
return JsonFile::encode(str_replace('\\/', '/', $existingPackage)) . $m['separator'] . '"' . $constraint . '"'; return JsonFile::encode(str_replace('\\/', '/', $existingPackage)) . $m['separator'] . '"' . $constraint . '"';
}, $links); }, $links);
} else { } else {
if (Preg::isMatch('#^\s*\{\s*\S+.*?(\s*\}\s*)$#s', $links, $match)) { if (Preg::isMatchStrictGroups('#^\s*\{\s*\S+.*?(\s*\}\s*)$#s', $links, $match)) {
// link missing but non empty links // link missing but non empty links
$links = Preg::replace( $links = Preg::replace(
'{'.preg_quote($match[1]).'$}', '{'.preg_quote($match[1]).'$}',
@ -237,7 +241,7 @@ class JsonManipulator
} }
// main node content not match-able // main node content not match-able
$nodeRegex = '{'.self::$DEFINES.'^(?P<start> \s* \{ \s* (?: (?&string) \s* : (?&json) \s* , \s* )*?'. $nodeRegex = '{'.self::DEFINES.'^(?P<start> \s* \{ \s* (?: (?&string) \s* : (?&json) \s* , \s* )*?'.
preg_quote(JsonFile::encode($mainNode)).'\s*:\s*)(?P<content>(?&object))(?P<end>.*)}sx'; preg_quote(JsonFile::encode($mainNode)).'\s*:\s*)(?P<content>(?&object))(?P<end>.*)}sx';
try { try {
@ -251,6 +255,10 @@ class JsonManipulator
throw $e; throw $e;
} }
assert(is_string($match['start']));
assert(is_string($match['content']));
assert(is_string($match['end']));
$children = $match['content']; $children = $match['content'];
// invalid match due to un-regexable content, abort // invalid match due to un-regexable content, abort
if (!@json_decode($children)) { if (!@json_decode($children)) {
@ -258,7 +266,7 @@ class JsonManipulator
} }
// child exists // child exists
$childRegex = '{'.self::$DEFINES.'(?P<start>"'.preg_quote($name).'"\s*:\s*)(?P<content>(?&json))(?P<end>,?)}x'; $childRegex = '{'.self::DEFINES.'(?P<start>"'.preg_quote($name).'"\s*:\s*)(?P<content>(?&json))(?P<end>,?)}x';
if (Preg::isMatch($childRegex, $children, $matches)) { if (Preg::isMatch($childRegex, $children, $matches)) {
$children = Preg::replaceCallback($childRegex, function ($matches) use ($subName, $value): string { $children = Preg::replaceCallback($childRegex, function ($matches) use ($subName, $value): string {
if ($subName !== null && is_string($matches['content'])) { if ($subName !== null && is_string($matches['content'])) {
@ -331,7 +339,7 @@ class JsonManipulator
} }
// no node content match-able // no node content match-able
$nodeRegex = '{'.self::$DEFINES.'^(?P<start> \s* \{ \s* (?: (?&string) \s* : (?&json) \s* , \s* )*?'. $nodeRegex = '{'.self::DEFINES.'^(?P<start> \s* \{ \s* (?: (?&string) \s* : (?&json) \s* , \s* )*?'.
preg_quote(JsonFile::encode($mainNode)).'\s*:\s*)(?P<content>(?&object))(?P<end>.*)}sx'; preg_quote(JsonFile::encode($mainNode)).'\s*:\s*)(?P<content>(?&object))(?P<end>.*)}sx';
try { try {
if (!Preg::isMatch($nodeRegex, $this->contents, $match)) { if (!Preg::isMatch($nodeRegex, $this->contents, $match)) {
@ -344,6 +352,10 @@ class JsonManipulator
throw $e; throw $e;
} }
assert(is_string($match['start']));
assert(is_string($match['content']));
assert(is_string($match['end']));
$children = $match['content']; $children = $match['content'];
// invalid match due to un-regexable content, abort // invalid match due to un-regexable content, abort
@ -365,9 +377,10 @@ class JsonManipulator
$keyRegex = str_replace('/', '\\\\?/', preg_quote($name)); $keyRegex = str_replace('/', '\\\\?/', preg_quote($name));
if (Preg::isMatch('{"'.$keyRegex.'"\s*:}i', $children)) { if (Preg::isMatch('{"'.$keyRegex.'"\s*:}i', $children)) {
// find best match for the value of "name" // find best match for the value of "name"
if (Preg::isMatchAll('{'.self::$DEFINES.'"'.$keyRegex.'"\s*:\s*(?:(?&json))}x', $children, $matches)) { if (Preg::isMatchAll('{'.self::DEFINES.'"'.$keyRegex.'"\s*:\s*(?:(?&json))}x', $children, $matches)) {
$bestMatch = ''; $bestMatch = '';
foreach ($matches[0] as $match) { foreach ($matches[0] as $match) {
assert(is_string($match));
if (strlen($bestMatch) < strlen($match)) { if (strlen($bestMatch) < strlen($match)) {
$bestMatch = $match; $bestMatch = $match;
} }
@ -432,7 +445,7 @@ class JsonManipulator
$content = $this->format($content); $content = $this->format($content);
// key exists already // key exists already
$regex = '{'.self::$DEFINES.'^(?P<start>\s*\{\s*(?:(?&string)\s*:\s*(?&json)\s*,\s*)*?)'. $regex = '{'.self::DEFINES.'^(?P<start>\s*\{\s*(?:(?&string)\s*:\s*(?&json)\s*,\s*)*?)'.
'(?P<key>'.preg_quote(JsonFile::encode($key)).'\s*:\s*(?&json))(?P<end>.*)}sx'; '(?P<key>'.preg_quote(JsonFile::encode($key)).'\s*:\s*(?&json))(?P<end>.*)}sx';
if (isset($decoded[$key]) && Preg::isMatch($regex, $this->contents, $matches)) { if (isset($decoded[$key]) && Preg::isMatch($regex, $this->contents, $matches)) {
// invalid match due to un-regexable content, abort // invalid match due to un-regexable content, abort
@ -475,16 +488,20 @@ class JsonManipulator
} }
// key exists already // key exists already
$regex = '{'.self::$DEFINES.'^(?P<start>\s*\{\s*(?:(?&string)\s*:\s*(?&json)\s*,\s*)*?)'. $regex = '{'.self::DEFINES.'^(?P<start>\s*\{\s*(?:(?&string)\s*:\s*(?&json)\s*,\s*)*?)'.
'(?P<removal>'.preg_quote(JsonFile::encode($key)).'\s*:\s*(?&json))\s*,?\s*(?P<end>.*)}sx'; '(?P<removal>'.preg_quote(JsonFile::encode($key)).'\s*:\s*(?&json))\s*,?\s*(?P<end>.*)}sx';
if (Preg::isMatch($regex, $this->contents, $matches)) { if (Preg::isMatch($regex, $this->contents, $matches)) {
assert(is_string($matches['start']));
assert(is_string($matches['removal']));
assert(is_string($matches['end']));
// invalid match due to un-regexable content, abort // invalid match due to un-regexable content, abort
if (!@json_decode('{'.$matches['removal'].'}')) { if (!@json_decode('{'.$matches['removal'].'}')) {
return false; return false;
} }
// check that we are not leaving a dangling comma on the previous line if the last line was removed // check that we are not leaving a dangling comma on the previous line if the last line was removed
if (Preg::isMatch('#,\s*$#', $matches['start']) && Preg::isMatch('#^\}$#', $matches['end'])) { if (Preg::isMatchStrictGroups('#,\s*$#', $matches['start']) && Preg::isMatch('#^\}$#', $matches['end'])) {
$matches['start'] = rtrim(Preg::replace('#,(\s*)$#', '$1', $matches['start']), $this->indent); $matches['start'] = rtrim(Preg::replace('#,(\s*)$#', '$1', $matches['start']), $this->indent);
} }
@ -544,7 +561,7 @@ class JsonManipulator
protected function detectIndenting(): void protected function detectIndenting(): void
{ {
if (Preg::isMatch('{^([ \t]+)"}m', $this->contents, $match)) { if (Preg::isMatchStrictGroups('{^([ \t]+)"}m', $this->contents, $match)) {
$this->indent = $match[1]; $this->indent = $match[1];
} else { } else {
$this->indent = ' '; $this->indent = ' ';

View File

@ -193,7 +193,7 @@ class RootPackageLoader extends ArrayLoader
private function extractAliases(array $requires, array $aliases): array private function extractAliases(array $requires, array $aliases): array
{ {
foreach ($requires as $reqName => $reqVersion) { foreach ($requires as $reqName => $reqVersion) {
if (Preg::isMatch('{(?:^|\| *|, *)([^,\s#|]+)(?:#[^ ]+)? +as +([^,\s|]+)(?:$| *\|| *,)}', $reqVersion, $match)) { if (Preg::isMatchStrictGroups('{(?:^|\| *|, *)([^,\s#|]+)(?:#[^ ]+)? +as +([^,\s|]+)(?:$| *\|| *,)}', $reqVersion, $match)) {
$aliases[] = [ $aliases[] = [
'package' => strtolower($reqName), 'package' => strtolower($reqName),
'version' => $this->versionParser->normalize($match[1], $reqVersion), 'version' => $this->versionParser->normalize($match[1], $reqVersion),
@ -239,7 +239,7 @@ class RootPackageLoader extends ArrayLoader
// parse explicit stability flags to the most unstable // parse explicit stability flags to the most unstable
$matched = false; $matched = false;
foreach ($constraints as $constraint) { foreach ($constraints as $constraint) {
if (Preg::isMatch('{^[^@]*?@('.implode('|', array_keys($stabilities)).')$}i', $constraint, $match)) { if (Preg::isMatchStrictGroups('{^[^@]*?@('.implode('|', array_keys($stabilities)).')$}i', $constraint, $match)) {
$name = strtolower($reqName); $name = strtolower($reqName);
$stability = $stabilities[VersionParser::normalizeStability($match[1])]; $stability = $stabilities[VersionParser::normalizeStability($match[1])];
@ -285,7 +285,7 @@ class RootPackageLoader extends ArrayLoader
{ {
foreach ($requires as $reqName => $reqVersion) { foreach ($requires as $reqName => $reqVersion) {
$reqVersion = Preg::replace('{^([^,\s@]+) as .+$}', '$1', $reqVersion); $reqVersion = Preg::replace('{^([^,\s@]+) as .+$}', '$1', $reqVersion);
if (Preg::isMatch('{^[^,\s@]+?#([a-f0-9]+)$}', $reqVersion, $match) && 'dev' === VersionParser::parseStability($reqVersion)) { if (Preg::isMatchStrictGroups('{^[^,\s@]+?#([a-f0-9]+)$}', $reqVersion, $match) && 'dev' === VersionParser::parseStability($reqVersion)) {
$name = strtolower($reqName); $name = strtolower($reqName);
$references[$name] = $match[1]; $references[$name] = $match[1];
} }

View File

@ -89,7 +89,7 @@ class VersionBumper
if (Preg::isMatchAllWithOffsets($pattern, $prettyConstraint, $matches)) { if (Preg::isMatchAllWithOffsets($pattern, $prettyConstraint, $matches)) {
$modified = $prettyConstraint; $modified = $prettyConstraint;
foreach (array_reverse($matches['constraint']) as $match) { foreach (array_reverse($matches['constraint']) as $match) {
$modified = substr_replace($modified, $newPrettyConstraint, $match[1], Platform::strlen($match[0])); $modified = substr_replace($modified, $newPrettyConstraint, $match[1], Platform::strlen((string) $match[0]));
} }
// if it is strictly equal to the previous one then no need to change anything // if it is strictly equal to the previous one then no need to change anything

View File

@ -142,7 +142,7 @@ class VersionGuesser
// find current branch and collect all branch names // find current branch and collect all branch names
foreach ($this->process->splitLines($output) as $branch) { foreach ($this->process->splitLines($output) as $branch) {
if ($branch && Preg::isMatch('{^(?:\* ) *(\(no branch\)|\(detached from \S+\)|\(HEAD detached at \S+\)|\S+) *([a-f0-9]+) .*$}', $branch, $match)) { if ($branch && Preg::isMatchStrictGroups('{^(?:\* ) *(\(no branch\)|\(detached from \S+\)|\(HEAD detached at \S+\)|\S+) *([a-f0-9]+) .*$}', $branch, $match)) {
if ( if (
$match[1] === '(no branch)' $match[1] === '(no branch)'
|| strpos($match[1], '(detached ') === 0 || strpos($match[1], '(detached ') === 0
@ -158,13 +158,11 @@ class VersionGuesser
$isFeatureBranch = $this->isFeatureBranch($packageConfig, $match[1]); $isFeatureBranch = $this->isFeatureBranch($packageConfig, $match[1]);
} }
if ($match[2]) { $commit = $match[2];
$commit = $match[2];
}
} }
if ($branch && !Preg::isMatch('{^ *.+/HEAD }', $branch)) { if ($branch && !Preg::isMatchStrictGroups('{^ *.+/HEAD }', $branch)) {
if (Preg::isMatch('{^(?:\* )? *((?:remotes/(?:origin|upstream)/)?[^\s/]+) *([a-f0-9]+) .*$}', $branch, $match)) { if (Preg::isMatchStrictGroups('{^(?:\* )? *((?:remotes/(?:origin|upstream)/)?[^\s/]+) *([a-f0-9]+) .*$}', $branch, $match)) {
$branches[] = $match[1]; $branches[] = $match[1];
} }
} }
@ -191,7 +189,7 @@ class VersionGuesser
} }
} }
if (!$commit) { if (null === $commit) {
$command = 'git log --pretty="%H" -n1 HEAD'.GitUtil::getNoShowSignatureFlag($this->process); $command = 'git log --pretty="%H" -n1 HEAD'.GitUtil::getNoShowSignatureFlag($this->process);
if (0 === $this->process->execute($command, $output, $path)) { if (0 === $this->process->execute($command, $output, $path)) {
$commit = trim($output) ?: null; $commit = trim($output) ?: null;
@ -399,7 +397,7 @@ class VersionGuesser
$urlPattern = '#<url>.*/(' . $trunkPath . '|(' . $branchesPath . '|' . $tagsPath . ')/(.*))</url>#'; $urlPattern = '#<url>.*/(' . $trunkPath . '|(' . $branchesPath . '|' . $tagsPath . ')/(.*))</url>#';
if (Preg::isMatch($urlPattern, $output, $matches)) { if (Preg::isMatchStrictGroups($urlPattern, $output, $matches)) {
if (isset($matches[2]) && ($branchesPath === $matches[2] || $tagsPath === $matches[2])) { if (isset($matches[2]) && ($branchesPath === $matches[2] || $tagsPath === $matches[2])) {
// we are in a branches path // we are in a branches path
$version = $this->versionParser->normalizeBranch($matches[3]); $version = $this->versionParser->normalizeBranch($matches[3]);

View File

@ -210,7 +210,7 @@ class VersionSelector
$extra = $loader->getBranchAlias($dumper->dump($package)); $extra = $loader->getBranchAlias($dumper->dump($package));
if ($extra && $extra !== VersionParser::DEFAULT_BRANCH_ALIAS) { if ($extra && $extra !== VersionParser::DEFAULT_BRANCH_ALIAS) {
$extra = Preg::replace('{^(\d+\.\d+\.\d+)(\.9999999)-dev$}', '$1.0', $extra, -1, $count); $extra = Preg::replace('{^(\d+\.\d+\.\d+)(\.9999999)-dev$}', '$1.0', $extra, -1, $count);
if ($count) { if ($count > 0) {
$extra = str_replace('.9999999', '.0', $extra); $extra = str_replace('.9999999', '.0', $extra);
return $this->transformVersion($extra, $extra, 'dev'); return $this->transformVersion($extra, $extra, 'dev');

View File

@ -26,7 +26,7 @@ class Version
{ {
$isFips = false; $isFips = false;
if (!Preg::isMatch('/^(?<version>[0-9.]+)(?<patch>[a-z]{0,2})?(?<suffix>(?:-?(?:dev|pre|alpha|beta|rc|fips)[\d]*)*)?(?<garbage>-\w+)?(?<garbage2> \(.+?\))?$/', $opensslVersion, $matches)) { if (!Preg::isMatchStrictGroups('/^(?<version>[0-9.]+)(?<patch>[a-z]{0,2})(?<suffix>(?:-?(?:dev|pre|alpha|beta|rc|fips)[\d]*)*)(?:-\w+)?(?: \(.+?\))?$/', $opensslVersion, $matches)) {
return null; return null;
} }
@ -44,7 +44,7 @@ class Version
public static function parseLibjpeg(string $libjpegVersion): ?string public static function parseLibjpeg(string $libjpegVersion): ?string
{ {
if (!Preg::isMatch('/^(?<major>\d+)(?<minor>[a-z]*)$/', $libjpegVersion, $matches)) { if (!Preg::isMatchStrictGroups('/^(?<major>\d+)(?<minor>[a-z]*)$/', $libjpegVersion, $matches)) {
return null; return null;
} }
@ -53,7 +53,7 @@ class Version
public static function parseZoneinfoVersion(string $zoneinfoVersion): ?string public static function parseZoneinfoVersion(string $zoneinfoVersion): ?string
{ {
if (!Preg::isMatch('/^(?<year>\d{4})(?<revision>[a-z]*)$/', $zoneinfoVersion, $matches)) { if (!Preg::isMatchStrictGroups('/^(?<year>\d{4})(?<revision>[a-z]*)$/', $zoneinfoVersion, $matches)) {
return null; return null;
} }

View File

@ -584,7 +584,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
if ($this->hasProviders() || $this->lazyProvidersUrl) { if ($this->hasProviders() || $this->lazyProvidersUrl) {
// optimize search for "^foo/bar" where at least "^foo/" is present by loading this directly from the listUrl if present // optimize search for "^foo/bar" where at least "^foo/" is present by loading this directly from the listUrl if present
if (Preg::isMatch('{^\^(?P<query>(?P<vendor>[a-z0-9_.-]+)/[a-z0-9_.-]*)\*?$}i', $query, $match) && $this->listUrl !== null) { if (Preg::isMatchStrictGroups('{^\^(?P<query>(?P<vendor>[a-z0-9_.-]+)/[a-z0-9_.-]*)\*?$}i', $query, $match) && $this->listUrl !== null) {
$url = $this->listUrl . '?vendor='.urlencode($match['vendor']).'&filter='.urlencode($match['query'].'*'); $url = $this->listUrl . '?vendor='.urlencode($match['vendor']).'&filter='.urlencode($match['query'].'*');
$result = $this->httpDownloader->get($url, $this->options)->decodeJson(); $result = $this->httpDownloader->get($url, $this->options)->decodeJson();

View File

@ -211,7 +211,7 @@ class PlatformRepository extends ArrayRepository
} }
// AMQP protocol version => 0-9-1 // AMQP protocol version => 0-9-1
if (Preg::isMatch('/^AMQP protocol version => (?<version>.+)$/im', $info, $protocolMatches)) { if (Preg::isMatchStrictGroups('/^AMQP protocol version => (?<version>.+)$/im', $info, $protocolMatches)) {
$this->addLibrary($name.'-protocol', str_replace('-', '.', $protocolMatches['version']), 'AMQP protocol version'); $this->addLibrary($name.'-protocol', str_replace('-', '.', $protocolMatches['version']), 'AMQP protocol version');
} }
break; break;
@ -232,7 +232,7 @@ class PlatformRepository extends ArrayRepository
$info = $this->runtime->getExtensionInfo($name); $info = $this->runtime->getExtensionInfo($name);
// SSL Version => OpenSSL/1.0.1t // SSL Version => OpenSSL/1.0.1t
if (Preg::isMatch('{^SSL Version => (?<library>[^/]+)/(?<version>.+)$}im', $info, $sslMatches)) { if (Preg::isMatchStrictGroups('{^SSL Version => (?<library>[^/]+)/(?<version>.+)$}im', $info, $sslMatches)) {
$library = strtolower($sslMatches['library']); $library = strtolower($sslMatches['library']);
if ($library === 'openssl') { if ($library === 'openssl') {
$parsedVersion = Version::parseOpenssl($sslMatches['version'], $isFips); $parsedVersion = Version::parseOpenssl($sslMatches['version'], $isFips);
@ -243,12 +243,12 @@ class PlatformRepository extends ArrayRepository
} }
// libSSH Version => libssh2/1.4.3 // libSSH Version => libssh2/1.4.3
if (Preg::isMatch('{^libSSH Version => (?<library>[^/]+)/(?<version>.+?)(?:/.*)?$}im', $info, $sshMatches)) { if (Preg::isMatchStrictGroups('{^libSSH Version => (?<library>[^/]+)/(?<version>.+?)(?:/.*)?$}im', $info, $sshMatches)) {
$this->addLibrary($name.'-'.strtolower($sshMatches['library']), $sshMatches['version'], 'curl '.$sshMatches['library'].' version'); $this->addLibrary($name.'-'.strtolower($sshMatches['library']), $sshMatches['version'], 'curl '.$sshMatches['library'].' version');
} }
// ZLib Version => 1.2.8 // ZLib Version => 1.2.8
if (Preg::isMatch('{^ZLib Version => (?<version>.+)$}im', $info, $zlibMatches)) { if (Preg::isMatchStrictGroups('{^ZLib Version => (?<version>.+)$}im', $info, $zlibMatches)) {
$this->addLibrary($name.'-zlib', $zlibMatches['version'], 'curl zlib version'); $this->addLibrary($name.'-zlib', $zlibMatches['version'], 'curl zlib version');
} }
break; break;
@ -257,14 +257,14 @@ class PlatformRepository extends ArrayRepository
$info = $this->runtime->getExtensionInfo($name); $info = $this->runtime->getExtensionInfo($name);
// timelib version => 2018.03 // timelib version => 2018.03
if (Preg::isMatch('/^timelib version => (?<version>.+)$/im', $info, $timelibMatches)) { if (Preg::isMatchStrictGroups('/^timelib version => (?<version>.+)$/im', $info, $timelibMatches)) {
$this->addLibrary($name.'-timelib', $timelibMatches['version'], 'date timelib version'); $this->addLibrary($name.'-timelib', $timelibMatches['version'], 'date timelib version');
} }
// Timezone Database => internal // Timezone Database => internal
if (Preg::isMatch('/^Timezone Database => (?<source>internal|external)$/im', $info, $zoneinfoSourceMatches)) { if (Preg::isMatchStrictGroups('/^Timezone Database => (?<source>internal|external)$/im', $info, $zoneinfoSourceMatches)) {
$external = $zoneinfoSourceMatches['source'] === 'external'; $external = $zoneinfoSourceMatches['source'] === 'external';
if (Preg::isMatch('/^"Olson" Timezone Database Version => (?<version>.+?)(\.system)?$/im', $info, $zoneinfoMatches)) { if (Preg::isMatchStrictGroups('/^"Olson" Timezone Database Version => (?<version>.+?)(?:\.system)?$/im', $info, $zoneinfoMatches)) {
// If the timezonedb is provided by ext/timezonedb, register that version as a replacement // If the timezonedb is provided by ext/timezonedb, register that version as a replacement
if ($external && in_array('timezonedb', $loadedExtensions, true)) { if ($external && in_array('timezonedb', $loadedExtensions, true)) {
$this->addLibrary('timezonedb-zoneinfo', $zoneinfoMatches['version'], 'zoneinfo ("Olson") database for date (replaced by timezonedb)', [$name.'-zoneinfo']); $this->addLibrary('timezonedb-zoneinfo', $zoneinfoMatches['version'], 'zoneinfo ("Olson") database for date (replaced by timezonedb)', [$name.'-zoneinfo']);
@ -289,19 +289,19 @@ class PlatformRepository extends ArrayRepository
$info = $this->runtime->getExtensionInfo($name); $info = $this->runtime->getExtensionInfo($name);
if (Preg::isMatch('/^libJPEG Version => (?<version>.+?)(?: compatible)?$/im', $info, $libjpegMatches)) { if (Preg::isMatchStrictGroups('/^libJPEG Version => (?<version>.+?)(?: compatible)?$/im', $info, $libjpegMatches)) {
$this->addLibrary($name.'-libjpeg', Version::parseLibjpeg($libjpegMatches['version']), 'libjpeg version for gd'); $this->addLibrary($name.'-libjpeg', Version::parseLibjpeg($libjpegMatches['version']), 'libjpeg version for gd');
} }
if (Preg::isMatch('/^libPNG Version => (?<version>.+)$/im', $info, $libpngMatches)) { if (Preg::isMatchStrictGroups('/^libPNG Version => (?<version>.+)$/im', $info, $libpngMatches)) {
$this->addLibrary($name.'-libpng', $libpngMatches['version'], 'libpng version for gd'); $this->addLibrary($name.'-libpng', $libpngMatches['version'], 'libpng version for gd');
} }
if (Preg::isMatch('/^FreeType Version => (?<version>.+)$/im', $info, $freetypeMatches)) { if (Preg::isMatchStrictGroups('/^FreeType Version => (?<version>.+)$/im', $info, $freetypeMatches)) {
$this->addLibrary($name.'-freetype', $freetypeMatches['version'], 'freetype version for gd'); $this->addLibrary($name.'-freetype', $freetypeMatches['version'], 'freetype version for gd');
} }
if (Preg::isMatch('/^libXpm Version => (?<versionId>\d+)$/im', $info, $libxpmMatches)) { if (Preg::isMatchStrictGroups('/^libXpm Version => (?<versionId>\d+)$/im', $info, $libxpmMatches)) {
$this->addLibrary($name.'-libxpm', Version::convertLibxpmVersionId((int) $libxpmMatches['versionId']), 'libxpm version for gd'); $this->addLibrary($name.'-libxpm', Version::convertLibxpmVersionId((int) $libxpmMatches['versionId']), 'libxpm version for gd');
} }
@ -327,7 +327,7 @@ class PlatformRepository extends ArrayRepository
} }
// ICU TZData version => 2019c // ICU TZData version => 2019c
if (Preg::isMatch('/^ICU TZData version => (?<version>.*)$/im', $info, $zoneinfoMatches) && null !== ($version = Version::parseZoneinfoVersion($zoneinfoMatches['version']))) { if (Preg::isMatchStrictGroups('/^ICU TZData version => (?<version>.*)$/im', $info, $zoneinfoMatches) && null !== ($version = Version::parseZoneinfoVersion($zoneinfoMatches['version']))) {
$this->addLibrary('icu-zoneinfo', $version, 'zoneinfo ("Olson") database for icu'); $this->addLibrary('icu-zoneinfo', $version, 'zoneinfo ("Olson") database for icu');
} }
@ -358,7 +358,7 @@ class PlatformRepository extends ArrayRepository
case 'ldap': case 'ldap':
$info = $this->runtime->getExtensionInfo($name); $info = $this->runtime->getExtensionInfo($name);
if (Preg::isMatch('/^Vendor Version => (?<versionId>\d+)$/im', $info, $matches) && Preg::isMatch('/^Vendor Name => (?<vendor>.+)$/im', $info, $vendorMatches)) { if (Preg::isMatchStrictGroups('/^Vendor Version => (?<versionId>\d+)$/im', $info, $matches) && Preg::isMatchStrictGroups('/^Vendor Name => (?<vendor>.+)$/im', $info, $vendorMatches)) {
$this->addLibrary($name.'-'.strtolower($vendorMatches['vendor']), Version::convertOpenldapVersionId((int) $matches['versionId']), $vendorMatches['vendor'].' version of ldap'); $this->addLibrary($name.'-'.strtolower($vendorMatches['vendor']), Version::convertOpenldapVersionId((int) $matches['versionId']), $vendorMatches['vendor'].' version of ldap');
} }
break; break;
@ -402,7 +402,7 @@ class PlatformRepository extends ArrayRepository
case 'openssl': case 'openssl':
// OpenSSL 1.1.1g 21 Apr 2020 // OpenSSL 1.1.1g 21 Apr 2020
if (Preg::isMatch('{^(?:OpenSSL|LibreSSL)?\s*(?<version>\S+)}i', $this->runtime->getConstant('OPENSSL_VERSION_TEXT'), $matches)) { if (Preg::isMatchStrictGroups('{^(?:OpenSSL|LibreSSL)?\s*(?<version>\S+)}i', $this->runtime->getConstant('OPENSSL_VERSION_TEXT'), $matches)) {
$parsedVersion = Version::parseOpenssl($matches['version'], $isFips); $parsedVersion = Version::parseOpenssl($matches['version'], $isFips);
$this->addLibrary($name.($isFips ? '-fips' : ''), $parsedVersion, $this->runtime->getConstant('OPENSSL_VERSION_TEXT'), [], $isFips ? [$name] : []); $this->addLibrary($name.($isFips ? '-fips' : ''), $parsedVersion, $this->runtime->getConstant('OPENSSL_VERSION_TEXT'), [], $isFips ? [$name] : []);
} }
@ -414,7 +414,7 @@ class PlatformRepository extends ArrayRepository
$info = $this->runtime->getExtensionInfo($name); $info = $this->runtime->getExtensionInfo($name);
// PCRE Unicode Version => 12.1.0 // PCRE Unicode Version => 12.1.0
if (Preg::isMatch('/^PCRE Unicode Version => (?<version>.+)$/im', $info, $pcreUnicodeMatches)) { if (Preg::isMatchStrictGroups('/^PCRE Unicode Version => (?<version>.+)$/im', $info, $pcreUnicodeMatches)) {
$this->addLibrary($name.'-unicode', $pcreUnicodeMatches['version'], 'PCRE Unicode version support'); $this->addLibrary($name.'-unicode', $pcreUnicodeMatches['version'], 'PCRE Unicode version support');
} }
@ -424,7 +424,7 @@ class PlatformRepository extends ArrayRepository
case 'pdo_mysql': case 'pdo_mysql':
$info = $this->runtime->getExtensionInfo($name); $info = $this->runtime->getExtensionInfo($name);
if (Preg::isMatch('/^(?:Client API version|Version) => mysqlnd (?<version>.+?) /mi', $info, $matches)) { if (Preg::isMatchStrictGroups('/^(?:Client API version|Version) => mysqlnd (?<version>.+?) /mi', $info, $matches)) {
$this->addLibrary($name.'-mysqlnd', $matches['version'], 'mysqlnd library version for '.$name); $this->addLibrary($name.'-mysqlnd', $matches['version'], 'mysqlnd library version for '.$name);
} }
break; break;
@ -432,11 +432,11 @@ class PlatformRepository extends ArrayRepository
case 'mongodb': case 'mongodb':
$info = $this->runtime->getExtensionInfo($name); $info = $this->runtime->getExtensionInfo($name);
if (Preg::isMatch('/^libmongoc bundled version => (?<version>.+)$/im', $info, $libmongocMatches)) { if (Preg::isMatchStrictGroups('/^libmongoc bundled version => (?<version>.+)$/im', $info, $libmongocMatches)) {
$this->addLibrary($name.'-libmongoc', $libmongocMatches['version'], 'libmongoc version of mongodb'); $this->addLibrary($name.'-libmongoc', $libmongocMatches['version'], 'libmongoc version of mongodb');
} }
if (Preg::isMatch('/^libbson bundled version => (?<version>.+)$/im', $info, $libbsonMatches)) { if (Preg::isMatchStrictGroups('/^libbson bundled version => (?<version>.+)$/im', $info, $libbsonMatches)) {
$this->addLibrary($name.'-libbson', $libbsonMatches['version'], 'libbson version of mongodb'); $this->addLibrary($name.'-libbson', $libbsonMatches['version'], 'libbson version of mongodb');
} }
break; break;
@ -611,7 +611,7 @@ class PlatformRepository extends ArrayRepository
$version = $this->versionParser->normalize($prettyVersion); $version = $this->versionParser->normalize($prettyVersion);
} catch (\UnexpectedValueException $e) { } catch (\UnexpectedValueException $e) {
$extraDescription = ' (actual version: '.$prettyVersion.')'; $extraDescription = ' (actual version: '.$prettyVersion.')';
if (Preg::isMatch('{^(\d+\.\d+\.\d+(?:\.\d+)?)}', $prettyVersion, $match)) { if (Preg::isMatchStrictGroups('{^(\d+\.\d+\.\d+(?:\.\d+)?)}', $prettyVersion, $match)) {
$prettyVersion = $match[1]; $prettyVersion = $match[1];
} else { } else {
$prettyVersion = '0'; $prettyVersion = '0';

View File

@ -63,7 +63,7 @@ class GitBitbucketDriver extends VcsDriver
*/ */
public function initialize(): void public function initialize(): void
{ {
if (!Preg::isMatch('#^https?://bitbucket\.org/([^/]+)/([^/]+?)(\.git|/?)?$#i', $this->url, $match)) { if (!Preg::isMatchStrictGroups('#^https?://bitbucket\.org/([^/]+)/([^/]+?)(?:\.git|/?)?$#i', $this->url, $match)) {
throw new \InvalidArgumentException(sprintf('The Bitbucket repository URL %s is invalid. It must be the HTTPS URL of a Bitbucket repository.', $this->url)); throw new \InvalidArgumentException(sprintf('The Bitbucket repository URL %s is invalid. It must be the HTTPS URL of a Bitbucket repository.', $this->url));
} }

View File

@ -106,7 +106,7 @@ class GitDriver extends VcsDriver
$branches = $this->process->splitLines($output); $branches = $this->process->splitLines($output);
if (!in_array('* master', $branches)) { if (!in_array('* master', $branches)) {
foreach ($branches as $branch) { foreach ($branches as $branch) {
if ($branch && Preg::isMatch('{^\* +(\S+)}', $branch, $match)) { if ($branch && Preg::isMatchStrictGroups('{^\* +(\S+)}', $branch, $match)) {
$this->rootIdentifier = $match[1]; $this->rootIdentifier = $match[1];
break; break;
} }
@ -203,7 +203,7 @@ class GitDriver extends VcsDriver
$this->process->execute('git branch --no-color --no-abbrev -v', $output, $this->repoDir); $this->process->execute('git branch --no-color --no-abbrev -v', $output, $this->repoDir);
foreach ($this->process->splitLines($output) as $branch) { foreach ($this->process->splitLines($output) as $branch) {
if ($branch && !Preg::isMatch('{^ *[^/]+/HEAD }', $branch)) { if ($branch && !Preg::isMatch('{^ *[^/]+/HEAD }', $branch)) {
if (Preg::isMatch('{^(?:\* )? *(\S+) *([a-f0-9]+)(?: .*)?$}', $branch, $match) && $match[1][0] !== '-') { if (Preg::isMatchStrictGroups('{^(?:\* )? *(\S+) *([a-f0-9]+)(?: .*)?$}', $branch, $match) && $match[1][0] !== '-') {
$branches[$match[1]] = $match[2]; $branches[$match[1]] = $match[2];
} }
} }

View File

@ -63,9 +63,11 @@ class GitHubDriver extends VcsDriver
throw new \InvalidArgumentException(sprintf('The GitHub repository URL %s is invalid.', $this->url)); throw new \InvalidArgumentException(sprintf('The GitHub repository URL %s is invalid.', $this->url));
} }
assert(is_string($match[3]));
assert(is_string($match[4]));
$this->owner = $match[3]; $this->owner = $match[3];
$this->repository = $match[4]; $this->repository = $match[4];
$this->originUrl = strtolower(!empty($match[1]) ? $match[1] : $match[2]); $this->originUrl = strtolower($match[1] ?? (string) $match[2]);
if ($this->originUrl === 'www.github.com') { if ($this->originUrl === 'www.github.com') {
$this->originUrl = 'github.com'; $this->originUrl = 'github.com';
} }
@ -227,27 +229,27 @@ class GitHubDriver extends VcsDriver
$key = null; $key = null;
foreach (Preg::split('{\r?\n}', $funding) as $line) { foreach (Preg::split('{\r?\n}', $funding) as $line) {
$line = trim($line); $line = trim($line);
if (Preg::isMatch('{^(\w+)\s*:\s*(.+)$}', $line, $match)) { if (Preg::isMatchStrictGroups('{^(\w+)\s*:\s*(.+)$}', $line, $match)) {
if ($match[2] === '[') { if ($match[2] === '[') {
$key = $match[1]; $key = $match[1];
continue; continue;
} }
if (Preg::isMatch('{^\[(.*)\](?:\s*#.*)?$}', $match[2], $match2)) { if (Preg::isMatchStrictGroups('{^\[(.*)\](?:\s*#.*)?$}', $match[2], $match2)) {
foreach (array_map('trim', Preg::split('{[\'"]?\s*,\s*[\'"]?}', $match2[1])) as $item) { foreach (array_map('trim', Preg::split('{[\'"]?\s*,\s*[\'"]?}', $match2[1])) as $item) {
$result[] = ['type' => $match[1], 'url' => trim($item, '"\' ')]; $result[] = ['type' => $match[1], 'url' => trim($item, '"\' ')];
} }
} elseif (Preg::isMatch('{^([^#].*?)(\s+#.*)?$}', $match[2], $match2)) { } elseif (Preg::isMatchStrictGroups('{^([^#].*?)(?:\s+#.*)?$}', $match[2], $match2)) {
$result[] = ['type' => $match[1], 'url' => trim($match2[1], '"\' ')]; $result[] = ['type' => $match[1], 'url' => trim($match2[1], '"\' ')];
} }
$key = null; $key = null;
} elseif (Preg::isMatch('{^(\w+)\s*:\s*#\s*$}', $line, $match)) { } elseif (Preg::isMatchStrictGroups('{^(\w+)\s*:\s*#\s*$}', $line, $match)) {
$key = $match[1]; $key = $match[1];
} elseif ($key && ( } elseif ($key !== null && (
Preg::isMatch('{^-\s*(.+)(\s+#.*)?$}', $line, $match) Preg::isMatchStrictGroups('{^-\s*(.+)(?:\s+#.*)?$}', $line, $match)
|| Preg::isMatch('{^(.+),(\s*#.*)?$}', $line, $match) || Preg::isMatchStrictGroups('{^(.+),(?:\s*#.*)?$}', $line, $match)
)) { )) {
$result[] = ['type' => $key, 'url' => trim($match[1], '"\' ')]; $result[] = ['type' => $key, 'url' => trim($match[1], '"\' ')];
} elseif ($key && $line === ']') { } elseif ($key !== null && $line === ']') {
$key = null; $key = null;
} }
} }
@ -395,7 +397,7 @@ class GitHubDriver extends VcsDriver
return false; return false;
} }
$originUrl = !empty($matches[2]) ? $matches[2] : $matches[3]; $originUrl = $matches[2] ?? (string) $matches[3];
if (!in_array(strtolower(Preg::replace('{^www\.}i', '', $originUrl)), $config->get('github-domains'))) { if (!in_array(strtolower(Preg::replace('{^www\.}i', '', $originUrl)), $config->get('github-domains'))) {
return false; return false;
} }

View File

@ -97,11 +97,13 @@ class GitLabDriver extends VcsDriver
throw new \InvalidArgumentException(sprintf('The GitLab repository URL %s is invalid. It must be the HTTP URL of a GitLab project.', $this->url)); throw new \InvalidArgumentException(sprintf('The GitLab repository URL %s is invalid. It must be the HTTP URL of a GitLab project.', $this->url));
} }
$guessedDomain = !empty($match['domain']) ? $match['domain'] : $match['domain2']; assert(is_string($match['parts']));
assert(is_string($match['repo']));
$guessedDomain = $match['domain'] ?? (string) $match['domain2'];
$configuredDomains = $this->config->get('gitlab-domains'); $configuredDomains = $this->config->get('gitlab-domains');
$urlParts = explode('/', $match['parts']); $urlParts = explode('/', $match['parts']);
$this->scheme = !empty($match['scheme']) $this->scheme = in_array($match['scheme'], ['https', 'http'], true)
? $match['scheme'] ? $match['scheme']
: (isset($this->repoConfig['secure-http']) && $this->repoConfig['secure-http'] === false ? 'http' : 'https') : (isset($this->repoConfig['secure-http']) && $this->repoConfig['secure-http'] === false ? 'http' : 'https')
; ;
@ -557,8 +559,10 @@ class GitLabDriver extends VcsDriver
return false; return false;
} }
$scheme = !empty($match['scheme']) ? $match['scheme'] : null; assert(is_string($match['parts']));
$guessedDomain = !empty($match['domain']) ? $match['domain'] : $match['domain2']; assert(is_string($match['repo']));
$scheme = $match['scheme'];
$guessedDomain = $match['domain'] ?? (string) $match['domain2'];
$urlParts = explode('/', $match['parts']); $urlParts = explode('/', $match['parts']);
if (false === self::determineOrigin($config->get('gitlab-domains'), $guessedDomain, $urlParts, $match['port'])) { if (false === self::determineOrigin($config->get('gitlab-domains'), $guessedDomain, $urlParts, $match['port'])) {
@ -580,7 +584,7 @@ class GitLabDriver extends VcsDriver
$links = explode(',', $header); $links = explode(',', $header);
foreach ($links as $link) { foreach ($links as $link) {
if (Preg::isMatch('{<(.+?)>; *rel="next"}', $link, $match)) { if (Preg::isMatchStrictGroups('{<(.+?)>; *rel="next"}', $link, $match)) {
return $match[1]; return $match[1];
} }
} }

View File

@ -167,7 +167,7 @@ class HgDriver extends VcsDriver
$this->process->execute('hg tags', $output, $this->repoDir); $this->process->execute('hg tags', $output, $this->repoDir);
foreach ($this->process->splitLines($output) as $tag) { foreach ($this->process->splitLines($output) as $tag) {
if ($tag && Preg::isMatch('(^([^\s]+)\s+\d+:(.*)$)', $tag, $match)) { if ($tag && Preg::isMatchStrictGroups('(^([^\s]+)\s+\d+:(.*)$)', $tag, $match)) {
$tags[$match[1]] = $match[2]; $tags[$match[1]] = $match[2];
} }
} }
@ -190,14 +190,14 @@ class HgDriver extends VcsDriver
$this->process->execute('hg branches', $output, $this->repoDir); $this->process->execute('hg branches', $output, $this->repoDir);
foreach ($this->process->splitLines($output) as $branch) { foreach ($this->process->splitLines($output) as $branch) {
if ($branch && Preg::isMatch('(^([^\s]+)\s+\d+:([a-f0-9]+))', $branch, $match) && $match[1][0] !== '-') { if ($branch && Preg::isMatchStrictGroups('(^([^\s]+)\s+\d+:([a-f0-9]+))', $branch, $match) && $match[1][0] !== '-') {
$branches[$match[1]] = $match[2]; $branches[$match[1]] = $match[2];
} }
} }
$this->process->execute('hg bookmarks', $output, $this->repoDir); $this->process->execute('hg bookmarks', $output, $this->repoDir);
foreach ($this->process->splitLines($output) as $branch) { foreach ($this->process->splitLines($output) as $branch) {
if ($branch && Preg::isMatch('(^(?:[\s*]*)([^\s]+)\s+\d+:(.*)$)', $branch, $match) && $match[1][0] !== '-') { if ($branch && Preg::isMatchStrictGroups('(^(?:[\s*]*)([^\s]+)\s+\d+:(.*)$)', $branch, $match) && $match[1][0] !== '-') {
$bookmarks[$match[1]] = $match[2]; $bookmarks[$match[1]] = $match[2];
} }
} }

View File

@ -207,7 +207,7 @@ class SvnDriver extends VcsDriver
$identifier = '/' . trim($identifier, '/') . '/'; $identifier = '/' . trim($identifier, '/') . '/';
Preg::match('{^(.+?)(@\d+)?/$}', $identifier, $match); Preg::match('{^(.+?)(@\d+)?/$}', $identifier, $match);
if (!empty($match[2])) { if (null !== $match[2] && null !== $match[1]) {
$path = $match[1]; $path = $match[1];
$rev = $match[2]; $rev = $match[2];
} else { } else {
@ -217,7 +217,7 @@ class SvnDriver extends VcsDriver
$output = $this->execute('svn info', $this->baseUrl . $path . $rev); $output = $this->execute('svn info', $this->baseUrl . $path . $rev);
foreach ($this->process->splitLines($output) as $line) { foreach ($this->process->splitLines($output) as $line) {
if ($line && Preg::isMatch('{^Last Changed Date: ([^(]+)}', $line, $match)) { if ($line && Preg::isMatchStrictGroups('{^Last Changed Date: ([^(]+)}', $line, $match)) {
return new \DateTimeImmutable($match[1], new \DateTimeZone('UTC')); return new \DateTimeImmutable($match[1], new \DateTimeZone('UTC'));
} }
} }

View File

@ -564,7 +564,7 @@ class Filesystem
} }
// extract a prefix being a protocol://, protocol:, protocol://drive: or simply drive: // extract a prefix being a protocol://, protocol:, protocol://drive: or simply drive:
if (Preg::isMatch('{^( [0-9a-z]{2,}+: (?: // (?: [a-z]: )? )? | [a-z]: )}ix', $path, $match)) { if (Preg::isMatchStrictGroups('{^( [0-9a-z]{2,}+: (?: // (?: [a-z]: )? )? | [a-z]: )}ix', $path, $match)) {
$prefix = $match[1]; $prefix = $match[1];
$path = substr($path, \strlen($prefix)); $path = substr($path, \strlen($prefix));
} }

View File

@ -62,14 +62,14 @@ class Git
if (!$initialClone) { if (!$initialClone) {
// capture username/password from URL if there is one and we have no auth configured yet // capture username/password from URL if there is one and we have no auth configured yet
$this->process->execute('git remote -v', $output, $cwd); $this->process->execute('git remote -v', $output, $cwd);
if (Preg::isMatch('{^(?:composer|origin)\s+https?://(.+):(.+)@([^/]+)}im', $output, $match) && !$this->io->hasAuthentication($match[3])) { if (Preg::isMatchStrictGroups('{^(?:composer|origin)\s+https?://(.+):(.+)@([^/]+)}im', $output, $match) && !$this->io->hasAuthentication($match[3])) {
$this->io->setAuthentication($match[3], rawurldecode($match[1]), rawurldecode($match[2])); $this->io->setAuthentication($match[3], rawurldecode($match[1]), rawurldecode($match[2]));
} }
} }
$protocols = $this->config->get('github-protocols'); $protocols = $this->config->get('github-protocols');
// public github, autoswitch protocols // public github, autoswitch protocols
if (Preg::isMatch('{^(?:https?|git)://' . self::getGitHubDomainsRegex($this->config) . '/(.*)}', $url, $match)) { if (Preg::isMatchStrictGroups('{^(?:https?|git)://' . self::getGitHubDomainsRegex($this->config) . '/(.*)}', $url, $match)) {
$messages = []; $messages = [];
foreach ($protocols as $protocol) { foreach ($protocols as $protocol) {
if ('ssh' === $protocol) { if ('ssh' === $protocol) {
@ -104,8 +104,8 @@ class Git
if ($bypassSshForGitHub || 0 !== $this->process->execute($command, $commandOutput, $cwd)) { if ($bypassSshForGitHub || 0 !== $this->process->execute($command, $commandOutput, $cwd)) {
$errorMsg = $this->process->getErrorOutput(); $errorMsg = $this->process->getErrorOutput();
// private github repository without ssh key access, try https with auth // private github repository without ssh key access, try https with auth
if (Preg::isMatch('{^git@' . self::getGitHubDomainsRegex($this->config) . ':(.+?)\.git$}i', $url, $match) if (Preg::isMatchStrictGroups('{^git@' . self::getGitHubDomainsRegex($this->config) . ':(.+?)\.git$}i', $url, $match)
|| Preg::isMatch('{^https?://' . self::getGitHubDomainsRegex($this->config) . '/(.*?)(?:\.git)?$}i', $url, $match) || Preg::isMatchStrictGroups('{^https?://' . self::getGitHubDomainsRegex($this->config) . '/(.*?)(?:\.git)?$}i', $url, $match)
) { ) {
if (!$this->io->hasAuthentication($match[1])) { if (!$this->io->hasAuthentication($match[1])) {
$gitHubUtil = new GitHub($this->io, $this->config, $this->process); $gitHubUtil = new GitHub($this->io, $this->config, $this->process);
@ -127,7 +127,7 @@ class Git
$credentials = [rawurlencode($auth['username']), rawurlencode($auth['password'])]; $credentials = [rawurlencode($auth['username']), rawurlencode($auth['password'])];
$errorMsg = $this->process->getErrorOutput(); $errorMsg = $this->process->getErrorOutput();
} }
} elseif (Preg::isMatch('{^https://(bitbucket\.org)/(.*?)(?:\.git)?$}i', $url, $match)) { //bitbucket oauth } elseif (Preg::isMatchStrictGroups('{^https://(bitbucket\.org)/(.*?)(?:\.git)?$}i', $url, $match)) { //bitbucket oauth
$bitbucketUtil = new Bitbucket($this->io, $this->config, $this->process); $bitbucketUtil = new Bitbucket($this->io, $this->config, $this->process);
if (!$this->io->hasAuthentication($match[1])) { if (!$this->io->hasAuthentication($match[1])) {
@ -172,8 +172,8 @@ class Git
$errorMsg = $this->process->getErrorOutput(); $errorMsg = $this->process->getErrorOutput();
} }
} elseif ( } elseif (
Preg::isMatch('{^(git)@' . self::getGitLabDomainsRegex($this->config) . ':(.+?\.git)$}i', $url, $match) Preg::isMatchStrictGroups('{^(git)@' . self::getGitLabDomainsRegex($this->config) . ':(.+?\.git)$}i', $url, $match)
|| Preg::isMatch('{^(https?)://' . self::getGitLabDomainsRegex($this->config) . '/(.*)}i', $url, $match) || Preg::isMatchStrictGroups('{^(https?)://' . self::getGitLabDomainsRegex($this->config) . '/(.*)}i', $url, $match)
) { ) {
if ($match[1] === 'git') { if ($match[1] === 'git') {
$match[1] = 'https'; $match[1] = 'https';

View File

@ -58,9 +58,9 @@ class Hg
} }
// Try with the authentication information available // Try with the authentication information available
if (Preg::isMatch('{^(https?)://((.+)(?:\:(.+))?@)?([^/]+)(/.*)?}mi', $url, $match) && $this->io->hasAuthentication($match[5])) { if (Preg::isMatch('{^(https?)://((.+)(?:\:(.+))?@)?([^/]+)(/.*)?}mi', $url, $match) && $this->io->hasAuthentication((string) $match[5])) {
$auth = $this->io->getAuthentication($match[5]); $auth = $this->io->getAuthentication((string) $match[5]);
$authenticatedUrl = $match[1] . '://' . rawurlencode($auth['username']) . ':' . rawurlencode($auth['password']) . '@' . $match[5] . (!empty($match[6]) ? $match[6] : null); $authenticatedUrl = $match[1] . '://' . rawurlencode($auth['username']) . ':' . rawurlencode($auth['password']) . '@' . $match[5] . $match[6];
$command = $commandCallable($authenticatedUrl); $command = $commandCallable($authenticatedUrl);

View File

@ -218,7 +218,7 @@ class HttpDownloader
} }
// capture username/password from URL if there is one // capture username/password from URL if there is one
if (Preg::isMatch('{^https?://([^:/]+):([^@/]+)@([^/]+)}i', $request['url'], $match)) { if (Preg::isMatchStrictGroups('{^https?://([^:/]+):([^@/]+)@([^/]+)}i', $request['url'], $match)) {
$this->io->setAuthentication($job['origin'], rawurldecode($match[1]), rawurldecode($match[2])); $this->io->setAuthentication($job['origin'], rawurldecode($match[1]), rawurldecode($match[2]));
} }

View File

@ -460,7 +460,7 @@ class ProcessExecutor
// In addition to whitespace, commas need quoting to preserve paths // In addition to whitespace, commas need quoting to preserve paths
$quote = strpbrk($argument, " \t,") !== false; $quote = strpbrk($argument, " \t,") !== false;
$argument = Preg::replace('/(\\\\*)"/', '$1$1\\"', $argument, -1, $dquotes); $argument = Preg::replace('/(\\\\*)"/', '$1$1\\"', $argument, -1, $dquotes);
$meta = $dquotes || Preg::isMatch('/%[^%]+%|![^!]+!/', $argument); $meta = $dquotes > 0 || Preg::isMatch('/%[^%]+%|![^!]+!/', $argument);
if (!$meta && !$quote) { if (!$meta && !$quote) {
$quote = strpbrk($argument, '^&|<>()') !== false; $quote = strpbrk($argument, '^&|<>()') !== false;

View File

@ -135,12 +135,12 @@ class AllFunctionalTest extends TestCase
$line++; $line++;
} }
if ($expected[$i] === '%') { if ($expected[$i] === '%') {
Preg::isMatch('{%(.+?)%}', substr($expected, $i), $match); Preg::isMatchStrictGroups('{%(.+?)%}', substr($expected, $i), $match);
$regex = $match[1]; $regex = $match[1];
if (Preg::isMatch('{'.$regex.'}', substr($output, $j), $match)) { if (Preg::isMatch('{'.$regex.'}', substr($output, $j), $match)) {
$i += strlen($regex) + 2; $i += strlen($regex) + 2;
$j += strlen($match[0]); $j += strlen((string) $match[0]);
continue; continue;
} else { } else {
$this->fail( $this->fail(