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

Breadcrumb

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

class JsonConfigSource

JSON Configuration Source

@author Jordi Boggiano <j.boggiano@seld.be> @author Beau Simensen <beau@dflydev.com>

Hierarchy

  • class \Composer\Config\JsonConfigSource implements \Composer\Config\ConfigSourceInterface

Expanded class hierarchy of JsonConfigSource

4 files declare their use of JsonConfigSource
ConfigCommand.php in vendor/composer/composer/src/Composer/Command/ConfigCommand.php
CreateProjectCommand.php in vendor/composer/composer/src/Composer/Command/CreateProjectCommand.php
Factory.php in vendor/composer/composer/src/Composer/Factory.php
RemoveCommand.php in vendor/composer/composer/src/Composer/Command/RemoveCommand.php

File

vendor/composer/composer/src/Composer/Config/JsonConfigSource.php, line 28

Namespace

Composer\Config
View source
class JsonConfigSource implements ConfigSourceInterface {
    
    /**
     * @var JsonFile
     */
    private $file;
    
    /**
     * @var bool
     */
    private $authConfig;
    
    /**
     * Constructor
     */
    public function __construct(JsonFile $file, bool $authConfig = false) {
        $this->file = $file;
        $this->authConfig = $authConfig;
    }
    
    /**
     * @inheritDoc
     */
    public function getName() : string {
        return $this->file
            ->getPath();
    }
    
    /**
     * @inheritDoc
     */
    public function addRepository(string $name, $config, bool $append = true) : void {
        $this->manipulateJson('addRepository', static function (&$config, $repo, $repoConfig) use ($append) : void {
            // if converting from an array format to hashmap format, and there is a {"packagist.org":false} repo, we have
            // to convert it to "packagist.org": false key on the hashmap otherwise it fails schema validation
            if (isset($config['repositories'])) {
                foreach ($config['repositories'] as $index => $val) {
                    if ($index === $repo) {
                        continue;
                    }
                    if (is_numeric($index) && ($val === [
                        'packagist' => false,
                    ] || $val === [
                        'packagist.org' => false,
                    ])) {
                        unset($config['repositories'][$index]);
                        $config['repositories']['packagist.org'] = false;
                        break;
                    }
                }
            }
            if ($append) {
                $config['repositories'][$repo] = $repoConfig;
            }
            else {
                $config['repositories'] = [
                    $repo => $repoConfig,
                ] + $config['repositories'];
            }
        }, $name, $config, $append);
    }
    
    /**
     * @inheritDoc
     */
    public function removeRepository(string $name) : void {
        $this->manipulateJson('removeRepository', static function (&$config, $repo) : void {
            unset($config['repositories'][$repo]);
        }, $name);
    }
    
    /**
     * @inheritDoc
     */
    public function addConfigSetting(string $name, $value) : void {
        $authConfig = $this->authConfig;
        $this->manipulateJson('addConfigSetting', static function (&$config, $key, $val) use ($authConfig) : void {
            if (Preg::isMatch('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|bearer|http-basic|platform)\\.}', $key)) {
                [
                    $key,
                    $host,
                ] = explode('.', $key, 2);
                if ($authConfig) {
                    $config[$key][$host] = $val;
                }
                else {
                    $config['config'][$key][$host] = $val;
                }
            }
            else {
                $config['config'][$key] = $val;
            }
        }, $name, $value);
    }
    
    /**
     * @inheritDoc
     */
    public function removeConfigSetting(string $name) : void {
        $authConfig = $this->authConfig;
        $this->manipulateJson('removeConfigSetting', static function (&$config, $key) use ($authConfig) : void {
            if (Preg::isMatch('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|bearer|http-basic|platform)\\.}', $key)) {
                [
                    $key,
                    $host,
                ] = explode('.', $key, 2);
                if ($authConfig) {
                    unset($config[$key][$host]);
                }
                else {
                    unset($config['config'][$key][$host]);
                }
            }
            else {
                unset($config['config'][$key]);
            }
        }, $name);
    }
    
    /**
     * @inheritDoc
     */
    public function addProperty(string $name, $value) : void {
        $this->manipulateJson('addProperty', static function (&$config, $key, $val) : void {
            if (strpos($key, 'extra.') === 0 || strpos($key, 'scripts.') === 0) {
                $bits = explode('.', $key);
                $last = array_pop($bits);
                $arr =& $config[reset($bits)];
                foreach ($bits as $bit) {
                    if (!isset($arr[$bit])) {
                        $arr[$bit] = [];
                    }
                    $arr =& $arr[$bit];
                }
                $arr[$last] = $val;
            }
            else {
                $config[$key] = $val;
            }
        }, $name, $value);
    }
    
    /**
     * @inheritDoc
     */
    public function removeProperty(string $name) : void {
        $this->manipulateJson('removeProperty', static function (&$config, $key) : void {
            if (strpos($key, 'extra.') === 0 || strpos($key, 'scripts.') === 0 || stripos($key, 'autoload.') === 0 || stripos($key, 'autoload-dev.') === 0) {
                $bits = explode('.', $key);
                $last = array_pop($bits);
                $arr =& $config[reset($bits)];
                foreach ($bits as $bit) {
                    if (!isset($arr[$bit])) {
                        return;
                    }
                    $arr =& $arr[$bit];
                }
                unset($arr[$last]);
            }
            else {
                unset($config[$key]);
            }
        }, $name);
    }
    
    /**
     * @inheritDoc
     */
    public function addLink(string $type, string $name, string $value) : void {
        $this->manipulateJson('addLink', static function (&$config, $type, $name, $value) : void {
            $config[$type][$name] = $value;
        }, $type, $name, $value);
    }
    
    /**
     * @inheritDoc
     */
    public function removeLink(string $type, string $name) : void {
        $this->manipulateJson('removeSubNode', static function (&$config, $type, $name) : void {
            unset($config[$type][$name]);
        }, $type, $name);
        $this->manipulateJson('removeMainKeyIfEmpty', static function (&$config, $type) : void {
            if (0 === count($config[$type])) {
                unset($config[$type]);
            }
        }, $type);
    }
    
    /**
     * @param mixed ...$args
     */
    private function manipulateJson(string $method, callable $fallback, ...$args) : void {
        if ($this->file
            ->exists()) {
            if (!is_writable($this->file
                ->getPath())) {
                throw new \RuntimeException(sprintf('The file "%s" is not writable.', $this->file
                    ->getPath()));
            }
            if (!Filesystem::isReadable($this->file
                ->getPath())) {
                throw new \RuntimeException(sprintf('The file "%s" is not readable.', $this->file
                    ->getPath()));
            }
            $contents = file_get_contents($this->file
                ->getPath());
        }
        elseif ($this->authConfig) {
            $contents = "{\n}\n";
        }
        else {
            $contents = "{\n    \"config\": {\n    }\n}\n";
        }
        $manipulator = new JsonManipulator($contents);
        $newFile = !$this->file
            ->exists();
        // override manipulator method for auth config files
        if ($this->authConfig && $method === 'addConfigSetting') {
            $method = 'addSubNode';
            [
                $mainNode,
                $name,
            ] = explode('.', $args[0], 2);
            $args = [
                $mainNode,
                $name,
                $args[1],
            ];
        }
        elseif ($this->authConfig && $method === 'removeConfigSetting') {
            $method = 'removeSubNode';
            [
                $mainNode,
                $name,
            ] = explode('.', $args[0], 2);
            $args = [
                $mainNode,
                $name,
            ];
        }
        // try to update cleanly
        if (call_user_func_array([
            $manipulator,
            $method,
        ], $args)) {
            file_put_contents($this->file
                ->getPath(), $manipulator->getContents());
        }
        else {
            // on failed clean update, call the fallback and rewrite the whole file
            $config = $this->file
                ->read();
            $this->arrayUnshiftRef($args, $config);
            $fallback(...$args);
            // avoid ending up with arrays for keys that should be objects
            foreach ([
                'require',
                'require-dev',
                'conflict',
                'provide',
                'replace',
                'suggest',
                'config',
                'autoload',
                'autoload-dev',
                'scripts',
                'scripts-descriptions',
                'scripts-aliases',
                'support',
            ] as $prop) {
                if (isset($config[$prop]) && $config[$prop] === []) {
                    $config[$prop] = new \stdClass();
                }
            }
            foreach ([
                'psr-0',
                'psr-4',
            ] as $prop) {
                if (isset($config['autoload'][$prop]) && $config['autoload'][$prop] === []) {
                    $config['autoload'][$prop] = new \stdClass();
                }
                if (isset($config['autoload-dev'][$prop]) && $config['autoload-dev'][$prop] === []) {
                    $config['autoload-dev'][$prop] = new \stdClass();
                }
            }
            foreach ([
                'platform',
                'http-basic',
                'bearer',
                'gitlab-token',
                'gitlab-oauth',
                'github-oauth',
                'preferred-install',
            ] as $prop) {
                if (isset($config['config'][$prop]) && $config['config'][$prop] === []) {
                    $config['config'][$prop] = new \stdClass();
                }
            }
            $this->file
                ->write($config);
        }
        try {
            $this->file
                ->validateSchema(JsonFile::LAX_SCHEMA);
        } catch (JsonValidationException $e) {
            // restore contents to the original state
            file_put_contents($this->file
                ->getPath(), $contents);
            throw new \RuntimeException('Failed to update composer.json with a valid format, reverting to the original content. Please report an issue to us with details (command you run and a copy of your composer.json). ' . PHP_EOL . implode(PHP_EOL, $e->getErrors()), 0, $e);
        }
        if ($newFile) {
            Silencer::call('chmod', $this->file
                ->getPath(), 0600);
        }
    }
    
    /**
     * Prepend a reference to an element to the beginning of an array.
     *
     * @param  mixed[] $array
     * @param  mixed $value
     */
    private function arrayUnshiftRef(array &$array, &$value) : int {
        $return = array_unshift($array, '');
        $array[0] =& $value;
        return $return;
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title
JsonConfigSource::$authConfig private property
JsonConfigSource::$file private property
JsonConfigSource::addConfigSetting public function @inheritDoc Overrides ConfigSourceInterface::addConfigSetting
JsonConfigSource::addLink public function @inheritDoc Overrides ConfigSourceInterface::addLink
JsonConfigSource::addProperty public function @inheritDoc Overrides ConfigSourceInterface::addProperty
JsonConfigSource::addRepository public function @inheritDoc Overrides ConfigSourceInterface::addRepository
JsonConfigSource::arrayUnshiftRef private function Prepend a reference to an element to the beginning of an array.
JsonConfigSource::getName public function @inheritDoc Overrides ConfigSourceInterface::getName
JsonConfigSource::manipulateJson private function
JsonConfigSource::removeConfigSetting public function @inheritDoc Overrides ConfigSourceInterface::removeConfigSetting
JsonConfigSource::removeLink public function @inheritDoc Overrides ConfigSourceInterface::removeLink
JsonConfigSource::removeProperty public function @inheritDoc Overrides ConfigSourceInterface::removeProperty
JsonConfigSource::removeRepository public function @inheritDoc Overrides ConfigSourceInterface::removeRepository
JsonConfigSource::__construct public function Constructor

API Navigation

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