Browse Source

Add Config class and system-wide config management, fixes #513

Jordi Boggiano 13 years ago
parent
commit
e638182397
3 changed files with 154 additions and 47 deletions
  1. 10 0
      src/Composer/Composer.php
  2. 73 0
      src/Composer/Config.php
  3. 71 47
      src/Composer/Factory.php

+ 10 - 0
src/Composer/Composer.php

@@ -43,6 +43,16 @@ class Composer
         return $this->package;
     }
 
+    public function setConfig(Config $config)
+    {
+        $this->config = $config;
+    }
+
+    public function getConfig()
+    {
+        return $this->config;
+    }
+
     public function setLocker(Locker $locker)
     {
         $this->locker = $locker;

+ 73 - 0
src/Composer/Config.php

@@ -0,0 +1,73 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class Config
+{
+    private $config;
+
+    public function __construct()
+    {
+        // load defaults
+        $this->config = array(
+            'process-timeout' => 300,
+            'vendor-dir' => 'vendor',
+            'bin-dir' => '{$vendor-dir}/bin',
+        );
+    }
+
+    public function merge(array $config)
+    {
+        // override defaults with given config
+        if (!empty($config['config']) && is_array($config['config'])) {
+            $this->config = array_merge_recursive($this->config, $config['config']);
+        }
+    }
+
+    public function get($key)
+    {
+        switch ($key) {
+            case 'vendor-dir':
+            case 'bin-dir':
+            case 'process-timeout':
+                // convert foo-bar to COMPOSER_FOO_BAR and check if it exists since it overrides the local config
+                $env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_'));
+                return $this->process(getenv($env) ?: $this->config[$key]);
+
+            default:
+                return $this->process($this->config[$key]);
+        }
+    }
+
+    public function has($key)
+    {
+        return array_key_exists($key, $this->config);
+    }
+
+    /**
+     * Replaces {$refs} inside a config string
+     *
+     * @param string a config string that can contain {$refs-to-other-config}
+     * @return string
+     */
+    private function process($value)
+    {
+        $config = $this;
+        return preg_replace_callback('#\{\$(.+)\}#', function ($match) use ($config) {
+            return $config->get($match[1]);
+        }, $value);
+    }
+}

+ 71 - 47
src/Composer/Factory.php

@@ -27,66 +27,81 @@ use Composer\Util\RemoteFilesystem;
  */
 class Factory
 {
+    public static function createConfig()
+    {
+        // load main Composer configuration
+        if (!$home = getenv('COMPOSER_HOME')) {
+            if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
+                $home = getenv('APPDATA') . '/Composer/';
+            } else {
+                $home = getenv('HOME') . '/.composer/';
+            }
+        }
+
+        $config = new Config();
+
+        $file = new JsonFile($home.'config.json');
+        if ($file->exists()) {
+            $config->merge($file->read());
+        }
+
+        return $config;
+    }
+
     /**
      * Creates a Composer instance
      *
+     * @param IOInterface $io IO instance
+     * @param mixed $localConfig either a configuration array or a filename to read from, if null it will read from the default filename
      * @return Composer
      */
-    public function createComposer(IOInterface $io, $composerFile = null)
+    public function createComposer(IOInterface $io, $localConfig = null)
     {
         // load Composer configuration
-        if (null === $composerFile) {
-            $composerFile = getenv('COMPOSER') ?: 'composer.json';
+        if (null === $localConfig) {
+            $localConfig = getenv('COMPOSER') ?: 'composer.json';
         }
 
-        $file = new JsonFile($composerFile, new RemoteFilesystem($io));
-        if (!$file->exists()) {
-            if ($composerFile === 'composer.json') {
-                $message = 'Composer could not find a composer.json file in '.getcwd();
-            } else {
-                $message = 'Composer could not find the config file: '.$composerFile;
+        if (is_string($localConfig)) {
+            $composerFile = $localConfig;
+            $file = new JsonFile($localConfig, new RemoteFilesystem($io));
+
+            if (!$file->exists()) {
+                if ($localConfig === 'composer.json') {
+                    $message = 'Composer could not find a composer.json file in '.getcwd();
+                } else {
+                    $message = 'Composer could not find the config file: '.$localConfig;
+                }
+                $instructions = 'To initialize a project, please create a composer.json file as described in the http://getcomposer.org/ "Getting Started" section';
+                throw new \InvalidArgumentException($message.PHP_EOL.$instructions);
             }
-            $instructions = 'To initialize a project, please create a composer.json file as described in the http://getcomposer.org/ "Getting Started" section';
-            throw new \InvalidArgumentException($message.PHP_EOL.$instructions);
+
+            $file->validateSchema(JsonFile::LAX_SCHEMA);
+            $localConfig = $file->read();
         }
 
         // Configuration defaults
-        $composerConfig = array(
-            'vendor-dir' => 'vendor',
-            'process-timeout' => 300,
-        );
-
-        $packageConfig = $file->read();
-        $file->validateSchema(JsonFile::LAX_SCHEMA);
+        $config = $this->createConfig();
+        $config->merge($localConfig);
 
-        if (isset($packageConfig['config']) && is_array($packageConfig['config'])) {
-            $packageConfig['config'] = array_merge($composerConfig, $packageConfig['config']);
-        } else {
-            $packageConfig['config'] = $composerConfig;
-        }
-
-        $vendorDir = getenv('COMPOSER_VENDOR_DIR') ?: $packageConfig['config']['vendor-dir'];
-        if (!isset($packageConfig['config']['bin-dir'])) {
-            $packageConfig['config']['bin-dir'] = $vendorDir.'/bin';
-        }
-        $binDir = getenv('COMPOSER_BIN_DIR') ?: $packageConfig['config']['bin-dir'];
+        $vendorDir = $config->get('vendor-dir');
+        $binDir = $config->get('bin-dir');
 
         // setup process timeout
-        $processTimeout = getenv('COMPOSER_PROCESS_TIMEOUT') ?: $packageConfig['config']['process-timeout'];
-        ProcessExecutor::setTimeout((int) $processTimeout);
+        ProcessExecutor::setTimeout((int) $config->get('process-timeout'));
 
         // initialize repository manager
         $rm = $this->createRepositoryManager($io);
 
         // load default repository unless it's explicitly disabled
-        $packageConfig = $this->addPackagistRepository($packageConfig);
+        $localConfig = $this->addPackagistRepository($localConfig);
 
         // load local repository
         $this->addLocalRepository($rm, $vendorDir);
 
         // load package
         $loader  = new Package\Loader\RootPackageLoader($rm);
-        $package = $loader->load($packageConfig);
+        $package = $loader->load($localConfig);
 
         // initialize download manager
         $dm = $this->createDownloadManager($io);
@@ -97,18 +112,21 @@ class Factory
         // purge packages if they have been deleted on the filesystem
         $this->purgePackages($rm, $im);
 
-        // init locker
-        $lockFile = substr($composerFile, -5) === '.json' ? substr($composerFile, 0, -4).'lock' : $composerFile . '.lock';
-        $locker = new Package\Locker(new JsonFile($lockFile, new RemoteFilesystem($io)), $rm, md5_file($composerFile));
-
         // initialize composer
         $composer = new Composer();
+        $composer->setConfig($config);
         $composer->setPackage($package);
-        $composer->setLocker($locker);
         $composer->setRepositoryManager($rm);
         $composer->setDownloadManager($dm);
         $composer->setInstallationManager($im);
 
+        // init locker if possible
+        if (isset($composerFile)) {
+            $lockFile = substr($composerFile, -5) === '.json' ? substr($composerFile, 0, -4).'lock' : $composerFile . '.lock';
+            $locker = new Package\Locker(new JsonFile($lockFile, new RemoteFilesystem($io)), $rm, md5_file($composerFile));
+            $composer->setLocker($locker);
+        }
+
         return $composer;
     }
 
@@ -131,18 +149,19 @@ class Factory
         $rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/.composer/installed.json')));
     }
 
-    protected function addPackagistRepository(array $packageConfig)
+    protected function addPackagistRepository(array $localConfig)
     {
         $loadPackagist = true;
         $packagistConfig = array(
             'type' => 'composer',
             'url' => 'http://packagist.org'
         );
-        if (isset($packageConfig['repositories'])) {
-            foreach ($packageConfig['repositories'] as $key => $repo) {
+
+        if (isset($localConfig['repositories'])) {
+            foreach ($localConfig['repositories'] as $key => $repo) {
                 if (isset($repo['packagist'])) {
                     if (true === $repo['packagist']) {
-                        $packageConfig['repositories'][$key] = $packagistConfig;
+                        $localConfig['repositories'][$key] = $packagistConfig;
                     }
 
                     $loadPackagist = false;
@@ -150,14 +169,14 @@ class Factory
                 }
             }
         } else {
-            $packageConfig['repositories'] = array();
+            $localConfig['repositories'] = array();
         }
 
         if ($loadPackagist) {
-            $packageConfig['repositories'][] = $packagistConfig;
+            $localConfig['repositories'][] = $packagistConfig;
         }
 
-        return $packageConfig;
+        return $localConfig;
     }
 
     public function createDownloadManager(IOInterface $io)
@@ -194,10 +213,15 @@ class Factory
         }
     }
 
-    static public function create(IOInterface $io, $composerFile = null)
+    /**
+     * @param IOInterface $io IO instance
+     * @param mixed $config either a configuration array or a filename to read from, if null it will read from the default filename
+     * @return Composer
+     */
+    static public function create(IOInterface $io, $config = null)
     {
         $factory = new static();
 
-        return $factory->createComposer($io, $composerFile);
+        return $factory->createComposer($io, $config);
     }
 }