function ConfigCommand::execute
Throws
\Seld\JsonLint\ParsingException
Overrides Command::execute
File
-
vendor/
composer/ composer/ src/ Composer/ Command/ ConfigCommand.php, line 225
Class
- ConfigCommand
- @author Joshua Estes <Joshua.Estes@iostudio.com> @author Jordi Boggiano <j.boggiano@seld.be>
Namespace
Composer\CommandCode
protected function execute(InputInterface $input, OutputInterface $output) : int {
// Open file in editor
if (true === $input->getOption('editor')) {
$editor = Platform::getEnv('EDITOR');
if (false === $editor || '' === $editor) {
if (Platform::isWindows()) {
$editor = 'notepad';
}
else {
foreach ([
'editor',
'vim',
'vi',
'nano',
'pico',
'ed',
] as $candidate) {
if (exec('which ' . $candidate)) {
$editor = $candidate;
break;
}
}
}
}
else {
$editor = escapeshellcmd($editor);
}
$file = $input->getOption('auth') ? $this->authConfigFile
->getPath() : $this->configFile
->getPath();
system($editor . ' ' . $file . (Platform::isWindows() ? '' : ' > `tty`'));
return 0;
}
if (false === $input->getOption('global')) {
$this->config
->merge($this->configFile
->read(), $this->configFile
->getPath());
$this->config
->merge([
'config' => $this->authConfigFile
->exists() ? $this->authConfigFile
->read() : [],
], $this->authConfigFile
->getPath());
}
$this->getIO()
->loadConfiguration($this->config);
// List the configuration of the file settings
if (true === $input->getOption('list')) {
$this->listConfiguration($this->config
->all(), $this->config
->raw(), $output, null, $input->getOption('source'));
return 0;
}
$settingKey = $input->getArgument('setting-key');
if (!is_string($settingKey)) {
return 0;
}
// If the user enters in a config variable, parse it and save to file
if ([] !== $input->getArgument('setting-value') && $input->getOption('unset')) {
throw new \RuntimeException('You can not combine a setting value with --unset');
}
// show the value if no value is provided
if ([] === $input->getArgument('setting-value') && !$input->getOption('unset')) {
$properties = self::CONFIGURABLE_PACKAGE_PROPERTIES;
$propertiesDefaults = [
'type' => 'library',
'description' => '',
'homepage' => '',
'minimum-stability' => 'stable',
'prefer-stable' => false,
'keywords' => [],
'license' => [],
'suggest' => [],
'extra' => [],
];
$rawData = $this->configFile
->read();
$data = $this->config
->all();
$source = $this->config
->getSourceOfValue($settingKey);
if (Preg::isMatch('/^repos?(?:itories)?(?:\\.(.+))?/', $settingKey, $matches)) {
if (!isset($matches[1])) {
$value = $data['repositories'] ?? [];
}
else {
if (!isset($data['repositories'][$matches[1]])) {
throw new \InvalidArgumentException('There is no ' . $matches[1] . ' repository defined');
}
$value = $data['repositories'][$matches[1]];
}
}
elseif (strpos($settingKey, '.')) {
$bits = explode('.', $settingKey);
if ($bits[0] === 'extra' || $bits[0] === 'suggest') {
$data = $rawData;
}
else {
$data = $data['config'];
}
$match = false;
foreach ($bits as $bit) {
$key = isset($key) ? $key . '.' . $bit : $bit;
$match = false;
if (isset($data[$key])) {
$match = true;
$data = $data[$key];
unset($key);
}
}
if (!$match) {
throw new \RuntimeException($settingKey . ' is not defined.');
}
$value = $data;
}
elseif (isset($data['config'][$settingKey])) {
$value = $this->config
->get($settingKey, $input->getOption('absolute') ? 0 : Config::RELATIVE_PATHS);
// ensure we get {} output for properties which are objects
if ($value === []) {
$schema = JsonFile::parseJson((string) file_get_contents(JsonFile::COMPOSER_SCHEMA_PATH));
if (isset($schema['properties']['config']['properties'][$settingKey]['type']) && in_array('object', (array) $schema['properties']['config']['properties'][$settingKey]['type'], true)) {
$value = new \stdClass();
}
}
}
elseif (isset($rawData[$settingKey]) && in_array($settingKey, $properties, true)) {
$value = $rawData[$settingKey];
$source = $this->configFile
->getPath();
}
elseif (isset($propertiesDefaults[$settingKey])) {
$value = $propertiesDefaults[$settingKey];
$source = 'defaults';
}
else {
throw new \RuntimeException($settingKey . ' is not defined');
}
if (is_array($value) || is_object($value) || is_bool($value)) {
$value = JsonFile::encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
$sourceOfConfigValue = '';
if ($input->getOption('source')) {
$sourceOfConfigValue = ' (' . $source . ')';
}
$this->getIO()
->write($value . $sourceOfConfigValue, true, IOInterface::QUIET);
return 0;
}
$values = $input->getArgument('setting-value');
// what the user is trying to add/change
$booleanValidator = static function ($val) : bool {
return in_array($val, [
'true',
'false',
'1',
'0',
], true);
};
$booleanNormalizer = static function ($val) : bool {
return $val !== 'false' && (bool) $val;
};
// handle config values
$uniqueConfigValues = [
'process-timeout' => [
'is_numeric',
'intval',
],
'use-include-path' => [
$booleanValidator,
$booleanNormalizer,
],
'use-github-api' => [
$booleanValidator,
$booleanNormalizer,
],
'preferred-install' => [
static function ($val) : bool {
return in_array($val, [
'auto',
'source',
'dist',
], true);
},
static function ($val) {
return $val;
},
],
'gitlab-protocol' => [
static function ($val) : bool {
return in_array($val, [
'git',
'http',
'https',
], true);
},
static function ($val) {
return $val;
},
],
'store-auths' => [
static function ($val) : bool {
return in_array($val, [
'true',
'false',
'prompt',
], true);
},
static function ($val) {
if ('prompt' === $val) {
return 'prompt';
}
return $val !== 'false' && (bool) $val;
},
],
'notify-on-install' => [
$booleanValidator,
$booleanNormalizer,
],
'vendor-dir' => [
'is_string',
static function ($val) {
return $val;
},
],
'bin-dir' => [
'is_string',
static function ($val) {
return $val;
},
],
'archive-dir' => [
'is_string',
static function ($val) {
return $val;
},
],
'archive-format' => [
'is_string',
static function ($val) {
return $val;
},
],
'data-dir' => [
'is_string',
static function ($val) {
return $val;
},
],
'cache-dir' => [
'is_string',
static function ($val) {
return $val;
},
],
'cache-files-dir' => [
'is_string',
static function ($val) {
return $val;
},
],
'cache-repo-dir' => [
'is_string',
static function ($val) {
return $val;
},
],
'cache-vcs-dir' => [
'is_string',
static function ($val) {
return $val;
},
],
'cache-ttl' => [
'is_numeric',
'intval',
],
'cache-files-ttl' => [
'is_numeric',
'intval',
],
'cache-files-maxsize' => [
static function ($val) : bool {
return Preg::isMatch('/^\\s*([0-9.]+)\\s*(?:([kmg])(?:i?b)?)?\\s*$/i', $val);
},
static function ($val) {
return $val;
},
],
'bin-compat' => [
static function ($val) : bool {
return in_array($val, [
'auto',
'full',
'proxy',
'symlink',
]);
},
static function ($val) {
return $val;
},
],
'discard-changes' => [
static function ($val) : bool {
return in_array($val, [
'stash',
'true',
'false',
'1',
'0',
], true);
},
static function ($val) {
if ('stash' === $val) {
return 'stash';
}
return $val !== 'false' && (bool) $val;
},
],
'autoloader-suffix' => [
'is_string',
static function ($val) {
return $val === 'null' ? null : $val;
},
],
'sort-packages' => [
$booleanValidator,
$booleanNormalizer,
],
'optimize-autoloader' => [
$booleanValidator,
$booleanNormalizer,
],
'classmap-authoritative' => [
$booleanValidator,
$booleanNormalizer,
],
'apcu-autoloader' => [
$booleanValidator,
$booleanNormalizer,
],
'prepend-autoloader' => [
$booleanValidator,
$booleanNormalizer,
],
'disable-tls' => [
$booleanValidator,
$booleanNormalizer,
],
'secure-http' => [
$booleanValidator,
$booleanNormalizer,
],
'bump-after-update' => [
static function ($val) : bool {
return in_array($val, [
'dev',
'no-dev',
'true',
'false',
'1',
'0',
], true);
},
static function ($val) {
if ('dev' === $val || 'no-dev' === $val) {
return $val;
}
return $val !== 'false' && (bool) $val;
},
],
'cafile' => [
static function ($val) : bool {
return file_exists($val) && Filesystem::isReadable($val);
},
static function ($val) {
return $val === 'null' ? null : $val;
},
],
'capath' => [
static function ($val) : bool {
return is_dir($val) && Filesystem::isReadable($val);
},
static function ($val) {
return $val === 'null' ? null : $val;
},
],
'github-expose-hostname' => [
$booleanValidator,
$booleanNormalizer,
],
'htaccess-protect' => [
$booleanValidator,
$booleanNormalizer,
],
'lock' => [
$booleanValidator,
$booleanNormalizer,
],
'allow-plugins' => [
$booleanValidator,
$booleanNormalizer,
],
'platform-check' => [
static function ($val) : bool {
return in_array($val, [
'php-only',
'true',
'false',
'1',
'0',
], true);
},
static function ($val) {
if ('php-only' === $val) {
return 'php-only';
}
return $val !== 'false' && (bool) $val;
},
],
'use-parent-dir' => [
static function ($val) : bool {
return in_array($val, [
'true',
'false',
'prompt',
], true);
},
static function ($val) {
if ('prompt' === $val) {
return 'prompt';
}
return $val !== 'false' && (bool) $val;
},
],
'audit.abandoned' => [
static function ($val) : bool {
return in_array($val, [
Auditor::ABANDONED_IGNORE,
Auditor::ABANDONED_REPORT,
Auditor::ABANDONED_FAIL,
], true);
},
static function ($val) {
return $val;
},
],
];
$multiConfigValues = [
'github-protocols' => [
static function ($vals) {
if (!is_array($vals)) {
return 'array expected';
}
foreach ($vals as $val) {
if (!in_array($val, [
'git',
'https',
'ssh',
])) {
return 'valid protocols include: git, https, ssh';
}
}
return true;
},
static function ($vals) {
return $vals;
},
],
'github-domains' => [
static function ($vals) {
if (!is_array($vals)) {
return 'array expected';
}
return true;
},
static function ($vals) {
return $vals;
},
],
'gitlab-domains' => [
static function ($vals) {
if (!is_array($vals)) {
return 'array expected';
}
return true;
},
static function ($vals) {
return $vals;
},
],
'audit.ignore' => [
static function ($vals) {
if (!is_array($vals)) {
return 'array expected';
}
return true;
},
static function ($vals) {
return $vals;
},
],
];
// allow unsetting audit config entirely
if ($input->getOption('unset') && $settingKey === 'audit') {
$this->configSource
->removeConfigSetting($settingKey);
return 0;
}
if ($input->getOption('unset') && (isset($uniqueConfigValues[$settingKey]) || isset($multiConfigValues[$settingKey]))) {
if ($settingKey === 'disable-tls' && $this->config
->get('disable-tls')) {
$this->getIO()
->writeError('<info>You are now running Composer with SSL/TLS protection enabled.</info>');
}
$this->configSource
->removeConfigSetting($settingKey);
return 0;
}
if (isset($uniqueConfigValues[$settingKey])) {
$this->handleSingleValue($settingKey, $uniqueConfigValues[$settingKey], $values, 'addConfigSetting');
return 0;
}
if (isset($multiConfigValues[$settingKey])) {
$this->handleMultiValue($settingKey, $multiConfigValues[$settingKey], $values, 'addConfigSetting');
return 0;
}
// handle preferred-install per-package config
if (Preg::isMatch('/^preferred-install\\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->configSource
->removeConfigSetting($settingKey);
return 0;
}
[
$validator,
] = $uniqueConfigValues['preferred-install'];
if (!$validator($values[0])) {
throw new \RuntimeException('Invalid value for ' . $settingKey . '. Should be one of: auto, source, or dist');
}
$this->configSource
->addConfigSetting($settingKey, $values[0]);
return 0;
}
// handle allow-plugins config setting elements true or false to add/remove
if (Preg::isMatch('{^allow-plugins\\.([a-zA-Z0-9/*-]+)}', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->configSource
->removeConfigSetting($settingKey);
return 0;
}
if (true !== $booleanValidator($values[0])) {
throw new \RuntimeException(sprintf('"%s" is an invalid value', $values[0]));
}
$normalizedValue = $booleanNormalizer($values[0]);
$this->configSource
->addConfigSetting($settingKey, $normalizedValue);
return 0;
}
// handle properties
$uniqueProps = [
'name' => [
'is_string',
static function ($val) {
return $val;
},
],
'type' => [
'is_string',
static function ($val) {
return $val;
},
],
'description' => [
'is_string',
static function ($val) {
return $val;
},
],
'homepage' => [
'is_string',
static function ($val) {
return $val;
},
],
'version' => [
'is_string',
static function ($val) {
return $val;
},
],
'minimum-stability' => [
static function ($val) : bool {
return isset(BasePackage::STABILITIES[VersionParser::normalizeStability($val)]);
},
static function ($val) : string {
return VersionParser::normalizeStability($val);
},
],
'prefer-stable' => [
$booleanValidator,
$booleanNormalizer,
],
];
$multiProps = [
'keywords' => [
static function ($vals) {
if (!is_array($vals)) {
return 'array expected';
}
return true;
},
static function ($vals) {
return $vals;
},
],
'license' => [
static function ($vals) {
if (!is_array($vals)) {
return 'array expected';
}
return true;
},
static function ($vals) {
return $vals;
},
],
];
if ($input->getOption('global') && (isset($uniqueProps[$settingKey]) || isset($multiProps[$settingKey]) || strpos($settingKey, 'extra.') === 0)) {
throw new \InvalidArgumentException('The ' . $settingKey . ' property can not be set in the global config.json file. Use `composer global config` to apply changes to the global composer.json');
}
if ($input->getOption('unset') && (isset($uniqueProps[$settingKey]) || isset($multiProps[$settingKey]))) {
$this->configSource
->removeProperty($settingKey);
return 0;
}
if (isset($uniqueProps[$settingKey])) {
$this->handleSingleValue($settingKey, $uniqueProps[$settingKey], $values, 'addProperty');
return 0;
}
if (isset($multiProps[$settingKey])) {
$this->handleMultiValue($settingKey, $multiProps[$settingKey], $values, 'addProperty');
return 0;
}
// handle repositories
if (Preg::isMatchStrictGroups('/^repos?(?:itories)?\\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->configSource
->removeRepository($matches[1]);
return 0;
}
if (2 === count($values)) {
$this->configSource
->addRepository($matches[1], [
'type' => $values[0],
'url' => $values[1],
], $input->getOption('append'));
return 0;
}
if (1 === count($values)) {
$value = strtolower($values[0]);
if (true === $booleanValidator($value)) {
if (false === $booleanNormalizer($value)) {
$this->configSource
->addRepository($matches[1], false, $input->getOption('append'));
return 0;
}
}
else {
$value = JsonFile::parseJson($values[0]);
$this->configSource
->addRepository($matches[1], $value, $input->getOption('append'));
return 0;
}
}
throw new \RuntimeException('You must pass the type and a url. Example: php composer.phar config repositories.foo vcs https://bar.com');
}
// handle extra
if (Preg::isMatch('/^extra\\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->configSource
->removeProperty($settingKey);
return 0;
}
$value = $values[0];
if ($input->getOption('json')) {
$value = JsonFile::parseJson($value);
if ($input->getOption('merge')) {
$currentValue = $this->configFile
->read();
$bits = explode('.', $settingKey);
foreach ($bits as $bit) {
$currentValue = $currentValue[$bit] ?? null;
}
if (is_array($currentValue) && is_array($value)) {
if (array_is_list($currentValue) && array_is_list($value)) {
$value = array_merge($currentValue, $value);
}
else {
$value = $value + $currentValue;
}
}
}
}
$this->configSource
->addProperty($settingKey, $value);
return 0;
}
// handle suggest
if (Preg::isMatch('/^suggest\\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->configSource
->removeProperty($settingKey);
return 0;
}
$this->configSource
->addProperty($settingKey, implode(' ', $values));
return 0;
}
// handle unsetting extra/suggest
if (in_array($settingKey, [
'suggest',
'extra',
], true) && $input->getOption('unset')) {
$this->configSource
->removeProperty($settingKey);
return 0;
}
// handle platform
if (Preg::isMatch('/^platform\\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->configSource
->removeConfigSetting($settingKey);
return 0;
}
$this->configSource
->addConfigSetting($settingKey, $values[0] === 'false' ? false : $values[0]);
return 0;
}
// handle unsetting platform
if ($settingKey === 'platform' && $input->getOption('unset')) {
$this->configSource
->removeConfigSetting($settingKey);
return 0;
}
// handle auth
if (Preg::isMatch('/^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|bearer)\\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->authConfigSource
->removeConfigSetting($matches[1] . '.' . $matches[2]);
$this->configSource
->removeConfigSetting($matches[1] . '.' . $matches[2]);
return 0;
}
if ($matches[1] === 'bitbucket-oauth') {
if (2 !== count($values)) {
throw new \RuntimeException('Expected two arguments (consumer-key, consumer-secret), got ' . count($values));
}
$this->configSource
->removeConfigSetting($matches[1] . '.' . $matches[2]);
$this->authConfigSource
->addConfigSetting($matches[1] . '.' . $matches[2], [
'consumer-key' => $values[0],
'consumer-secret' => $values[1],
]);
}
elseif ($matches[1] === 'gitlab-token' && 2 === count($values)) {
$this->configSource
->removeConfigSetting($matches[1] . '.' . $matches[2]);
$this->authConfigSource
->addConfigSetting($matches[1] . '.' . $matches[2], [
'username' => $values[0],
'token' => $values[1],
]);
}
elseif (in_array($matches[1], [
'github-oauth',
'gitlab-oauth',
'gitlab-token',
'bearer',
], true)) {
if (1 !== count($values)) {
throw new \RuntimeException('Too many arguments, expected only one token');
}
$this->configSource
->removeConfigSetting($matches[1] . '.' . $matches[2]);
$this->authConfigSource
->addConfigSetting($matches[1] . '.' . $matches[2], $values[0]);
}
elseif ($matches[1] === 'http-basic') {
if (2 !== count($values)) {
throw new \RuntimeException('Expected two arguments (username, password), got ' . count($values));
}
$this->configSource
->removeConfigSetting($matches[1] . '.' . $matches[2]);
$this->authConfigSource
->addConfigSetting($matches[1] . '.' . $matches[2], [
'username' => $values[0],
'password' => $values[1],
]);
}
return 0;
}
// handle script
if (Preg::isMatch('/^scripts\\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->configSource
->removeProperty($settingKey);
return 0;
}
$this->configSource
->addProperty($settingKey, count($values) > 1 ? $values : $values[0]);
return 0;
}
// handle unsetting other top level properties
if ($input->getOption('unset')) {
$this->configSource
->removeProperty($settingKey);
return 0;
}
throw new \InvalidArgumentException('Setting ' . $settingKey . ' does not exist or is not supported by this command');
}