Skip to content

Commit

Permalink
Merge pull request composer#551 from Seldaek/config
Browse files Browse the repository at this point in the history
Add Config class and system-wide config management, fixes composer#513
  • Loading branch information
naderman committed Apr 9, 2012
2 parents 61708a1 + fc29487 commit f93ec9d
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 77 deletions.
9 changes: 2 additions & 7 deletions src/Composer/Cache.php
Expand Up @@ -25,15 +25,10 @@ class Cache
private $root;
private $enabled = true;

public function __construct(IOInterface $io, $cacheKey = null)
public function __construct(IOInterface $io, $cacheDir)
{
$this->io = $io;

if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
$this->root = getenv('APPDATA') . rtrim('/Composer/cache/' . $cacheKey, '/') . '/';
} else {
$this->root = getenv('HOME') . rtrim('/.composer/cache/' . $cacheKey, '/') . '/';
}
$this->root = rtrim($cacheDir, '/\\') . '/';

if (!is_dir($this->root)) {
if (!@mkdir($this->root, 0777, true)) {
Expand Down
10 changes: 10 additions & 0 deletions src/Composer/Composer.php
Expand Up @@ -43,6 +43,16 @@ public function getPackage()
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;
Expand Down
93 changes: 93 additions & 0 deletions src/Composer/Config.php
@@ -0,0 +1,93 @@
<?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',
);
}

/**
* Merges new config values with the existing ones (overriding)
*
* @param array $config
*/
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']);
}
}

/**
* Returns a setting
*
* @param string $key
* @return mixed
*/
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]);

case 'home':
return rtrim($this->process($this->config[$key]), '/\\');

default:
return $this->process($this->config[$key]);
}
}

/**
* Checks whether a setting exists
*
* @param string $key
* @return Boolean
*/
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);
}
}
127 changes: 77 additions & 50 deletions src/Composer/Factory.php
Expand Up @@ -27,66 +27,84 @@
*/
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());
}

// add home dir to the config
$config->merge(array('config' => array('home' => $home)));

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);
$rm = $this->createRepositoryManager($io, $config);

// 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);
Expand All @@ -97,24 +115,27 @@ public function createComposer(IOInterface $io, $composerFile = null)
// 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;
}

protected function createRepositoryManager(IOInterface $io)
protected function createRepositoryManager(IOInterface $io, Config $config)
{
$rm = new RepositoryManager($io);
$rm = new RepositoryManager($io, $config);
$rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository');
$rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository');
$rm->setRepositoryClass('package', 'Composer\Repository\PackageRepository');
Expand All @@ -131,33 +152,34 @@ protected function addLocalRepository(RepositoryManager $rm, $vendorDir)
$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;
break;
}
}
} else {
$packageConfig['repositories'] = array();
$localConfig['repositories'] = array();
}

if ($loadPackagist) {
$packageConfig['repositories'][] = $packagistConfig;
$localConfig['repositories'][] = $packagistConfig;
}

return $packageConfig;
return $localConfig;
}

public function createDownloadManager(IOInterface $io)
Expand Down Expand Up @@ -194,10 +216,15 @@ protected function purgePackages(Repository\RepositoryManager $rm, Installer\Ins
}
}

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);
}
}
17 changes: 9 additions & 8 deletions src/Composer/Repository/ComposerRepository.php
Expand Up @@ -16,6 +16,7 @@
use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\Json\JsonFile;
use Composer\Cache;
use Composer\Config;
use Composer\IO\IOInterface;
use Composer\Util\RemoteFilesystem;

Expand All @@ -29,20 +30,20 @@ class ComposerRepository extends ArrayRepository
protected $packages;
protected $cache;

public function __construct(array $config, IOInterface $io)
public function __construct(array $repoConfig, IOInterface $io, Config $config)
{
if (!preg_match('{^\w+://}', $config['url'])) {
if (!preg_match('{^\w+://}', $repoConfig['url'])) {
// assume http as the default protocol
$config['url'] = 'http://'.$config['url'];
$repoConfig['url'] = 'http://'.$repoConfig['url'];
}
$config['url'] = rtrim($config['url'], '/');
if (function_exists('filter_var') && !filter_var($config['url'], FILTER_VALIDATE_URL)) {
throw new \UnexpectedValueException('Invalid url given for Composer repository: '.$config['url']);
$repoConfig['url'] = rtrim($repoConfig['url'], '/');
if (function_exists('filter_var') && !filter_var($repoConfig['url'], FILTER_VALIDATE_URL)) {
throw new \UnexpectedValueException('Invalid url given for Composer repository: '.$repoConfig['url']);
}

$this->url = $config['url'];
$this->url = $repoConfig['url'];
$this->io = $io;
$this->cache = new Cache($io, preg_replace('{[^a-z0-9.]}', '-', $this->url));
$this->cache = new Cache($io, $config->get('home').'/cache/'.preg_replace('{[^a-z0-9.]}', '-', $this->url));
}

protected function initialize()
Expand Down

0 comments on commit f93ec9d

Please sign in to comment.