Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. ConfigCommand.php

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\Command

Code

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');
}

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal