*/ private $properties = []; public function __construct() { $schema = JsonFile::parseJson((string) file_get_contents(__DIR__.'/../../../res/composer-schema.json')); /** * @var string $prop */ foreach ($schema['properties']['config']['properties'] as $prop => $conf) { $type = $this->parseType($conf, $prop); $this->properties[$prop] = $type; } } public function getClass(): string { return Config::class; } public function isMethodSupported(MethodReflection $methodReflection): bool { return strtolower($methodReflection->getName()) === 'get'; } public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope, ): Type { $args = $methodCall->getArgs(); $defaultReturn = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); if (count($args) < 1) { return $defaultReturn; } $keyType = $scope->getType($args[0]->value); if (method_exists($keyType, 'getConstantStrings')) { // @phpstan-ignore-line - depending on PHPStan version, this method will always exist, or not. $strings = $keyType->getConstantStrings(); } else { // for compat with old phpstan versions, we use a deprecated phpstan method. $strings = TypeUtils::getConstantStrings($keyType); // @phpstan-ignore-line ignore deprecation } if ($strings !== []) { $types = []; foreach($strings as $string) { if (!isset($this->properties[$string->getValue()])) { return $defaultReturn; } $types[] = $this->properties[$string->getValue()]; } return TypeCombinator::union(...$types); } return $defaultReturn; } /** * @param array $def */ private function parseType(array $def, string $path): Type { if (isset($def['type'])) { $types = []; foreach ((array) $def['type'] as $type) { switch ($type) { case 'integer': if (in_array($path, ['process-timeout', 'cache-ttl', 'cache-files-ttl', 'cache-files-maxsize'], true)) { $types[] = IntegerRangeType::createAllGreaterThanOrEqualTo(0); } else { $types[] = new IntegerType(); } break; case 'string': if ($path === 'cache-files-maxsize') { // passthru, skip as it is always converted to int } elseif ($path === 'discard-changes') { $types[] = new ConstantStringType('stash'); } elseif ($path === 'use-parent-dir') { $types[] = new ConstantStringType('prompt'); } elseif ($path === 'store-auths') { $types[] = new ConstantStringType('prompt'); } elseif ($path === 'platform-check') { $types[] = new ConstantStringType('php-only'); } elseif ($path === 'github-protocols') { $types[] = new UnionType([new ConstantStringType('git'), new ConstantStringType('https'), new ConstantStringType('ssh'), new ConstantStringType('http')]); } elseif (str_starts_with($path, 'preferred-install')) { $types[] = new UnionType([new ConstantStringType('source'), new ConstantStringType('dist'), new ConstantStringType('auto')]); } else { $types[] = new StringType(); } break; case 'boolean': if ($path === 'platform.additionalProperties') { $types[] = new ConstantBooleanType(false); } else { $types[] = new BooleanType(); } break; case 'object': $addlPropType = null; if (isset($def['additionalProperties'])) { $addlPropType = $this->parseType($def['additionalProperties'], $path.'.additionalProperties'); } if (isset($def['properties'])) { $keyNames = []; $valTypes = []; $optionalKeys = []; $propIndex = 0; foreach ($def['properties'] as $propName => $propdef) { $keyNames[] = new ConstantStringType($propName); $valType = $this->parseType($propdef, $path.'.'.$propName); if (!isset($def['required']) || !in_array($propName, $def['required'], true)) { $valType = TypeCombinator::addNull($valType); $optionalKeys[] = $propIndex; } $valTypes[] = $valType; $propIndex++; } if ($addlPropType !== null) { $types[] = new ArrayType(TypeCombinator::union(new StringType(), ...$keyNames), TypeCombinator::union($addlPropType, ...$valTypes)); } else { $types[] = new ConstantArrayType($keyNames, $valTypes, [0], $optionalKeys); } } else { $types[] = new ArrayType(new StringType(), $addlPropType ?? new MixedType()); } break; case 'array': if (isset($def['items'])) { $valType = $this->parseType($def['items'], $path.'.items'); } else { $valType = new MixedType(); } $types[] = new ArrayType(new IntegerType(), $valType); break; default: $types[] = new MixedType(); } } $type = TypeCombinator::union(...$types); } elseif (isset($def['enum'])) { $type = TypeCombinator::union(...array_map(static function (string $value): ConstantStringType { return new ConstantStringType($value); }, $def['enum'])); } else { $type = new MixedType(); } // allow-plugins defaults to null until July 1st 2022 for some BC hackery, but after that it is not nullable anymore if ($path === 'allow-plugins' && time() < strtotime('2022-07-01')) { $type = TypeCombinator::addNull($type); } // default null props if (in_array($path, ['autoloader-suffix', 'gitlab-protocol'], true)) { $type = TypeCombinator::addNull($type); } return $type; } } __halt_compiler();----SIGNATURE:----PMJE79JwnfDeE25gpxYpsjE8xQpzpjKApnt4n9wuVq0G6N8JV1iFPfrdTU+NIWqe90F7YyE1T2VJ7XSFg+VT24M5J1C7pTxRTVGHWF+dv6Tl8Q+FXLSBcVM1HiAdWjtPYiMQ0zMT5qFQGCT0jkDOvp5ZgEDsTPwxKniar/B3D9Etp0qqIYrEeFFHHnriXm/C4DDnzl5w6sHSYNjGV5m5BgX6lXRAjzPplSKBH0Dcfa0mYQT0NHEElEIcM+F2xHsHuvKIyt+E9vBKzSDzNHQo0uLRVUEDR4A62a7jyJkGErm57aSdE5zx6ofwD0p7qrq+6ugb/YKvpymXH3ABJv+D0W9mhLWTdXVo3ovQCwyAG+71IlP2duIbx3e0AVyAP8C2kPEl9IHBMNtKXqxvbiJAx2ZLV0AiNMR4T9aspwaBy0prgrKdro9kpTr49RMRTDsX8UUHuzrxmDvS62dXxlhGxT97i9BaLFSwzKKFj4Oeq044I/+HRjfxyIYkts+Teg5iSiU/+C1kuvV6BOTZFyOECvrfnTU9Ph9V2hm7vNFvZZGGqebuFWPmNZz10flh1OoJ3uYYyvjjxQ1FDDIFXyv+wf2WfpMXevqUafD6I+Xbz5jdHPRYWBpidN7sb7gjpHCM1xowOWRYGSBp+Du1R2x3kb5E0ad9eH4syv11gPI+Cjk=----ATTACHMENT:----OTAxODU0NDgyNjQ4ODc2NiAyNjc0MDczNzI2MjA5MDIwIDQ1MjEwMjMxNzY0Mjc1MTA=