Finalize platform override feature

- Added tests, docs
- Persist to lock file
- Add support in config command
- Added to json schema
main
Jordi Boggiano 9 years ago
parent 80b0a35a68
commit a57c51e8d7

@ -82,7 +82,7 @@ resolution.
have a proper setup.
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
requirements and force the installation even if the local machine does not
fulfill these.
fulfill these. See also the `platform` [config option](04-schema.md#config).
* **--dry-run:** If you want to run through an installation without actually
installing a package, you can use `--dry-run`. This will simulate the
installation and show you what would happen.
@ -127,7 +127,7 @@ php composer.phar update vendor/*
* **--prefer-dist:** Install packages from `dist` when available.
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
requirements and force the installation even if the local machine does not
fulfill these.
fulfill these. See also the `platform` [config option](04-schema.md#config).
* **--dry-run:** Simulate the command without actually doing anything.
* **--dev:** Install packages listed in `require-dev` (this is the default behavior).
* **--no-dev:** Skip installing packages listed in `require-dev`. The autoloader generation skips the `autoload-dev` rules.
@ -171,7 +171,7 @@ php composer.phar require vendor/package:2.* vendor/package2:dev-master
* **--prefer-dist:** Install packages from `dist` when available.
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
requirements and force the installation even if the local machine does not
fulfill these.
fulfill these. See also the `platform` [config option](04-schema.md#config).
* **--dev:** Add packages to `require-dev`.
* **--no-update:** Disables the automatic update of the dependencies.
* **--no-progress:** Removes the progress display that can mess with some
@ -195,7 +195,7 @@ uninstalled.
### Options
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
requirements and force the installation even if the local machine does not
fulfill these.
fulfill these. See also the `platform` [config option](04-schema.md#config).
* **--dev:** Remove packages from `require-dev`.
* **--no-update:** Disables the automatic update of the dependencies.
* **--no-progress:** Removes the progress display that can mess with some

@ -761,6 +761,9 @@ The following options are supported:
against them. For example using
`{"example.org": {"username": "alice", "password": "foo"}` as the value of this
option will let composer authenticate against example.org.
* **platform:** Lets you fake platform packages (PHP and extensions) so that
you can emulate a production env or define your target platform in the
config. e.g. `{"php": "5.4", "ext-something": "4.0"}`.
* **vendor-dir:** Defaults to `vendor`. You can install dependencies into a
different directory if you want to. `$HOME` and `~` will be replaced by your
home directory's path in vendor-dir and all `*-dir` options below.

@ -145,6 +145,11 @@
"type": ["string", "boolean"],
"description": "What to do after prompting for authentication, one of: true (store), false (do not store) or \"prompt\" (ask every time), defaults to prompt."
},
"platform": {
"type": "object",
"description": "This is a hash of package name (keys) and version (values) that will be used to mock the platform packages on this machine.",
"additionalProperties": true
},
"vendor-dir": {
"type": "string",
"description": "The location where all packages are installed, defaults to \"vendor\"."

@ -410,6 +410,15 @@ EOT
throw new \RuntimeException('You must pass the type and a url. Example: php composer.phar config repositories.foo vcs http://bar.com');
}
// handle platform
if (preg_match('/^platform\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
return $this->configSource->removeConfigSetting($settingKey);
}
return $this->configSource->addConfigSetting($settingKey, $values[0]);
}
// handle github-oauth
if (preg_match('/^(github-oauth|http-basic)\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {

@ -79,7 +79,7 @@ class JsonConfigSource implements ConfigSourceInterface
public function addConfigSetting($name, $value)
{
$this->manipulateJson('addConfigSetting', $name, $value, function (&$config, $key, $val) {
if ($key === 'github-oauth' || $key === 'http-basic') {
if (preg_match('{^(github-oauth|http-basic|platform)\.}', $key)) {
list($key, $host) = explode('.', $key, 2);
if ($this->authConfig) {
$config[$key][$host] = $val;
@ -98,7 +98,7 @@ class JsonConfigSource implements ConfigSourceInterface
public function removeConfigSetting($name)
{
$this->manipulateJson('removeConfigSetting', $name, function (&$config, $key) {
if ($key === 'github-oauth' || $key === 'http-basic') {
if (preg_match('{^(github-oauth|http-basic|platform)\.}', $key)) {
list($key, $host) = explode('.', $key, 2);
if ($this->authConfig) {
unset($config[$key][$host]);

@ -206,9 +206,12 @@ class Installer
// create installed repo, this contains all local packages + platform packages (php & extensions)
$localRepo = $this->repositoryManager->getLocalRepository();
$platformOverride = $this->config->get('platform');
$platformOverride = is_array($platformOverride) ? $platformOverride : array();
$platformRepo = new PlatformRepository($platformOverride);
if (!$this->update && $this->locker->isLocked()) {
$platformOverrides = $this->locker->getPlatformOverrides();
} else {
$platformOverrides = $this->config->get('platform') ?: array();
}
$platformRepo = new PlatformRepository(array(), $platformOverrides);
$repos = array(
$localRepo,
new InstalledArrayRepository(array($installedRootPackage)),
@ -313,7 +316,8 @@ class Installer
$this->package->getMinimumStability(),
$this->package->getStabilityFlags(),
$this->preferStable || $this->package->getPreferStable(),
$this->preferLowest
$this->preferLowest,
$this->config->get('platform') ?: array()
);
if ($updatedLock) {
$this->io->writeError('<info>Writing lock file</info>');

@ -191,6 +191,13 @@ class Locker
return isset($lockData['prefer-lowest']) ? $lockData['prefer-lowest'] : null;
}
public function getPlatformOverrides()
{
$lockData = $this->getLockData();
return isset($lockData['platform-overrides']) ? $lockData['platform-overrides'] : array();
}
public function getAliases()
{
$lockData = $this->getLockData();
@ -214,19 +221,20 @@ class Locker
/**
* Locks provided data into lockfile.
*
* @param array $packages array of packages
* @param mixed $devPackages array of dev packages or null if installed without --dev
* @param array $platformReqs array of package name => constraint for required platform packages
* @param mixed $platformDevReqs array of package name => constraint for dev-required platform packages
* @param array $aliases array of aliases
* @param array $packages array of packages
* @param mixed $devPackages array of dev packages or null if installed without --dev
* @param array $platformReqs array of package name => constraint for required platform packages
* @param mixed $platformDevReqs array of package name => constraint for dev-required platform packages
* @param array $aliases array of aliases
* @param string $minimumStability
* @param array $stabilityFlags
* @param bool $preferStable
* @param bool $preferLowest
* @param array $platformOverrides
*
* @return bool
*/
public function setLockData(array $packages, $devPackages, array $platformReqs, $platformDevReqs, array $aliases, $minimumStability, array $stabilityFlags, $preferStable, $preferLowest)
public function setLockData(array $packages, $devPackages, array $platformReqs, $platformDevReqs, array $aliases, $minimumStability, array $stabilityFlags, $preferStable, $preferLowest, array $platformOverrides)
{
$lock = array(
'_readme' => array('This file locks the dependencies of your project to a known state',
@ -260,6 +268,9 @@ class Locker
$lock['platform'] = $platformReqs;
$lock['platform-dev'] = $platformDevReqs;
if ($platformOverrides) {
$lock['platform-overrides'] = $platformOverrides;
}
if (empty($lock['packages']) && empty($lock['packages-dev']) && empty($lock['platform']) && empty($lock['platform-dev'])) {
if ($this->lockFile->exists()) {

@ -24,12 +24,22 @@ class PlatformRepository extends ArrayRepository
{
const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit)?|hhvm|(?:ext|lib)-[^/]+)$}i';
/**
* Defines overrides so that the platform can be mocked
*
* Should be an array of package name => version number mappings
*
* @var array
*/
private $overrides;
public function __construct(array $overrides = array())
public function __construct(array $packages = array(), array $overrides = array())
{
parent::__construct(array());
$this->overrides = $overrides;
parent::__construct($packages);
$this->overrides = array();
foreach ($overrides as $name => $version) {
$this->overrides[strtolower($name)] = array('name' => $name, 'version' => $version);
}
}
protected function initialize()
@ -40,19 +50,17 @@ class PlatformRepository extends ArrayRepository
// Add each of the override versions as options.
// Later we might even replace the extensions instead.
foreach( $this->overrides as $name => $prettyVersion ) {
foreach ($this->overrides as $override) {
// Check that it's a platform package.
if( preg_match(self::PLATFORM_PACKAGE_REGEX, $name) ) {
$version = $versionParser->normalize($prettyVersion);
$package = new CompletePackage($name, $version, $prettyVersion);
$package->setDescription("Overridden virtual platform package $name.");
parent::addPackage($package);
}
else {
throw new \InvalidArgumentException('Invalid platform package '.$name);
if (!preg_match(self::PLATFORM_PACKAGE_REGEX, $override['name'])) {
throw new \InvalidArgumentException('Invalid platform package name in config.platform: '.$override['name']);
}
}
$version = $versionParser->normalize($override['version']);
$package = new CompletePackage($override['name'], $version, $override['version']);
$package->setDescription('Overridden virtual platform package '.$override['name']);
parent::addPackage($package);
}
$prettyVersion = PluginInterface::PLUGIN_API_VERSION;
$version = $versionParser->normalize($prettyVersion);
@ -186,21 +194,13 @@ class PlatformRepository extends ArrayRepository
}
}
// TODO: Is it a good thing to redefine the public interface
// like this, or is it better to make the "only-add-if-no-in-platform"
// feature in a
// protected function addOverriddenPackage()
// instead?
/**
* {@inheritDoc}
*/
public function addPackage(PackageInterface $package)
{
/*
If we can find the package in this repository,
in any version, it can only mean that it has been
added by the config key 'platform' and should
the real package (i.e. this one) should not be added.
*/
if( count($this->findPackages($package->getName())) > 0 ) {
// Log a warning that we're ignoring existing package?
// Skip if overridden
if (isset($this->overrides[strtolower($package->getName())])) {
return;
}
parent::addPackage($package);

@ -0,0 +1,29 @@
--TEST--
Install overridden platform requirements works
--COMPOSER--
{
"repositories": [
{
"type": "package",
"package": [
{ "name": "a/a", "version": "1.0.0", "require": { "ext-dummy2": "1.*" } }
]
}
],
"require": {
"a/a": "*",
"ext-dummy": "~1.0",
"php": "1.0"
},
"config": {
"platform": {
"php": "1.0.0",
"ext-dummy": "1.2.3",
"ext-dummy2": "1.2.3"
}
}
}
--RUN--
install
--EXPECT--
Installing a/a (1.0.0)

@ -134,11 +134,12 @@ class LockerTest extends \PHPUnit_Framework_TestCase
'stability-flags' => array(),
'platform' => array(),
'platform-dev' => array(),
'platform-overrides' => array('foo/bar' => '1.0'),
'prefer-stable' => false,
'prefer-lowest' => false,
));
$locker->setLockData(array($package1, $package2), array(), array(), array(), array(), 'dev', array(), false, false);
$locker->setLockData(array($package1, $package2), array(), array(), array(), array(), 'dev', array(), false, false, array('foo/bar' => '1.0'));
}
public function testLockBadPackages()
@ -157,7 +158,7 @@ class LockerTest extends \PHPUnit_Framework_TestCase
$this->setExpectedException('LogicException');
$locker->setLockData(array($package1), array(), array(), array(), array(), 'dev', array(), false, false);
$locker->setLockData(array($package1), array(), array(), array(), array(), 'dev', array(), false, false, array());
}
public function testIsFresh()

Loading…
Cancel
Save