提交的内容

This commit is contained in:
2025-05-12 15:45:02 +08:00
parent 629c4750da
commit b48c692775
3043 changed files with 34732 additions and 60810 deletions

View File

@ -42,76 +42,30 @@ namespace Composer\Autoload;
*/
class ClassLoader
{
/** @var \Closure(string):void */
private static $includeFile;
/** @var string|null */
private $vendorDir;
// PSR-4
/**
* @var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array<string, list<string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* List of PSR-0 prefixes
*
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
*
* @var array<string, array<string, list<string>>>
*/
private $prefixesPsr0 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var array<string, bool>
*/
private $missingClasses = array();
/** @var string|null */
private $apcuPrefix;
/**
* @var array<string, self>
*/
private static $registeredLoaders = array();
/**
* @param string|null $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
@ -121,42 +75,28 @@ class ClassLoader
return array();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return list<string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return list<string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return array<string, string> Array of classname => path
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array<string, string> $classMap Class to filename map
*
* @return void
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
@ -171,25 +111,22 @@ class ClassLoader
* 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 list<string>|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
$paths,
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
$paths
(array) $paths
);
}
@ -198,19 +135,19 @@ class ClassLoader
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = $paths;
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$paths,
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
$paths
(array) $paths
);
}
}
@ -219,28 +156,25 @@ class ClassLoader
* 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 list<string>|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
* @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
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
$paths,
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
$paths
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
@ -250,18 +184,18 @@ class ClassLoader
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = $paths;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$paths,
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
$paths
(array) $paths
);
}
}
@ -270,10 +204,8 @@ class ClassLoader
* 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 list<string>|string $paths The PSR-0 base directories
*
* @return void
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
@ -288,12 +220,10 @@ class ClassLoader
* 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 list<string>|string $paths The PSR-4 base directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
@ -313,8 +243,6 @@ class ClassLoader
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
@ -337,8 +265,6 @@ class ClassLoader
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
@ -359,8 +285,6 @@ class ClassLoader
* 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)
{
@ -381,8 +305,6 @@ class ClassLoader
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
@ -402,8 +324,6 @@ class ClassLoader
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
@ -418,18 +338,15 @@ class ClassLoader
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
$includeFile = self::$includeFile;
$includeFile($file);
includeFile($file);
return true;
}
return null;
}
/**
@ -475,20 +392,15 @@ class ClassLoader
}
/**
* Returns the currently registered loaders keyed by their corresponding vendor directories.
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return array<string, self>
* @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
@ -554,26 +466,14 @@ class ClassLoader
return false;
}
/**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = \Closure::bind(static function($file) {
include $file;
}, null, null);
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

File diff suppressed because it is too large Load Diff

View File

@ -2,17 +2,18 @@
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'CURLStringFile' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'Requests' => $vendorDir . '/rmccue/requests/library/Requests.php',
'ReturnTypeWillChange' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php',
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'Stringable' => $vendorDir . '/myclabs/php-enum/stubs/Stringable.php',
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
);

View File

@ -2,22 +2,23 @@
// autoload_files.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
'9b552a3cc426e3287cc811caefa3cf53' => $vendorDir . '/topthink/think-helper/src/helper.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'd767e4fc2dc52fe66584ab8c6684783e' => $vendorDir . '/adbario/php-dot-notation/src/helpers.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'35fab96057f1bf5e7aba31a8a6d5fdde' => $vendorDir . '/topthink/think-orm/stubs/load_stubs.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'15ec93fa4ce4b2d53816a1a5f2c514e2' => $vendorDir . '/topthink/think-validate/src/helper.php',
'2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
'b067bc7112e384b61c701452d53a14a8' => $vendorDir . '/mtdowling/jmespath.php/src/JmesPath.php',
'c5521cebe610a9bf42c44b3a5163adfd' => $vendorDir . '/overtrue/socialite/src/Contracts/FactoryInterface.php',
@ -28,5 +29,4 @@ return array(
'cd5441689b14144e5573bf989ee47b34' => $vendorDir . '/qcloud/cos-sdk-v5/src/Common.php',
'841780ea2e1d6545ea3a253239d59c05' => $vendorDir . '/qiniu/php-sdk/src/Qiniu/functions.php',
'941748b3c8cae4466c827dfb5ca9602a' => $vendorDir . '/rmccue/requests/library/Deprecated.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
);

View File

@ -2,7 +2,7 @@
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(

View File

@ -2,14 +2,14 @@
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'think\\view\\driver\\' => array($vendorDir . '/topthink/think-view/src'),
'think\\trace\\' => array($vendorDir . '/topthink/think-trace/src'),
'think\\app\\' => array($vendorDir . '/topthink/think-multi-app/src'),
'think\\' => array($vendorDir . '/topthink/framework/src/think', $vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-orm/src', $vendorDir . '/topthink/think-template/src'),
'think\\' => array($vendorDir . '/topthink/framework/src/think', $vendorDir . '/topthink/think-container/src', $vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-orm/src', $vendorDir . '/topthink/think-template/src', $vendorDir . '/topthink/think-validate/src'),
'clagiordano\\weblibs\\configmanager\\' => array($vendorDir . '/clagiordano/weblibs-configmanager/src'),
'app\\' => array($baseDir . '/app'),
'ZipStream\\' => array($vendorDir . '/maennchen/zipstream-php/src'),
@ -19,7 +19,6 @@ return array(
'TencentCloud\\' => array($vendorDir . '/tencentcloud/common/src/TencentCloud', $vendorDir . '/tencentcloud/sms/src/TencentCloud'),
'Symfony\\Polyfill\\Php81\\' => array($vendorDir . '/symfony/polyfill-php81'),
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
@ -49,7 +48,6 @@ return array(
'Nyholm\\Psr7\\' => array($vendorDir . '/nyholm/psr7/src'),
'Nyholm\\Psr7Server\\' => array($vendorDir . '/nyholm/psr7-server/src'),
'MyCLabs\\Enum\\' => array($vendorDir . '/myclabs/php-enum/src'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
'Matrix\\' => array($vendorDir . '/markbaker/matrix/classes/src'),
'JmesPath\\' => array($vendorDir . '/mtdowling/jmespath.php/src'),
'GuzzleHttp\\UriTemplate\\' => array($vendorDir . '/guzzlehttp/uri-template/src'),
@ -60,6 +58,7 @@ return array(
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
'EasyWeChat\\' => array($vendorDir . '/w7corp/easywechat/src'),
'Cron\\' => array($vendorDir . '/dragonmantank/cron-expression/src/Cron'),
'Composer\\Pcre\\' => array($vendorDir . '/composer/pcre/src'),
'Complex\\' => array($vendorDir . '/markbaker/complex/classes/src'),
'Alipay\\EasySDK\\' => array($vendorDir . '/alipaysdk/easysdk/php/src'),
'AlibabaCloud\\Tea\\FileForm\\' => array($vendorDir . '/alibabacloud/tea-fileform/src'),

View File

@ -25,26 +25,51 @@ class ComposerAutoloaderInitd2a74ba94e266cc4f45a64c54a292d7e
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInitd2a74ba94e266cc4f45a64c54a292d7e', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInitd2a74ba94e266cc4f45a64c54a292d7e', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e::getInitializer($loader));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}, null, null);
foreach ($filesToLoad as $fileIdentifier => $file) {
$requireFile($fileIdentifier, $file);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequired2a74ba94e266cc4f45a64c54a292d7e($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequired2a74ba94e266cc4f45a64c54a292d7e($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}

View File

@ -7,18 +7,19 @@ namespace Composer\Autoload;
class ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e
{
public static $files = array (
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
'9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
'd767e4fc2dc52fe66584ab8c6684783e' => __DIR__ . '/..' . '/adbario/php-dot-notation/src/helpers.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'35fab96057f1bf5e7aba31a8a6d5fdde' => __DIR__ . '/..' . '/topthink/think-orm/stubs/load_stubs.php',
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
'15ec93fa4ce4b2d53816a1a5f2c514e2' => __DIR__ . '/..' . '/topthink/think-validate/src/helper.php',
'2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
'b067bc7112e384b61c701452d53a14a8' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/JmesPath.php',
'c5521cebe610a9bf42c44b3a5163adfd' => __DIR__ . '/..' . '/overtrue/socialite/src/Contracts/FactoryInterface.php',
@ -29,7 +30,6 @@ class ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e
'cd5441689b14144e5573bf989ee47b34' => __DIR__ . '/..' . '/qcloud/cos-sdk-v5/src/Common.php',
'841780ea2e1d6545ea3a253239d59c05' => __DIR__ . '/..' . '/qiniu/php-sdk/src/Qiniu/functions.php',
'941748b3c8cae4466c827dfb5ca9602a' => __DIR__ . '/..' . '/rmccue/requests/library/Deprecated.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
);
public static $prefixLengthsPsr4 = array (
@ -66,7 +66,6 @@ class ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e
array (
'Symfony\\Polyfill\\Php81\\' => 23,
'Symfony\\Polyfill\\Php80\\' => 23,
'Symfony\\Polyfill\\Php72\\' => 23,
'Symfony\\Polyfill\\Mbstring\\' => 26,
'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
'Symfony\\Polyfill\\Intl\\Idn\\' => 26,
@ -111,7 +110,6 @@ class ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e
'M' =>
array (
'MyCLabs\\Enum\\' => 13,
'Monolog\\' => 8,
'Matrix\\' => 7,
),
'J' =>
@ -134,6 +132,7 @@ class ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e
'C' =>
array (
'Cron\\' => 5,
'Composer\\Pcre\\' => 14,
'Complex\\' => 8,
),
'A' =>
@ -162,9 +161,11 @@ class ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e
'think\\' =>
array (
0 => __DIR__ . '/..' . '/topthink/framework/src/think',
1 => __DIR__ . '/..' . '/topthink/think-helper/src',
2 => __DIR__ . '/..' . '/topthink/think-orm/src',
3 => __DIR__ . '/..' . '/topthink/think-template/src',
1 => __DIR__ . '/..' . '/topthink/think-container/src',
2 => __DIR__ . '/..' . '/topthink/think-helper/src',
3 => __DIR__ . '/..' . '/topthink/think-orm/src',
4 => __DIR__ . '/..' . '/topthink/think-template/src',
5 => __DIR__ . '/..' . '/topthink/think-validate/src',
),
'clagiordano\\weblibs\\configmanager\\' =>
array (
@ -203,10 +204,6 @@ class ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
),
'Symfony\\Polyfill\\Php72\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php72',
),
'Symfony\\Polyfill\\Mbstring\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
@ -324,10 +321,6 @@ class ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e
array (
0 => __DIR__ . '/..' . '/myclabs/php-enum/src',
),
'Monolog\\' =>
array (
0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog',
),
'Matrix\\' =>
array (
0 => __DIR__ . '/..' . '/markbaker/matrix/classes/src',
@ -368,6 +361,10 @@ class ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e
array (
0 => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron',
),
'Composer\\Pcre\\' =>
array (
0 => __DIR__ . '/..' . '/composer/pcre/src',
),
'Complex\\' =>
array (
0 => __DIR__ . '/..' . '/markbaker/complex/classes/src',
@ -410,12 +407,13 @@ class ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e
public static $classMap = array (
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'CURLStringFile' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'Requests' => __DIR__ . '/..' . '/rmccue/requests/library/Requests.php',
'ReturnTypeWillChange' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php',
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'Stringable' => __DIR__ . '/..' . '/myclabs/php-enum/stubs/Stringable.php',
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

19
vendor/composer/pcre/LICENSE vendored Normal file
View File

@ -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.

189
vendor/composer/pcre/README.md vendored Normal file
View File

@ -0,0 +1,189 @@
composer/pcre
=============
PCRE wrapping library that offers type-safe `preg_*` replacements.
This library gives you a way to ensure `preg_*` functions do not fail silently, returning
unexpected `null`s that may not be handled.
As of 3.0 this library enforces [`PREG_UNMATCHED_AS_NULL`](#preg_unmatched_as_null) usage
for all matching and replaceCallback functions, [read more below](#preg_unmatched_as_null)
to understand the implications.
It thus makes it easier to work with static analysis tools like PHPStan or Psalm as it
simplifies and reduces the possible return values from all the `preg_*` functions which
are quite packed with edge cases. As of v2.2.0 / v3.2.0 the library also comes with a
[PHPStan extension](#phpstan-extension) for parsing regular expressions and giving you even better output types.
This library is a thin wrapper around `preg_*` functions with [some limitations](#restrictions--limitations).
If you are looking for a richer API to handle regular expressions have a look at
[rawr/t-regx](https://packagist.org/packages/rawr/t-regx) instead.
[![Continuous Integration](https://github.com/composer/pcre/workflows/Continuous%20Integration/badge.svg?branch=main)](https://github.com/composer/pcre/actions)
Installation
------------
Install the latest version with:
```bash
$ composer require composer/pcre
```
Requirements
------------
* PHP 7.4.0 is required for 3.x versions
* PHP 7.2.0 is required for 2.x versions
* PHP 5.3.2 is required for 1.x versions
Basic usage
-----------
Instead of:
```php
if (preg_match('{fo+}', $string, $matches)) { ... }
if (preg_match('{fo+}', $string, $matches, PREG_OFFSET_CAPTURE)) { ... }
if (preg_match_all('{fo+}', $string, $matches)) { ... }
$newString = preg_replace('{fo+}', 'bar', $string);
$newString = preg_replace_callback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string);
$newString = preg_replace_callback_array(['{fo+}' => fn ($match) => strtoupper($match[0])], $string);
$filtered = preg_grep('{[a-z]}', $elements);
$array = preg_split('{[a-z]+}', $string);
```
You can now call these on the `Preg` class:
```php
use Composer\Pcre\Preg;
if (Preg::match('{fo+}', $string, $matches)) { ... }
if (Preg::matchWithOffsets('{fo+}', $string, $matches)) { ... }
if (Preg::matchAll('{fo+}', $string, $matches)) { ... }
$newString = Preg::replace('{fo+}', 'bar', $string);
$newString = Preg::replaceCallback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string);
$newString = Preg::replaceCallbackArray(['{fo+}' => fn ($match) => strtoupper($match[0])], $string);
$filtered = Preg::grep('{[a-z]}', $elements);
$array = Preg::split('{[a-z]+}', $string);
```
The main difference is if anything fails to match/replace/.., it will throw a `Composer\Pcre\PcreException`
instead of returning `null` (or false in some cases), so you can now use the return values safely relying on
the fact that they can only be strings (for replace), ints (for match) or arrays (for grep/split).
Additionally the `Preg` class provides match methods that return `bool` rather than `int`, for stricter type safety
when the number of pattern matches is not useful:
```php
use Composer\Pcre\Preg;
if (Preg::isMatch('{fo+}', $string, $matches)) // bool
if (Preg::isMatchAll('{fo+}', $string, $matches)) // bool
```
Finally the `Preg` class provides a few `*StrictGroups` method variants that ensure match groups
are always present and thus non-nullable, making it easier to write type-safe code:
```php
use Composer\Pcre\Preg;
// $matches is guaranteed to be an array of strings, if a subpattern does not match and produces a null it will throw
if (Preg::matchStrictGroups('{fo+}', $string, $matches))
if (Preg::matchAllStrictGroups('{fo+}', $string, $matches))
```
**Note:** This is generally safe to use as long as you do not have optional subpatterns (i.e. `(something)?`
or `(something)*` or branches with a `|` that result in some groups not being matched at all).
A subpattern that can match an empty string like `(.*)` is **not** optional, it will be present as an
empty string in the matches. A non-matching subpattern, even if optional like `(?:foo)?` will anyway not be present in
matches so it is also not a problem to use these with `*StrictGroups` methods.
If you would prefer a slightly more verbose usage, replacing by-ref arguments by result objects, you can use the `Regex` class:
```php
use Composer\Pcre\Regex;
// this is useful when you are just interested in knowing if something matched
// as it returns a bool instead of int(1/0) for match
$bool = Regex::isMatch('{fo+}', $string);
$result = Regex::match('{fo+}', $string);
if ($result->matched) { something($result->matches); }
$result = Regex::matchWithOffsets('{fo+}', $string);
if ($result->matched) { something($result->matches); }
$result = Regex::matchAll('{fo+}', $string);
if ($result->matched && $result->count > 3) { something($result->matches); }
$newString = Regex::replace('{fo+}', 'bar', $string)->result;
$newString = Regex::replaceCallback('{fo+}', function ($match) { return strtoupper($match[0]); }, $string)->result;
$newString = Regex::replaceCallbackArray(['{fo+}' => fn ($match) => strtoupper($match[0])], $string)->result;
```
Note that `preg_grep` and `preg_split` are only callable via the `Preg` class as they do not have
complex return types warranting a specific result object.
See the [MatchResult](src/MatchResult.php), [MatchWithOffsetsResult](src/MatchWithOffsetsResult.php), [MatchAllResult](src/MatchAllResult.php),
[MatchAllWithOffsetsResult](src/MatchAllWithOffsetsResult.php), and [ReplaceResult](src/ReplaceResult.php) class sources for more details.
Restrictions / Limitations
--------------------------
Due to type safety requirements a few restrictions are in place.
- matching using `PREG_OFFSET_CAPTURE` is made available via `matchWithOffsets` and `matchAllWithOffsets`.
You cannot pass the flag to `match`/`matchAll`.
- `Preg::split` will also reject `PREG_SPLIT_OFFSET_CAPTURE` and you should use `splitWithOffsets`
instead.
- `matchAll` rejects `PREG_SET_ORDER` as it also changes the shape of the returned matches. There
is no alternative provided as you can fairly easily code around it.
- `preg_filter` is not supported as it has a rather crazy API, most likely you should rather
use `Preg::grep` in combination with some loop and `Preg::replace`.
- `replace`, `replaceCallback` and `replaceCallbackArray` do not support an array `$subject`,
only simple strings.
- As of 2.0, the library always uses `PREG_UNMATCHED_AS_NULL` for matching, which offers [much
saner/more predictable results](#preg_unmatched_as_null). As of 3.0 the flag is also set for
`replaceCallback` and `replaceCallbackArray`.
#### PREG_UNMATCHED_AS_NULL
As of 2.0, this library always uses PREG_UNMATCHED_AS_NULL for all `match*` and `isMatch*`
functions. As of 3.0 it is also done for `replaceCallback` and `replaceCallbackArray`.
This means your matches will always contain all matching groups, either as null if unmatched
or as string if it matched.
The advantages in clarity and predictability are clearer if you compare the two outputs of
running this with and without PREG_UNMATCHED_AS_NULL in $flags:
```php
preg_match('/(a)(b)*(c)(d)*/', 'ac', $matches, $flags);
```
| no flag | PREG_UNMATCHED_AS_NULL |
| --- | --- |
| array (size=4) | array (size=5) |
| 0 => string 'ac' (length=2) | 0 => string 'ac' (length=2) |
| 1 => string 'a' (length=1) | 1 => string 'a' (length=1) |
| 2 => string '' (length=0) | 2 => null |
| 3 => string 'c' (length=1) | 3 => string 'c' (length=1) |
| | 4 => null |
| group 2 (any unmatched group preceding one that matched) is set to `''`. You cannot tell if it matched an empty string or did not match at all | group 2 is `null` when unmatched and a string if it matched, easy to check for |
| group 4 (any optional group without a matching one following) is missing altogether. So you have to check with `isset()`, but really you want `isset($m[4]) && $m[4] !== ''` for safety unless you are very careful to check that a non-optional group follows it | group 4 is always set, and null in this case as there was no match, easy to check for with `$m[4] !== null` |
PHPStan Extension
-----------------
To use the PHPStan extension if you do not use `phpstan/extension-installer` you can include `vendor/composer/pcre/extension.neon` in your PHPStan config.
The extension provides much better type information for $matches as well as regex validation where possible.
License
-------
composer/pcre is licensed under the MIT License, see the LICENSE file for details.

54
vendor/composer/pcre/composer.json vendored Normal file
View File

@ -0,0 +1,54 @@
{
"name": "composer/pcre",
"description": "PCRE wrapping library that offers type-safe preg_* replacements.",
"type": "library",
"license": "MIT",
"keywords": [
"pcre",
"regex",
"preg",
"regular expression"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"require": {
"php": "^7.4 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^8 || ^9",
"phpstan/phpstan": "^1.12 || ^2",
"phpstan/phpstan-strict-rules": "^1 || ^2"
},
"conflict": {
"phpstan/phpstan": "<1.11.10"
},
"autoload": {
"psr-4": {
"Composer\\Pcre\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Composer\\Pcre\\": "tests"
}
},
"extra": {
"branch-alias": {
"dev-main": "3.x-dev"
},
"phpstan": {
"includes": [
"extension.neon"
]
}
},
"scripts": {
"test": "@php vendor/bin/phpunit",
"phpstan": "@php phpstan analyse"
}
}

22
vendor/composer/pcre/extension.neon vendored Normal file
View File

@ -0,0 +1,22 @@
# composer/pcre PHPStan extensions
#
# These can be reused by third party packages by including 'vendor/composer/pcre/extension.neon'
# in your phpstan config
services:
-
class: Composer\Pcre\PHPStan\PregMatchParameterOutTypeExtension
tags:
- phpstan.staticMethodParameterOutTypeExtension
-
class: Composer\Pcre\PHPStan\PregMatchTypeSpecifyingExtension
tags:
- phpstan.typeSpecifier.staticMethodTypeSpecifyingExtension
-
class: Composer\Pcre\PHPStan\PregReplaceCallbackClosureTypeExtension
tags:
- phpstan.staticMethodParameterClosureTypeExtension
rules:
- Composer\Pcre\PHPStan\UnsafeStrictGroupsCallRule
- Composer\Pcre\PHPStan\InvalidRegexPatternRule

View File

@ -0,0 +1,46 @@
<?php
/*
* This file is part of composer/pcre.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\Pcre;
final class MatchAllResult
{
/**
* An array of match group => list of matched strings
*
* @readonly
* @var array<int|string, list<string|null>>
*/
public $matches;
/**
* @readonly
* @var 0|positive-int
*/
public $count;
/**
* @readonly
* @var bool
*/
public $matched;
/**
* @param 0|positive-int $count
* @param array<int|string, list<string|null>> $matches
*/
public function __construct(int $count, array $matches)
{
$this->matches = $matches;
$this->matched = (bool) $count;
$this->count = $count;
}
}

View File

@ -0,0 +1,46 @@
<?php
/*
* This file is part of composer/pcre.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\Pcre;
final class MatchAllStrictGroupsResult
{
/**
* An array of match group => list of matched strings
*
* @readonly
* @var array<int|string, list<string>>
*/
public $matches;
/**
* @readonly
* @var 0|positive-int
*/
public $count;
/**
* @readonly
* @var bool
*/
public $matched;
/**
* @param 0|positive-int $count
* @param array<list<string>> $matches
*/
public function __construct(int $count, array $matches)
{
$this->matches = $matches;
$this->matched = (bool) $count;
$this->count = $count;
}
}

View File

@ -0,0 +1,48 @@
<?php
/*
* This file is part of composer/pcre.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\Pcre;
final class MatchAllWithOffsetsResult
{
/**
* An array of match group => list of matches, every match being a pair of string matched + offset in bytes (or -1 if no match)
*
* @readonly
* @var array<int|string, list<array{string|null, int}>>
* @phpstan-var array<int|string, list<array{string|null, int<-1, max>}>>
*/
public $matches;
/**
* @readonly
* @var 0|positive-int
*/
public $count;
/**
* @readonly
* @var bool
*/
public $matched;
/**
* @param 0|positive-int $count
* @param array<int|string, list<array{string|null, int}>> $matches
* @phpstan-param array<int|string, list<array{string|null, int<-1, max>}>> $matches
*/
public function __construct(int $count, array $matches)
{
$this->matches = $matches;
$this->matched = (bool) $count;
$this->count = $count;
}
}

View File

@ -0,0 +1,39 @@
<?php
/*
* This file is part of composer/pcre.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\Pcre;
final class MatchResult
{
/**
* An array of match group => string matched
*
* @readonly
* @var array<int|string, string|null>
*/
public $matches;
/**
* @readonly
* @var bool
*/
public $matched;
/**
* @param 0|positive-int $count
* @param array<string|null> $matches
*/
public function __construct(int $count, array $matches)
{
$this->matches = $matches;
$this->matched = (bool) $count;
}
}

View File

@ -0,0 +1,39 @@
<?php
/*
* This file is part of composer/pcre.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\Pcre;
final class MatchStrictGroupsResult
{
/**
* An array of match group => string matched
*
* @readonly
* @var array<int|string, string>
*/
public $matches;
/**
* @readonly
* @var bool
*/
public $matched;
/**
* @param 0|positive-int $count
* @param array<string> $matches
*/
public function __construct(int $count, array $matches)
{
$this->matches = $matches;
$this->matched = (bool) $count;
}
}

View File

@ -0,0 +1,41 @@
<?php
/*
* This file is part of composer/pcre.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\Pcre;
final class MatchWithOffsetsResult
{
/**
* An array of match group => pair of string matched + offset in bytes (or -1 if no match)
*
* @readonly
* @var array<int|string, array{string|null, int}>
* @phpstan-var array<int|string, array{string|null, int<-1, max>}>
*/
public $matches;
/**
* @readonly
* @var bool
*/
public $matched;
/**
* @param 0|positive-int $count
* @param array<array{string|null, int}> $matches
* @phpstan-param array<int|string, array{string|null, int<-1, max>}> $matches
*/
public function __construct(int $count, array $matches)
{
$this->matches = $matches;
$this->matched = (bool) $count;
}
}

View File

@ -0,0 +1,142 @@
<?php declare(strict_types = 1);
namespace Composer\Pcre\PHPStan;
use Composer\Pcre\Preg;
use Composer\Pcre\Regex;
use Composer\Pcre\PcreException;
use Nette\Utils\RegexpException;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name\FullyQualified;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use function in_array;
use function sprintf;
/**
* Copy of PHPStan's RegularExpressionPatternRule
*
* @implements Rule<StaticCall>
*/
class InvalidRegexPatternRule implements Rule
{
public function getNodeType(): string
{
return StaticCall::class;
}
public function processNode(Node $node, Scope $scope): array
{
$patterns = $this->extractPatterns($node, $scope);
$errors = [];
foreach ($patterns as $pattern) {
$errorMessage = $this->validatePattern($pattern);
if ($errorMessage === null) {
continue;
}
$errors[] = RuleErrorBuilder::message(sprintf('Regex pattern is invalid: %s', $errorMessage))->identifier('regexp.pattern')->build();
}
return $errors;
}
/**
* @return string[]
*/
private function extractPatterns(StaticCall $node, Scope $scope): array
{
if (!$node->class instanceof FullyQualified) {
return [];
}
$isRegex = $node->class->toString() === Regex::class;
$isPreg = $node->class->toString() === Preg::class;
if (!$isRegex && !$isPreg) {
return [];
}
if (!$node->name instanceof Node\Identifier || !Preg::isMatch('{^(match|isMatch|grep|replace|split)}', $node->name->name)) {
return [];
}
$functionName = $node->name->name;
if (!isset($node->getArgs()[0])) {
return [];
}
$patternNode = $node->getArgs()[0]->value;
$patternType = $scope->getType($patternNode);
$patternStrings = [];
foreach ($patternType->getConstantStrings() as $constantStringType) {
if ($functionName === 'replaceCallbackArray') {
continue;
}
$patternStrings[] = $constantStringType->getValue();
}
foreach ($patternType->getConstantArrays() as $constantArrayType) {
if (
in_array($functionName, [
'replace',
'replaceCallback',
], true)
) {
foreach ($constantArrayType->getValueTypes() as $arrayKeyType) {
foreach ($arrayKeyType->getConstantStrings() as $constantString) {
$patternStrings[] = $constantString->getValue();
}
}
}
if ($functionName !== 'replaceCallbackArray') {
continue;
}
foreach ($constantArrayType->getKeyTypes() as $arrayKeyType) {
foreach ($arrayKeyType->getConstantStrings() as $constantString) {
$patternStrings[] = $constantString->getValue();
}
}
}
return $patternStrings;
}
private function validatePattern(string $pattern): ?string
{
try {
$msg = null;
$prev = set_error_handler(function (int $severity, string $message, string $file) use (&$msg): bool {
$msg = preg_replace("#^preg_match(_all)?\\(.*?\\): #", '', $message);
return true;
});
if ($pattern === '') {
return 'Empty string is not a valid regular expression';
}
Preg::match($pattern, '');
if ($msg !== null) {
return $msg;
}
} catch (PcreException $e) {
if ($e->getCode() === PREG_INTERNAL_ERROR && $msg !== null) {
return $msg;
}
return preg_replace('{.*? failed executing ".*": }', '', $e->getMessage());
} finally {
restore_error_handler();
}
return null;
}
}

View File

@ -0,0 +1,70 @@
<?php declare(strict_types=1);
namespace Composer\Pcre\PHPStan;
use PHPStan\Analyser\Scope;
use PHPStan\Type\ArrayType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\Type;
use PhpParser\Node\Arg;
use PHPStan\Type\Php\RegexArrayShapeMatcher;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\UnionType;
final class PregMatchFlags
{
static public function getType(?Arg $flagsArg, Scope $scope): ?Type
{
if ($flagsArg === null) {
return new ConstantIntegerType(PREG_UNMATCHED_AS_NULL);
}
$flagsType = $scope->getType($flagsArg->value);
$constantScalars = $flagsType->getConstantScalarValues();
if ($constantScalars === []) {
return null;
}
$internalFlagsTypes = [];
foreach ($flagsType->getConstantScalarValues() as $constantScalarValue) {
if (!is_int($constantScalarValue)) {
return null;
}
$internalFlagsTypes[] = new ConstantIntegerType($constantScalarValue | PREG_UNMATCHED_AS_NULL);
}
return TypeCombinator::union(...$internalFlagsTypes);
}
static public function removeNullFromMatches(Type $matchesType): Type
{
return TypeTraverser::map($matchesType, static function (Type $type, callable $traverse): Type {
if ($type instanceof UnionType || $type instanceof IntersectionType) {
return $traverse($type);
}
if ($type instanceof ConstantArrayType) {
return new ConstantArrayType(
$type->getKeyTypes(),
array_map(static function (Type $valueType) use ($traverse): Type {
return $traverse($valueType);
}, $type->getValueTypes()),
$type->getNextAutoIndexes(),
[],
$type->isList()
);
}
if ($type instanceof ArrayType) {
return new ArrayType($type->getKeyType(), $traverse($type->getItemType()));
}
return TypeCombinator::removeNull($type);
});
}
}

View File

@ -0,0 +1,65 @@
<?php declare(strict_types=1);
namespace Composer\Pcre\PHPStan;
use Composer\Pcre\Preg;
use PhpParser\Node\Expr\StaticCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParameterReflection;
use PHPStan\TrinaryLogic;
use PHPStan\Type\Php\RegexArrayShapeMatcher;
use PHPStan\Type\StaticMethodParameterOutTypeExtension;
use PHPStan\Type\Type;
final class PregMatchParameterOutTypeExtension implements StaticMethodParameterOutTypeExtension
{
/**
* @var RegexArrayShapeMatcher
*/
private $regexShapeMatcher;
public function __construct(
RegexArrayShapeMatcher $regexShapeMatcher
)
{
$this->regexShapeMatcher = $regexShapeMatcher;
}
public function isStaticMethodSupported(MethodReflection $methodReflection, ParameterReflection $parameter): bool
{
return
$methodReflection->getDeclaringClass()->getName() === Preg::class
&& in_array($methodReflection->getName(), [
'match', 'isMatch', 'matchStrictGroups', 'isMatchStrictGroups',
'matchAll', 'isMatchAll', 'matchAllStrictGroups', 'isMatchAllStrictGroups'
], true)
&& $parameter->getName() === 'matches';
}
public function getParameterOutTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, ParameterReflection $parameter, Scope $scope): ?Type
{
$args = $methodCall->getArgs();
$patternArg = $args[0] ?? null;
$matchesArg = $args[2] ?? null;
$flagsArg = $args[3] ?? null;
if (
$patternArg === null || $matchesArg === null
) {
return null;
}
$flagsType = PregMatchFlags::getType($flagsArg, $scope);
if ($flagsType === null) {
return null;
}
if (stripos($methodReflection->getName(), 'matchAll') !== false) {
return $this->regexShapeMatcher->matchAllExpr($patternArg->value, $flagsType, TrinaryLogic::createMaybe(), $scope);
}
return $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createMaybe(), $scope);
}
}

View File

@ -0,0 +1,119 @@
<?php declare(strict_types=1);
namespace Composer\Pcre\PHPStan;
use Composer\Pcre\Preg;
use PhpParser\Node\Expr\StaticCall;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\SpecifiedTypes;
use PHPStan\Analyser\TypeSpecifier;
use PHPStan\Analyser\TypeSpecifierAwareExtension;
use PHPStan\Analyser\TypeSpecifierContext;
use PHPStan\Reflection\MethodReflection;
use PHPStan\TrinaryLogic;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Php\RegexArrayShapeMatcher;
use PHPStan\Type\StaticMethodTypeSpecifyingExtension;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\Type;
final class PregMatchTypeSpecifyingExtension implements StaticMethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
{
/**
* @var TypeSpecifier
*/
private $typeSpecifier;
/**
* @var RegexArrayShapeMatcher
*/
private $regexShapeMatcher;
public function __construct(RegexArrayShapeMatcher $regexShapeMatcher)
{
$this->regexShapeMatcher = $regexShapeMatcher;
}
public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
{
$this->typeSpecifier = $typeSpecifier;
}
public function getClass(): string
{
return Preg::class;
}
public function isStaticMethodSupported(MethodReflection $methodReflection, StaticCall $node, TypeSpecifierContext $context): bool
{
return in_array($methodReflection->getName(), [
'match', 'isMatch', 'matchStrictGroups', 'isMatchStrictGroups',
'matchAll', 'isMatchAll', 'matchAllStrictGroups', 'isMatchAllStrictGroups'
], true)
&& !$context->null();
}
public function specifyTypes(MethodReflection $methodReflection, StaticCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
{
$args = $node->getArgs();
$patternArg = $args[0] ?? null;
$matchesArg = $args[2] ?? null;
$flagsArg = $args[3] ?? null;
if (
$patternArg === null || $matchesArg === null
) {
return new SpecifiedTypes();
}
$flagsType = PregMatchFlags::getType($flagsArg, $scope);
if ($flagsType === null) {
return new SpecifiedTypes();
}
if (stripos($methodReflection->getName(), 'matchAll') !== false) {
$matchedType = $this->regexShapeMatcher->matchAllExpr($patternArg->value, $flagsType, TrinaryLogic::createFromBoolean($context->true()), $scope);
} else {
$matchedType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createFromBoolean($context->true()), $scope);
}
if ($matchedType === null) {
return new SpecifiedTypes();
}
if (
in_array($methodReflection->getName(), ['matchStrictGroups', 'isMatchStrictGroups', 'matchAllStrictGroups', 'isMatchAllStrictGroups'], true)
) {
$matchedType = PregMatchFlags::removeNullFromMatches($matchedType);
}
$overwrite = false;
if ($context->false()) {
$overwrite = true;
$context = $context->negate();
}
// @phpstan-ignore function.alreadyNarrowedType
if (method_exists('PHPStan\Analyser\SpecifiedTypes', 'setRootExpr')) {
$typeSpecifier = $this->typeSpecifier->create(
$matchesArg->value,
$matchedType,
$context,
$scope
)->setRootExpr($node);
return $overwrite ? $typeSpecifier->setAlwaysOverwriteTypes() : $typeSpecifier;
}
// @phpstan-ignore arguments.count
return $this->typeSpecifier->create(
$matchesArg->value,
$matchedType,
$context,
// @phpstan-ignore argument.type
$overwrite,
$scope,
$node
);
}
}

View File

@ -0,0 +1,91 @@
<?php declare(strict_types=1);
namespace Composer\Pcre\PHPStan;
use Composer\Pcre\Preg;
use Composer\Pcre\Regex;
use PhpParser\Node\Expr\StaticCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\Native\NativeParameterReflection;
use PHPStan\Reflection\ParameterReflection;
use PHPStan\TrinaryLogic;
use PHPStan\Type\ClosureType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Php\RegexArrayShapeMatcher;
use PHPStan\Type\StaticMethodParameterClosureTypeExtension;
use PHPStan\Type\StringType;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\Type;
final class PregReplaceCallbackClosureTypeExtension implements StaticMethodParameterClosureTypeExtension
{
/**
* @var RegexArrayShapeMatcher
*/
private $regexShapeMatcher;
public function __construct(RegexArrayShapeMatcher $regexShapeMatcher)
{
$this->regexShapeMatcher = $regexShapeMatcher;
}
public function isStaticMethodSupported(MethodReflection $methodReflection, ParameterReflection $parameter): bool
{
return in_array($methodReflection->getDeclaringClass()->getName(), [Preg::class, Regex::class], true)
&& in_array($methodReflection->getName(), ['replaceCallback', 'replaceCallbackStrictGroups'], true)
&& $parameter->getName() === 'replacement';
}
public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, ParameterReflection $parameter, Scope $scope): ?Type
{
$args = $methodCall->getArgs();
$patternArg = $args[0] ?? null;
$flagsArg = $args[5] ?? null;
if (
$patternArg === null
) {
return null;
}
$flagsType = PregMatchFlags::getType($flagsArg, $scope);
$matchesType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createYes(), $scope);
if ($matchesType === null) {
return null;
}
if ($methodReflection->getName() === 'replaceCallbackStrictGroups' && count($matchesType->getConstantArrays()) === 1) {
$matchesType = $matchesType->getConstantArrays()[0];
$matchesType = new ConstantArrayType(
$matchesType->getKeyTypes(),
array_map(static function (Type $valueType): Type {
if (count($valueType->getConstantArrays()) === 1) {
$valueTypeArray = $valueType->getConstantArrays()[0];
return new ConstantArrayType(
$valueTypeArray->getKeyTypes(),
array_map(static function (Type $valueType): Type {
return TypeCombinator::removeNull($valueType);
}, $valueTypeArray->getValueTypes()),
$valueTypeArray->getNextAutoIndexes(),
[],
$valueTypeArray->isList()
);
}
return TypeCombinator::removeNull($valueType);
}, $matchesType->getValueTypes()),
$matchesType->getNextAutoIndexes(),
[],
$matchesType->isList()
);
}
return new ClosureType(
[
new NativeParameterReflection($parameter->getName(), $parameter->isOptional(), $matchesType, $parameter->passedByReference(), $parameter->isVariadic(), $parameter->getDefaultValue()),
],
new StringType()
);
}
}

View File

@ -0,0 +1,112 @@
<?php declare(strict_types=1);
namespace Composer\Pcre\PHPStan;
use Composer\Pcre\Preg;
use Composer\Pcre\Regex;
use PhpParser\Node;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name\FullyQualified;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\SpecifiedTypes;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\TrinaryLogic;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\Php\RegexArrayShapeMatcher;
use function sprintf;
/**
* @implements Rule<StaticCall>
*/
final class UnsafeStrictGroupsCallRule implements Rule
{
/**
* @var RegexArrayShapeMatcher
*/
private $regexShapeMatcher;
public function __construct(RegexArrayShapeMatcher $regexShapeMatcher)
{
$this->regexShapeMatcher = $regexShapeMatcher;
}
public function getNodeType(): string
{
return StaticCall::class;
}
public function processNode(Node $node, Scope $scope): array
{
if (!$node->class instanceof FullyQualified) {
return [];
}
$isRegex = $node->class->toString() === Regex::class;
$isPreg = $node->class->toString() === Preg::class;
if (!$isRegex && !$isPreg) {
return [];
}
if (!$node->name instanceof Node\Identifier || !in_array($node->name->name, ['matchStrictGroups', 'isMatchStrictGroups', 'matchAllStrictGroups', 'isMatchAllStrictGroups'], true)) {
return [];
}
$args = $node->getArgs();
if (!isset($args[0])) {
return [];
}
$patternArg = $args[0] ?? null;
if ($isPreg) {
if (!isset($args[2])) { // no matches set, skip as the matches won't be used anyway
return [];
}
$flagsArg = $args[3] ?? null;
} else {
$flagsArg = $args[2] ?? null;
}
if ($patternArg === null) {
return [];
}
$flagsType = PregMatchFlags::getType($flagsArg, $scope);
if ($flagsType === null) {
return [];
}
$matchedType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createYes(), $scope);
if ($matchedType === null) {
return [
RuleErrorBuilder::message(sprintf('The %s call is potentially unsafe as $matches\' type could not be inferred.', $node->name->name))
->identifier('composerPcre.maybeUnsafeStrictGroups')
->build(),
];
}
if (count($matchedType->getConstantArrays()) === 1) {
$matchedType = $matchedType->getConstantArrays()[0];
$nullableGroups = [];
foreach ($matchedType->getValueTypes() as $index => $type) {
if (TypeCombinator::containsNull($type)) {
$nullableGroups[] = $matchedType->getKeyTypes()[$index]->getValue();
}
}
if (\count($nullableGroups) > 0) {
return [
RuleErrorBuilder::message(sprintf(
'The %s call is unsafe as match group%s "%s" %s optional and may be null.',
$node->name->name,
\count($nullableGroups) > 1 ? 's' : '',
implode('", "', $nullableGroups),
\count($nullableGroups) > 1 ? 'are' : 'is'
))->identifier('composerPcre.unsafeStrictGroups')->build(),
];
}
}
return [];
}
}

View File

@ -0,0 +1,55 @@
<?php
/*
* This file is part of composer/pcre.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\Pcre;
class PcreException extends \RuntimeException
{
/**
* @param string $function
* @param string|string[] $pattern
* @return self
*/
public static function fromFunction($function, $pattern)
{
$code = preg_last_error();
if (is_array($pattern)) {
$pattern = implode(', ', $pattern);
}
return new PcreException($function.'(): failed executing "'.$pattern.'": '.self::pcreLastErrorMessage($code), $code);
}
/**
* @param int $code
* @return string
*/
private static function pcreLastErrorMessage($code)
{
if (function_exists('preg_last_error_msg')) {
return preg_last_error_msg();
}
$constants = get_defined_constants(true);
if (!isset($constants['pcre']) || !is_array($constants['pcre'])) {
return 'UNDEFINED_ERROR';
}
foreach ($constants['pcre'] as $const => $val) {
if ($val === $code && substr($const, -6) === '_ERROR') {
return $const;
}
}
return 'UNDEFINED_ERROR';
}
}

430
vendor/composer/pcre/src/Preg.php vendored Normal file
View File

@ -0,0 +1,430 @@
<?php
/*
* This file is part of composer/pcre.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\Pcre;
class Preg
{
/** @internal */
public const ARRAY_MSG = '$subject as an array is not supported. You can use \'foreach\' instead.';
/** @internal */
public const INVALID_TYPE_MSG = '$subject must be a string, %s given.';
/**
* @param non-empty-string $pattern
* @param array<mixed> $matches Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
* @return 0|1
*
* @param-out array<int|string, string|null> $matches
*/
public static function match(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int
{
self::checkOffsetCapture($flags, 'matchWithOffsets');
$result = preg_match($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL, $offset);
if ($result === false) {
throw PcreException::fromFunction('preg_match', $pattern);
}
return $result;
}
/**
* Variant of `match()` which outputs non-null matches (or throws)
*
* @param non-empty-string $pattern
* @param array<mixed> $matches Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
* @return 0|1
* @throws UnexpectedNullMatchException
*
* @param-out array<int|string, string> $matches
*/
public static function matchStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int
{
$result = self::match($pattern, $subject, $matchesInternal, $flags, $offset);
$matches = self::enforceNonNullMatches($pattern, $matchesInternal, 'match');
return $result;
}
/**
* Runs preg_match with PREG_OFFSET_CAPTURE
*
* @param non-empty-string $pattern
* @param array<mixed> $matches Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE> $flags PREG_UNMATCHED_AS_NULL and PREG_OFFSET_CAPTURE are always set, no other flags are supported
* @return 0|1
*
* @param-out array<int|string, array{string|null, int<-1, max>}> $matches
*/
public static function matchWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): int
{
$result = preg_match($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE, $offset);
if ($result === false) {
throw PcreException::fromFunction('preg_match', $pattern);
}
return $result;
}
/**
* @param non-empty-string $pattern
* @param array<mixed> $matches Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
* @return 0|positive-int
*
* @param-out array<int|string, list<string|null>> $matches
*/
public static function matchAll(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int
{
self::checkOffsetCapture($flags, 'matchAllWithOffsets');
self::checkSetOrder($flags);
$result = preg_match_all($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL, $offset);
if (!is_int($result)) { // PHP < 8 may return null, 8+ returns int|false
throw PcreException::fromFunction('preg_match_all', $pattern);
}
return $result;
}
/**
* Variant of `match()` which outputs non-null matches (or throws)
*
* @param non-empty-string $pattern
* @param array<mixed> $matches Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
* @return 0|positive-int
* @throws UnexpectedNullMatchException
*
* @param-out array<int|string, list<string>> $matches
*/
public static function matchAllStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int
{
$result = self::matchAll($pattern, $subject, $matchesInternal, $flags, $offset);
$matches = self::enforceNonNullMatchAll($pattern, $matchesInternal, 'matchAll');
return $result;
}
/**
* Runs preg_match_all with PREG_OFFSET_CAPTURE
*
* @param non-empty-string $pattern
* @param array<mixed> $matches Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE> $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported
* @return 0|positive-int
*
* @param-out array<int|string, list<array{string|null, int<-1, max>}>> $matches
*/
public static function matchAllWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): int
{
self::checkSetOrder($flags);
$result = preg_match_all($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE, $offset);
if (!is_int($result)) { // PHP < 8 may return null, 8+ returns int|false
throw PcreException::fromFunction('preg_match_all', $pattern);
}
return $result;
}
/**
* @param string|string[] $pattern
* @param string|string[] $replacement
* @param string $subject
* @param int $count Set by method
*
* @param-out int<0, max> $count
*/
public static function replace($pattern, $replacement, $subject, int $limit = -1, ?int &$count = null): string
{
if (!is_scalar($subject)) {
if (is_array($subject)) {
throw new \InvalidArgumentException(static::ARRAY_MSG);
}
throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject)));
}
$result = preg_replace($pattern, $replacement, $subject, $limit, $count);
if ($result === null) {
throw PcreException::fromFunction('preg_replace', $pattern);
}
return $result;
}
/**
* @param string|string[] $pattern
* @param ($flags is PREG_OFFSET_CAPTURE ? (callable(array<int|string, array{string|null, int<-1, max>}>): string) : callable(array<int|string, string|null>): string) $replacement
* @param string $subject
* @param int $count Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE> $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set
*
* @param-out int<0, max> $count
*/
public static function replaceCallback($pattern, callable $replacement, $subject, int $limit = -1, ?int &$count = null, int $flags = 0): string
{
if (!is_scalar($subject)) {
if (is_array($subject)) {
throw new \InvalidArgumentException(static::ARRAY_MSG);
}
throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject)));
}
$result = preg_replace_callback($pattern, $replacement, $subject, $limit, $count, $flags | PREG_UNMATCHED_AS_NULL);
if ($result === null) {
throw PcreException::fromFunction('preg_replace_callback', $pattern);
}
return $result;
}
/**
* Variant of `replaceCallback()` which outputs non-null matches (or throws)
*
* @param string $pattern
* @param ($flags is PREG_OFFSET_CAPTURE ? (callable(array<int|string, array{string, int<0, max>}>): string) : callable(array<int|string, string>): string) $replacement
* @param string $subject
* @param int $count Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE> $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set
*
* @param-out int<0, max> $count
*/
public static function replaceCallbackStrictGroups(string $pattern, callable $replacement, $subject, int $limit = -1, ?int &$count = null, int $flags = 0): string
{
return self::replaceCallback($pattern, function (array $matches) use ($pattern, $replacement) {
return $replacement(self::enforceNonNullMatches($pattern, $matches, 'replaceCallback'));
}, $subject, $limit, $count, $flags);
}
/**
* @param ($flags is PREG_OFFSET_CAPTURE ? (array<string, callable(array<int|string, array{string|null, int<-1, max>}>): string>) : array<string, callable(array<int|string, string|null>): string>) $pattern
* @param string $subject
* @param int $count Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE> $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set
*
* @param-out int<0, max> $count
*/
public static function replaceCallbackArray(array $pattern, $subject, int $limit = -1, ?int &$count = null, int $flags = 0): string
{
if (!is_scalar($subject)) {
if (is_array($subject)) {
throw new \InvalidArgumentException(static::ARRAY_MSG);
}
throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject)));
}
$result = preg_replace_callback_array($pattern, $subject, $limit, $count, $flags | PREG_UNMATCHED_AS_NULL);
if ($result === null) {
$pattern = array_keys($pattern);
throw PcreException::fromFunction('preg_replace_callback_array', $pattern);
}
return $result;
}
/**
* @param int-mask<PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_OFFSET_CAPTURE> $flags PREG_SPLIT_NO_EMPTY or PREG_SPLIT_DELIM_CAPTURE
* @return list<string>
*/
public static function split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array
{
if (($flags & PREG_SPLIT_OFFSET_CAPTURE) !== 0) {
throw new \InvalidArgumentException('PREG_SPLIT_OFFSET_CAPTURE is not supported as it changes the type of $matches, use splitWithOffsets() instead');
}
$result = preg_split($pattern, $subject, $limit, $flags);
if ($result === false) {
throw PcreException::fromFunction('preg_split', $pattern);
}
return $result;
}
/**
* @param int-mask<PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_OFFSET_CAPTURE> $flags PREG_SPLIT_NO_EMPTY or PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_OFFSET_CAPTURE is always set
* @return list<array{string, int}>
* @phpstan-return list<array{string, int<0, max>}>
*/
public static function splitWithOffsets(string $pattern, string $subject, int $limit = -1, int $flags = 0): array
{
$result = preg_split($pattern, $subject, $limit, $flags | PREG_SPLIT_OFFSET_CAPTURE);
if ($result === false) {
throw PcreException::fromFunction('preg_split', $pattern);
}
return $result;
}
/**
* @template T of string|\Stringable
* @param string $pattern
* @param array<T> $array
* @param int-mask<PREG_GREP_INVERT> $flags PREG_GREP_INVERT
* @return array<T>
*/
public static function grep(string $pattern, array $array, int $flags = 0): array
{
$result = preg_grep($pattern, $array, $flags);
if ($result === false) {
throw PcreException::fromFunction('preg_grep', $pattern);
}
return $result;
}
/**
* Variant of match() which returns a bool instead of int
*
* @param non-empty-string $pattern
* @param array<mixed> $matches Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
*
* @param-out array<int|string, string|null> $matches
*/
public static function isMatch(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool
{
return (bool) static::match($pattern, $subject, $matches, $flags, $offset);
}
/**
* Variant of `isMatch()` which outputs non-null matches (or throws)
*
* @param non-empty-string $pattern
* @param array<mixed> $matches Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
* @throws UnexpectedNullMatchException
*
* @param-out array<int|string, string> $matches
*/
public static function isMatchStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool
{
return (bool) self::matchStrictGroups($pattern, $subject, $matches, $flags, $offset);
}
/**
* Variant of matchAll() which returns a bool instead of int
*
* @param non-empty-string $pattern
* @param array<mixed> $matches Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
*
* @param-out array<int|string, list<string|null>> $matches
*/
public static function isMatchAll(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool
{
return (bool) static::matchAll($pattern, $subject, $matches, $flags, $offset);
}
/**
* Variant of `isMatchAll()` which outputs non-null matches (or throws)
*
* @param non-empty-string $pattern
* @param array<mixed> $matches Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
*
* @param-out array<int|string, list<string>> $matches
*/
public static function isMatchAllStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool
{
return (bool) self::matchAllStrictGroups($pattern, $subject, $matches, $flags, $offset);
}
/**
* Variant of matchWithOffsets() which returns a bool instead of int
*
* Runs preg_match with PREG_OFFSET_CAPTURE
*
* @param non-empty-string $pattern
* @param array<mixed> $matches Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
*
* @param-out array<int|string, array{string|null, int<-1, max>}> $matches
*/
public static function isMatchWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): bool
{
return (bool) static::matchWithOffsets($pattern, $subject, $matches, $flags, $offset);
}
/**
* Variant of matchAllWithOffsets() which returns a bool instead of int
*
* Runs preg_match_all with PREG_OFFSET_CAPTURE
*
* @param non-empty-string $pattern
* @param array<mixed> $matches Set by method
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
*
* @param-out array<int|string, list<array{string|null, int<-1, max>}>> $matches
*/
public static function isMatchAllWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): bool
{
return (bool) static::matchAllWithOffsets($pattern, $subject, $matches, $flags, $offset);
}
private static function checkOffsetCapture(int $flags, string $useFunctionName): void
{
if (($flags & PREG_OFFSET_CAPTURE) !== 0) {
throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the type of $matches, use ' . $useFunctionName . '() instead');
}
}
private static function checkSetOrder(int $flags): void
{
if (($flags & PREG_SET_ORDER) !== 0) {
throw new \InvalidArgumentException('PREG_SET_ORDER is not supported as it changes the type of $matches');
}
}
/**
* @param array<int|string, string|null|array{string|null, int}> $matches
* @return array<int|string, string>
* @throws UnexpectedNullMatchException
*/
private static function enforceNonNullMatches(string $pattern, array $matches, string $variantMethod)
{
foreach ($matches as $group => $match) {
if (is_string($match) || (is_array($match) && is_string($match[0]))) {
continue;
}
throw new UnexpectedNullMatchException('Pattern "'.$pattern.'" had an unexpected unmatched group "'.$group.'", make sure the pattern always matches or use '.$variantMethod.'() instead.');
}
/** @var array<string> */
return $matches;
}
/**
* @param array<int|string, list<string|null>> $matches
* @return array<int|string, list<string>>
* @throws UnexpectedNullMatchException
*/
private static function enforceNonNullMatchAll(string $pattern, array $matches, string $variantMethod)
{
foreach ($matches as $group => $groupMatches) {
foreach ($groupMatches as $match) {
if (null === $match) {
throw new UnexpectedNullMatchException('Pattern "'.$pattern.'" had an unexpected unmatched group "'.$group.'", make sure the pattern always matches or use '.$variantMethod.'() instead.');
}
}
}
/** @var array<int|string, list<string>> */
return $matches;
}
}

176
vendor/composer/pcre/src/Regex.php vendored Normal file
View File

@ -0,0 +1,176 @@
<?php
/*
* This file is part of composer/pcre.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\Pcre;
class Regex
{
/**
* @param non-empty-string $pattern
*/
public static function isMatch(string $pattern, string $subject, int $offset = 0): bool
{
return (bool) Preg::match($pattern, $subject, $matches, 0, $offset);
}
/**
* @param non-empty-string $pattern
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
*/
public static function match(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchResult
{
self::checkOffsetCapture($flags, 'matchWithOffsets');
$count = Preg::match($pattern, $subject, $matches, $flags, $offset);
return new MatchResult($count, $matches);
}
/**
* Variant of `match()` which returns non-null matches (or throws)
*
* @param non-empty-string $pattern
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
* @throws UnexpectedNullMatchException
*/
public static function matchStrictGroups(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchStrictGroupsResult
{
// @phpstan-ignore composerPcre.maybeUnsafeStrictGroups
$count = Preg::matchStrictGroups($pattern, $subject, $matches, $flags, $offset);
return new MatchStrictGroupsResult($count, $matches);
}
/**
* Runs preg_match with PREG_OFFSET_CAPTURE
*
* @param non-empty-string $pattern
* @param int-mask<PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE> $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported
*/
public static function matchWithOffsets(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchWithOffsetsResult
{
$count = Preg::matchWithOffsets($pattern, $subject, $matches, $flags, $offset);
return new MatchWithOffsetsResult($count, $matches);
}
/**
* @param non-empty-string $pattern
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
*/
public static function matchAll(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllResult
{
self::checkOffsetCapture($flags, 'matchAllWithOffsets');
self::checkSetOrder($flags);
$count = Preg::matchAll($pattern, $subject, $matches, $flags, $offset);
return new MatchAllResult($count, $matches);
}
/**
* Variant of `matchAll()` which returns non-null matches (or throws)
*
* @param non-empty-string $pattern
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
* @throws UnexpectedNullMatchException
*/
public static function matchAllStrictGroups(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllStrictGroupsResult
{
self::checkOffsetCapture($flags, 'matchAllWithOffsets');
self::checkSetOrder($flags);
// @phpstan-ignore composerPcre.maybeUnsafeStrictGroups
$count = Preg::matchAllStrictGroups($pattern, $subject, $matches, $flags, $offset);
return new MatchAllStrictGroupsResult($count, $matches);
}
/**
* Runs preg_match_all with PREG_OFFSET_CAPTURE
*
* @param non-empty-string $pattern
* @param int-mask<PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE> $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported
*/
public static function matchAllWithOffsets(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllWithOffsetsResult
{
self::checkSetOrder($flags);
$count = Preg::matchAllWithOffsets($pattern, $subject, $matches, $flags, $offset);
return new MatchAllWithOffsetsResult($count, $matches);
}
/**
* @param string|string[] $pattern
* @param string|string[] $replacement
* @param string $subject
*/
public static function replace($pattern, $replacement, $subject, int $limit = -1): ReplaceResult
{
$result = Preg::replace($pattern, $replacement, $subject, $limit, $count);
return new ReplaceResult($count, $result);
}
/**
* @param string|string[] $pattern
* @param ($flags is PREG_OFFSET_CAPTURE ? (callable(array<int|string, array{string|null, int<-1, max>}>): string) : callable(array<int|string, string|null>): string) $replacement
* @param string $subject
* @param int-mask<PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE> $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set
*/
public static function replaceCallback($pattern, callable $replacement, $subject, int $limit = -1, int $flags = 0): ReplaceResult
{
$result = Preg::replaceCallback($pattern, $replacement, $subject, $limit, $count, $flags);
return new ReplaceResult($count, $result);
}
/**
* Variant of `replaceCallback()` which outputs non-null matches (or throws)
*
* @param string $pattern
* @param ($flags is PREG_OFFSET_CAPTURE ? (callable(array<int|string, array{string, int<0, max>}>): string) : callable(array<int|string, string>): string) $replacement
* @param string $subject
* @param int-mask<PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE> $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set
*/
public static function replaceCallbackStrictGroups($pattern, callable $replacement, $subject, int $limit = -1, int $flags = 0): ReplaceResult
{
$result = Preg::replaceCallbackStrictGroups($pattern, $replacement, $subject, $limit, $count, $flags);
return new ReplaceResult($count, $result);
}
/**
* @param ($flags is PREG_OFFSET_CAPTURE ? (array<string, callable(array<int|string, array{string|null, int<-1, max>}>): string>) : array<string, callable(array<int|string, string|null>): string>) $pattern
* @param string $subject
* @param int-mask<PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE> $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set
*/
public static function replaceCallbackArray(array $pattern, $subject, int $limit = -1, int $flags = 0): ReplaceResult
{
$result = Preg::replaceCallbackArray($pattern, $subject, $limit, $count, $flags);
return new ReplaceResult($count, $result);
}
private static function checkOffsetCapture(int $flags, string $useFunctionName): void
{
if (($flags & PREG_OFFSET_CAPTURE) !== 0) {
throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the return type, use '.$useFunctionName.'() instead');
}
}
private static function checkSetOrder(int $flags): void
{
if (($flags & PREG_SET_ORDER) !== 0) {
throw new \InvalidArgumentException('PREG_SET_ORDER is not supported as it changes the return type');
}
}
}

View File

@ -0,0 +1,43 @@
<?php
/*
* This file is part of composer/pcre.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\Pcre;
final class ReplaceResult
{
/**
* @readonly
* @var string
*/
public $result;
/**
* @readonly
* @var 0|positive-int
*/
public $count;
/**
* @readonly
* @var bool
*/
public $matched;
/**
* @param 0|positive-int $count
*/
public function __construct(int $count, string $result)
{
$this->count = $count;
$this->matched = (bool) $count;
$this->result = $result;
}
}

View File

@ -0,0 +1,20 @@
<?php
/*
* This file is part of composer/pcre.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\Pcre;
class UnexpectedNullMatchException extends PcreException
{
public static function fromFunction($function, $pattern)
{
throw new \LogicException('fromFunction should not be called on '.self::class.', use '.PcreException::class);
}
}