From e638182397963a0c814a91eb0808bd5f7cd1f60f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 9 Apr 2012 16:10:25 +0200 Subject: [PATCH] Add Config class and system-wide config management, fixes #513 --- src/Composer/Composer.php | 10 ++++ src/Composer/Config.php | 73 +++++++++++++++++++++++ src/Composer/Factory.php | 118 +++++++++++++++++++++++--------------- 3 files changed, 154 insertions(+), 47 deletions(-) create mode 100644 src/Composer/Config.php diff --git a/src/Composer/Composer.php b/src/Composer/Composer.php index 54b2c496a..0ad14c450 100644 --- a/src/Composer/Composer.php +++ b/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; diff --git a/src/Composer/Config.php b/src/Composer/Config.php new file mode 100644 index 000000000..b161739c3 --- /dev/null +++ b/src/Composer/Config.php @@ -0,0 +1,73 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +/** + * @author Jordi Boggiano + */ +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); + } +} diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 526ba5bf3..295fabab1 100644 --- a/src/Composer/Factory.php +++ b/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, - ); + $config = $this->createConfig(); + $config->merge($localConfig); - $packageConfig = $file->read(); - $file->validateSchema(JsonFile::LAX_SCHEMA); - - 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); } }