From afbb9cefa4f49088f87b85b0e75d7b1f98b9f0be Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 21 Feb 2012 11:58:17 +0100 Subject: [PATCH 1/7] Adjust version parser so it persists self.version references --- src/Composer/Package/Loader/ArrayLoader.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 7c992392c..c13150953 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -162,9 +162,10 @@ class ArrayLoader $links = array(); foreach ($linksSpecs as $packageName => $constraint) { if ('self.version' === $constraint) { - $constraint = $package->getPrettyVersion(); + $parsedConstraint = $this->versionParser->parseConstraints($package->getPrettyVersion()); + } else { + $parsedConstraint = $this->versionParser->parseConstraints($constraint); } - $parsedConstraint = $this->versionParser->parseConstraints($constraint); $links[] = new Package\Link($package->getName(), $packageName, $parsedConstraint, $description, $constraint); } From 5eb333680b027d3d86f9230e78800b21831290cb Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 21 Feb 2012 14:02:08 +0100 Subject: [PATCH 2/7] Rename findPackagesByName to findPackages and allow version arg --- src/Composer/Command/ShowCommand.php | 4 +- src/Composer/Repository/ArrayRepository.php | 11 ++++- .../Repository/CompositeRepository.php | 4 +- .../Repository/RepositoryInterface.php | 7 +-- src/Composer/Repository/RepositoryManager.php | 19 ++++++++ .../Test/Repository/ArrayRepositoryTest.php | 6 +-- .../Repository/CompositeRepositoryTest.php | 48 +++++++++---------- 7 files changed, 63 insertions(+), 36 deletions(-) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 4450966b3..e376dad32 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -118,7 +118,7 @@ EOT // we only have a name, so search for the highest version of the given package $highestVersion = null; - foreach ($repos->findPackagesByName($input->getArgument('package')) as $package) { + foreach ($repos->findPackages($input->getArgument('package')) as $package) { if (null === $highestVersion || version_compare($package->getVersion(), $highestVersion->getVersion(), '>=')) { $highestVersion = $package; } @@ -164,7 +164,7 @@ EOT $versions = array(); - foreach ($repos->findPackagesByName($package->getName()) as $version) { + foreach ($repos->findPackages($package->getName()) as $version) { $versions[] = $version->getPrettyVersion(); } diff --git a/src/Composer/Repository/ArrayRepository.php b/src/Composer/Repository/ArrayRepository.php index 36ed56b0b..a46bfd20d 100644 --- a/src/Composer/Repository/ArrayRepository.php +++ b/src/Composer/Repository/ArrayRepository.php @@ -44,14 +44,21 @@ class ArrayRepository implements RepositoryInterface /** * {@inheritDoc} */ - public function findPackagesByName($name) + public function findPackages($name, $version = null) { // normalize name $name = strtolower($name); + + // normalize version + if (null !== $version) { + $versionParser = new VersionParser(); + $version = $versionParser->normalize($version); + } + $packages = array(); foreach ($this->getPackages() as $package) { - if ($package->getName() === $name) { + if ($package->getName() === $name && (null === $version || $version === $package->getVersion())) { $packages[] = $package; } } diff --git a/src/Composer/Repository/CompositeRepository.php b/src/Composer/Repository/CompositeRepository.php index eccf5f230..e000d97e8 100644 --- a/src/Composer/Repository/CompositeRepository.php +++ b/src/Composer/Repository/CompositeRepository.php @@ -68,12 +68,12 @@ class CompositeRepository implements RepositoryInterface /** * {@inheritdoc} */ - public function findPackagesByName($name) + public function findPackages($name, $version = null) { $packages = array(); foreach ($this->repositories as $repository) { /* @var $repository RepositoryInterface */ - $packages[] = $repository->findPackagesByName($name); + $packages[] = $repository->findPackages($name, $version); } return call_user_func_array('array_merge', $packages); } diff --git a/src/Composer/Repository/RepositoryInterface.php b/src/Composer/Repository/RepositoryInterface.php index 2c1f7d43c..e4a79695d 100644 --- a/src/Composer/Repository/RepositoryInterface.php +++ b/src/Composer/Repository/RepositoryInterface.php @@ -32,7 +32,7 @@ interface RepositoryInterface extends \Countable function hasPackage(PackageInterface $package); /** - * Searches for a package by it's name and version (if has one). + * Searches for the first match of a package by name and version. * * @param string $name package name * @param string $version package version @@ -42,13 +42,14 @@ interface RepositoryInterface extends \Countable function findPackage($name, $version); /** - * Searches for packages by it's name. + * Searches for all packages matching a name and optionally a version. * * @param string $name package name + * @param string $version package version * * @return array */ - function findPackagesByName($name); + function findPackages($name, $version = null); /** * Returns list of registered packages. diff --git a/src/Composer/Repository/RepositoryManager.php b/src/Composer/Repository/RepositoryManager.php index 1ed0f18d0..4935ef68c 100644 --- a/src/Composer/Repository/RepositoryManager.php +++ b/src/Composer/Repository/RepositoryManager.php @@ -50,6 +50,25 @@ class RepositoryManager } } + /** + * Searches for all packages matching a name and optionally a version in managed repositories. + * + * @param string $name package name + * @param string $version package version + * + * @return array + */ + public function findPackages($name, $version) + { + $packages = array(); + + foreach ($this->repositories as $repository) { + $packages = array_merge($packages, $repository->findPackages($name, $version)); + } + + return $packages; + } + /** * Adds repository * diff --git a/tests/Composer/Test/Repository/ArrayRepositoryTest.php b/tests/Composer/Test/Repository/ArrayRepositoryTest.php index 5e29f5ed6..ed05819b6 100644 --- a/tests/Composer/Test/Repository/ArrayRepositoryTest.php +++ b/tests/Composer/Test/Repository/ArrayRepositoryTest.php @@ -51,18 +51,18 @@ class ArrayRepositoryTest extends TestCase $this->assertFalse($repo->hasPackage($this->getPackage('bar', '1'))); } - public function testFindPackagesByName() + public function testFindPackages() { $repo = new ArrayRepository(); $repo->addPackage($this->getPackage('foo', '1')); $repo->addPackage($this->getPackage('bar', '2')); $repo->addPackage($this->getPackage('bar', '3')); - $foo = $repo->findPackagesByName('foo'); + $foo = $repo->findPackages('foo'); $this->assertCount(1, $foo); $this->assertEquals('foo', $foo[0]->getName()); - $bar = $repo->findPackagesByName('bar'); + $bar = $repo->findPackages('bar'); $this->assertCount(2, $bar); $this->assertEquals('bar', $bar[0]->getName()); } diff --git a/tests/Composer/Test/Repository/CompositeRepositoryTest.php b/tests/Composer/Test/Repository/CompositeRepositoryTest.php index 49e017d18..c71196729 100644 --- a/tests/Composer/Test/Repository/CompositeRepositoryTest.php +++ b/tests/Composer/Test/Repository/CompositeRepositoryTest.php @@ -22,15 +22,15 @@ class CompositeRepositoryTest extends TestCase { $arrayRepoOne = new ArrayRepository; $arrayRepoOne->addPackage($this->getPackage('foo', '1')); - + $arrayRepoTwo = new ArrayRepository; $arrayRepoTwo->addPackage($this->getPackage('bar', '1')); - + $repo = new CompositeRepository(array($arrayRepoOne, $arrayRepoTwo)); - + $this->assertTrue($repo->hasPackage($this->getPackage('foo', '1')), "Should have package 'foo/1'"); $this->assertTrue($repo->hasPackage($this->getPackage('bar', '1')), "Should have package 'bar/1'"); - + $this->assertFalse($repo->hasPackage($this->getPackage('foo', '2')), "Should not have package 'foo/2'"); $this->assertFalse($repo->hasPackage($this->getPackage('bar', '2')), "Should not have package 'bar/2'"); } @@ -39,12 +39,12 @@ class CompositeRepositoryTest extends TestCase { $arrayRepoOne = new ArrayRepository; $arrayRepoOne->addPackage($this->getPackage('foo', '1')); - + $arrayRepoTwo = new ArrayRepository; $arrayRepoTwo->addPackage($this->getPackage('bar', '1')); - + $repo = new CompositeRepository(array($arrayRepoOne, $arrayRepoTwo)); - + $this->assertEquals('foo', $repo->findPackage('foo', '1')->getName(), "Should find package 'foo/1' and get name of 'foo'"); $this->assertEquals('1', $repo->findPackage('foo', '1')->getPrettyVersion(), "Should find package 'foo/1' and get pretty version of '1'"); $this->assertEquals('bar', $repo->findPackage('bar', '1')->getName(), "Should find package 'bar/1' and get name of 'bar'"); @@ -52,7 +52,7 @@ class CompositeRepositoryTest extends TestCase $this->assertNull($repo->findPackage('foo', '2'), "Should not find package 'foo/2'"); } - public function testFindPackagesByName() + public function testFindPackages() { $arrayRepoOne = new ArrayRepository; $arrayRepoOne->addPackage($this->getPackage('foo', '1')); @@ -63,32 +63,32 @@ class CompositeRepositoryTest extends TestCase $arrayRepoTwo->addPackage($this->getPackage('bar', '1')); $arrayRepoTwo->addPackage($this->getPackage('bar', '2')); $arrayRepoTwo->addPackage($this->getPackage('foo', '3')); - + $repo = new CompositeRepository(array($arrayRepoOne, $arrayRepoTwo)); - - $bats = $repo->findPackagesByName('bat'); + + $bats = $repo->findPackages('bat'); $this->assertCount(1, $bats, "Should find one instance of 'bats' (defined in just one repository)"); $this->assertEquals('bat', $bats[0]->getName(), "Should find packages named 'bat'"); - $bars = $repo->findPackagesByName('bar'); + $bars = $repo->findPackages('bar'); $this->assertCount(2, $bars, "Should find two instances of 'bar' (both defined in the same repository)"); $this->assertEquals('bar', $bars[0]->getName(), "Should find packages named 'bar'"); - - $foos = $repo->findPackagesByName('foo'); + + $foos = $repo->findPackages('foo'); $this->assertCount(3, $foos, "Should find three instances of 'foo' (two defined in one repository, the third in the other)"); $this->assertEquals('foo', $foos[0]->getName(), "Should find packages named 'foo'"); } - + public function testGetPackages() { $arrayRepoOne = new ArrayRepository; $arrayRepoOne->addPackage($this->getPackage('foo', '1')); - + $arrayRepoTwo = new ArrayRepository; $arrayRepoTwo->addPackage($this->getPackage('bar', '1')); - + $repo = new CompositeRepository(array($arrayRepoOne, $arrayRepoTwo)); - + $packages = $repo->getPackages(); $this->assertCount(2, $packages, "Should get two packages"); $this->assertEquals("foo", $packages[0]->getName(), "First package should have name of 'foo'"); @@ -96,17 +96,17 @@ class CompositeRepositoryTest extends TestCase $this->assertEquals("bar", $packages[1]->getName(), "Second package should have name of 'bar'"); $this->assertEquals("1", $packages[1]->getPrettyVersion(), "Second package should have pretty version of '1'"); } - + public function testAddRepository() { $arrayRepoOne = new ArrayRepository; $arrayRepoOne->addPackage($this->getPackage('foo', '1')); - + $arrayRepoTwo = new ArrayRepository; $arrayRepoTwo->addPackage($this->getPackage('bar', '1')); $arrayRepoTwo->addPackage($this->getPackage('bar', '2')); $arrayRepoTwo->addPackage($this->getPackage('bar', '3')); - + $repo = new CompositeRepository(array($arrayRepoOne)); $this->assertCount(1, $repo, "Composite repository should have just one package before addRepository() is called"); $repo->addRepository($arrayRepoTwo); @@ -117,12 +117,12 @@ class CompositeRepositoryTest extends TestCase { $arrayRepoOne = new ArrayRepository; $arrayRepoOne->addPackage($this->getPackage('foo', '1')); - + $arrayRepoTwo = new ArrayRepository; $arrayRepoTwo->addPackage($this->getPackage('bar', '1')); - + $repo = new CompositeRepository(array($arrayRepoOne, $arrayRepoTwo)); - + $this->assertEquals(2, count($repo), "Should return '2' for count(\$repo)"); } } From 82054f2060bc31ee9260b4a01497c343cc78c281 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 21 Feb 2012 14:03:24 +0100 Subject: [PATCH 3/7] Add aliasing: `foo as bar` will require foo and automatically make it replace the bar version --- src/Composer/Command/InstallCommand.php | 30 +++++++++++++++++-- .../Package/Loader/RootPackageLoader.php | 15 ++++++++++ src/Composer/Package/MemoryPackage.php | 17 +++++++++++ .../Package/Version/VersionParser.php | 5 ++++ .../Package/Version/VersionParserTest.php | 1 + 5 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 5885b033d..1644b8068 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -21,6 +21,7 @@ use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; use Composer\DependencyResolver\Operation; use Composer\Package\MemoryPackage; +use Composer\Package\Link; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\PackageInterface; use Composer\Repository\CompositeRepository; @@ -92,8 +93,10 @@ EOT $composer->getDownloadManager()->setPreferSource(true); } + $repoManager = $composer->getRepositoryManager(); + // create local repo, this contains all packages that are installed in the local project - $localRepo = $composer->getRepositoryManager()->getLocalRepository(); + $localRepo = $repoManager->getLocalRepository(); // create installed repo, this contains all local packages + platform packages (php & extensions) $installedRepo = new CompositeRepository(array($localRepo, new PlatformRepository())); if ($additionalInstalledRepository) { @@ -103,7 +106,7 @@ EOT // creating repository pool $pool = new Pool; $pool->addRepository($installedRepo); - foreach ($composer->getRepositoryManager()->getRepositories() as $repository) { + foreach ($repoManager->getRepositories() as $repository) { $pool->addRepository($repository); } @@ -148,6 +151,27 @@ EOT } } + // prepare aliased packages + foreach ($composer->getPackage()->getAliases() as $alias) { + $candidates = array_merge( + $repoManager->findPackages($alias['package'], $alias['version']), + $repoManager->getLocalRepository()->findPackages($alias['package'], $alias['version']) + ); + foreach ($candidates as $package) { + $replaces = $package->getReplaces(); + foreach ($replaces as $index => $link) { + // link is self.version, but must be replacing also the replaced version + if ('self.version' === $link->getPrettyConstraint()) { + $replaces[] = new Link($link->getSource(), $link->getTarget(), new VersionConstraint('=', $alias['replaces']), 'replaces', $alias['replaces']); + } + } + + // add replace of itself + $replaces[] = new Link($alias['package'], $alias['package'], new VersionConstraint('=', $alias['replaces']), 'replaces', $alias['replaces']); + $package->setReplaces($replaces); + } + } + // prepare solver $installationManager = $composer->getInstallationManager(); $policy = new DependencyResolver\DefaultPolicy(); @@ -179,7 +203,7 @@ EOT } // force update - $newPackage = $composer->getRepositoryManager()->findPackage($package->getName(), $package->getVersion()); + $newPackage = $repoManager->findPackage($package->getName(), $package->getVersion()); if ($newPackage && $newPackage->getSourceReference() !== $package->getSourceReference()) { $operations[] = new UpdateOperation($package, $newPackage); } diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 26765c78a..657ff482c 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -43,6 +43,21 @@ class RootPackageLoader extends ArrayLoader $package = parent::load($config); + if (isset($config['require'])) { + $aliases = array(); + foreach ($config['require'] as $reqName => $reqVersion) { + if (preg_match('{^([^,\s]+) +as +([^,\s]+)$}', $reqVersion, $match)) { + $aliases[] = array( + 'package' => strtolower($reqName), + 'version' => $this->versionParser->normalize($match[1]), + 'replaces' => $this->versionParser->normalize($match[2]), + ); + } + } + + $package->setAliases($aliases); + } + if (isset($config['repositories'])) { foreach ($config['repositories'] as $index => $repo) { if (isset($repo['packagist']) && $repo['packagist'] === false) { diff --git a/src/Composer/Package/MemoryPackage.php b/src/Composer/Package/MemoryPackage.php index a57f2b6f6..a2a711027 100644 --- a/src/Composer/Package/MemoryPackage.php +++ b/src/Composer/Package/MemoryPackage.php @@ -41,6 +41,7 @@ class MemoryPackage extends BasePackage protected $extra = array(); protected $binaries = array(); protected $scripts = array(); + protected $aliases = array(); protected $dev; protected $requires = array(); @@ -156,6 +157,22 @@ class MemoryPackage extends BasePackage return $this->scripts; } + /** + * @param array $aliases + */ + public function setAliases(array $aliases) + { + $this->aliases = $aliases; + } + + /** + * {@inheritDoc} + */ + public function getAliases() + { + return $this->aliases; + } + /** * {@inheritDoc} */ diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 716c24731..36a16ae09 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -34,6 +34,11 @@ class VersionParser { $version = trim($version); + // ignore aliases and just assume the alias is required instead of the source + if (preg_match('{^([^,\s]+) +as +([^,\s]+)$}', $version, $match)) { + $version = $match[1]; + } + // match master-like branches if (preg_match('{^(?:dev-)?(?:master|trunk|default)$}i', $version)) { return '9999999-dev'; diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index 64a3e79f4..d7336bd5a 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -53,6 +53,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase 'parses trunk' => array('dev-trunk', '9999999-dev'), 'parses arbitrary' => array('dev-feature-foo', 'dev-feature-foo'), 'parses arbitrary2' => array('DEV-FOOBAR', 'dev-foobar'), + 'ignores aliases' => array('dev-master as 1.0.0', '9999999-dev'), ); } From f9fc9695e9239d35b7bb7adcb8de2c9e2df20832 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 21 Feb 2012 15:32:45 +0100 Subject: [PATCH 4/7] Wrap the package in an aliased package instead of replacing --- src/Composer/Command/InstallCommand.php | 37 ++- .../Installer/InstallationManager.php | 22 +- src/Composer/Package/AliasPackage.php | 227 ++++++++++++++++++ src/Composer/Package/BasePackage.php | 10 + src/Composer/Package/Loader/ArrayLoader.php | 11 +- 5 files changed, 272 insertions(+), 35 deletions(-) create mode 100644 src/Composer/Package/AliasPackage.php diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 1644b8068..d90b05b07 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -20,10 +20,12 @@ use Composer\DependencyResolver; use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; use Composer\DependencyResolver\Operation; +use Composer\Package\AliasPackage; use Composer\Package\MemoryPackage; use Composer\Package\Link; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\PackageInterface; +use Composer\Repository\ArrayRepository; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryInterface; @@ -103,6 +105,20 @@ EOT $installedRepo->addRepository($additionalInstalledRepository); } + // prepare aliased packages + $aliasRepo = new ArrayRepository; + foreach ($composer->getPackage()->getAliases() as $alias) { + foreach ($repoManager->findPackages($alias['package'], $alias['version']) as $package) { + $aliasRepo->addPackage(new AliasPackage($package, $alias['replaces'])); + } + + foreach ($repoManager->getLocalRepository()->findPackages($alias['package'], $alias['version']) as $package) { + $repoManager->getLocalRepository()->addPackage(new AliasPackage($package, $alias['replaces'])); + $repoManager->getLocalRepository()->removePackage($package); + } + } + $repoManager->addRepository($aliasRepo); + // creating repository pool $pool = new Pool; $pool->addRepository($installedRepo); @@ -151,27 +167,6 @@ EOT } } - // prepare aliased packages - foreach ($composer->getPackage()->getAliases() as $alias) { - $candidates = array_merge( - $repoManager->findPackages($alias['package'], $alias['version']), - $repoManager->getLocalRepository()->findPackages($alias['package'], $alias['version']) - ); - foreach ($candidates as $package) { - $replaces = $package->getReplaces(); - foreach ($replaces as $index => $link) { - // link is self.version, but must be replacing also the replaced version - if ('self.version' === $link->getPrettyConstraint()) { - $replaces[] = new Link($link->getSource(), $link->getTarget(), new VersionConstraint('=', $alias['replaces']), 'replaces', $alias['replaces']); - } - } - - // add replace of itself - $replaces[] = new Link($alias['package'], $alias['package'], new VersionConstraint('=', $alias['replaces']), 'replaces', $alias['replaces']); - $package->setReplaces($replaces); - } - } - // prepare solver $installationManager = $composer->getInstallationManager(); $policy = new DependencyResolver\DefaultPolicy(); diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index 2acb09d5b..d2f0e2a6b 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -123,8 +123,12 @@ class InstallationManager */ public function install(InstallOperation $operation) { - $installer = $this->getInstaller($operation->getPackage()->getType()); - $installer->install($operation->getPackage()); + $package = $operation->getPackage(); + if ($package instanceof AliasPackage) { + $package = $package->getAliasOf(); + } + $installer = $this->getInstaller($package->getType()); + $installer->install($package); } /** @@ -135,7 +139,13 @@ class InstallationManager public function update(UpdateOperation $operation) { $initial = $operation->getInitialPackage(); + if ($initial instanceof AliasPackage) { + $initial = $initial->getAliasOf(); + } $target = $operation->getTargetPackage(); + if ($target instanceof AliasPackage) { + $target = $target->getAliasOf(); + } $initialType = $initial->getType(); $targetType = $target->getType(); @@ -156,8 +166,12 @@ class InstallationManager */ public function uninstall(UninstallOperation $operation) { - $installer = $this->getInstaller($operation->getPackage()->getType()); - $installer->uninstall($operation->getPackage()); + $package = $operation->getPackage(); + if ($package instanceof AliasPackage) { + $package = $package->getAliasOf(); + } + $installer = $this->getInstaller($package->getType()); + $installer->uninstall($package); } /** diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php new file mode 100644 index 000000000..3665fd30a --- /dev/null +++ b/src/Composer/Package/AliasPackage.php @@ -0,0 +1,227 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package; + +use Composer\Package\LinkConstraint\LinkConstraintInterface; +use Composer\Package\LinkConstraint\VersionConstraint; +use Composer\Repository\RepositoryInterface; +use Composer\Repository\PlatformRepository; + +/** + * @author Jordi Boggiano + */ +class AliasPackage extends BasePackage +{ + protected $version; + protected $dev; + protected $aliasOf; + + protected $requires; + protected $conflicts; + protected $provides; + protected $replaces; + protected $recommends; + protected $suggests; + + /** + * All descendants' constructors should call this parent constructor + * + * @param PackageInterface $aliasOf The package this package is an alias of + * @param string $version The version the alias must report + */ + public function __construct($aliasOf, $version) + { + parent::__construct($aliasOf->getName()); + + $this->version = $version; + $this->aliasOf = $aliasOf; + $this->dev = 'dev-' === substr($version, 0, 4) || '-dev' === substr($version, -4); + + foreach (self::$supportedLinkTypes as $type => $description) { + $links = $aliasOf->{'get'.ucfirst($description)}(); + $newLinks = array(); + foreach ($links as $link) { + // link is self.version, but must be replacing also the replaced version + if ('self.version' === $link->getPrettyConstraint()) { + $newLinks[] = new Link($link->getSource(), $link->getTarget(), new VersionConstraint('=', $this->version), $description, $this->version); + } + } + $this->$description = array_merge($links, $newLinks); + } + } + + /** + * {@inheritDoc} + */ + public function getVersion() + { + return $this->version; + } + + /** + * {@inheritDoc} + */ + public function getPrettyVersion() + { + return $this->version; + } + + /** + * {@inheritDoc} + */ + public function isDev() + { + return $this->dev; + } + + /** + * {@inheritDoc} + */ + function getRequires() + { + return $this->requires; + } + + /** + * {@inheritDoc} + */ + function getConflicts() + { + return $this->conflicts; + } + + /** + * {@inheritDoc} + */ + function getProvides() + { + return $this->provides; + } + + /** + * {@inheritDoc} + */ + function getReplaces() + { + return $this->replaces; + } + + /** + * {@inheritDoc} + */ + function getRecommends() + { + return $this->recommends; + } + + /** + * {@inheritDoc} + */ + function getSuggests() + { + return $this->suggests; + } + + /*************************************** + * Wrappers around the aliased package * + ***************************************/ + + public function getType() + { + return $this->aliasOf->getType(); + } + public function getTargetDir() + { + return $this->aliasOf->getTargetDir(); + } + public function getExtra() + { + return $this->aliasOf->getExtra(); + } + public function setInstallationSource($type) + { + $this->aliasOf->setInstallationSource($type); + } + public function getInstallationSource() + { + return $this->aliasOf->getInstallationSource(); + } + public function getSourceType() + { + return $this->aliasOf->getSourceType(); + } + public function getSourceUrl() + { + return $this->aliasOf->getSourceUrl(); + } + public function getSourceReference() + { + return $this->aliasOf->getSourceReference(); + } + public function getDistType() + { + return $this->aliasOf->getDistType(); + } + public function getDistUrl() + { + return $this->aliasOf->getDistUrl(); + } + public function getDistReference() + { + return $this->aliasOf->getDistReference(); + } + public function getDistSha1Checksum() + { + return $this->aliasOf->getDistSha1Checksum(); + } + public function getScripts() + { + return $this->aliasOf->getScripts(); + } + public function getLicense() + { + return $this->aliasOf->getLicense(); + } + public function getAutoload() + { + return $this->aliasOf->getAutoload(); + } + public function getRepositories() + { + return $this->aliasOf->getRepositories(); + } + public function getReleaseDate() + { + return $this->aliasOf->getReleaseDate(); + } + public function getKeywords() + { + return $this->aliasOf->getKeywords(); + } + public function getDescription() + { + return $this->aliasOf->getDescription(); + } + public function getHomepage() + { + return $this->aliasOf->getHomepage(); + } + public function getAuthors() + { + return $this->aliasOf->getAuthors(); + } + public function __toString() + { + return $this->aliasOf->__toString(); + } +} diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index d5a0e5230..0c0eef3cb 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -24,8 +24,18 @@ use Composer\Repository\PlatformRepository; */ abstract class BasePackage implements PackageInterface { + public static $supportedLinkTypes = array( + 'require' => 'requires', + 'conflict' => 'conflicts', + 'provide' => 'provides', + 'replace' => 'replaces', + 'recommend' => 'recommends', + 'suggest' => 'suggests', + ); + protected $name; protected $prettyName; + protected $repository; protected $id; diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index c13150953..7909316d0 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -22,15 +22,6 @@ use Composer\Repository\RepositoryManager; */ class ArrayLoader { - protected $supportedLinkTypes = array( - 'require' => 'requires', - 'conflict' => 'conflicts', - 'provide' => 'provides', - 'replace' => 'replaces', - 'recommend' => 'recommends', - 'suggest' => 'suggests', - ); - protected $versionParser; public function __construct(VersionParser $parser = null) @@ -141,7 +132,7 @@ class ArrayLoader $package->setDistSha1Checksum(isset($config['dist']['shasum']) ? $config['dist']['shasum'] : null); } - foreach ($this->supportedLinkTypes as $type => $description) { + foreach (Package\BasePackage::$supportedLinkTypes as $type => $description) { if (isset($config[$type])) { $method = 'set'.ucfirst($description); $package->{$method}( From 59d2b1145c1e1df52a2218aada100c29c45f7e7b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 21 Feb 2012 16:44:49 +0100 Subject: [PATCH 5/7] Add lock file alias support and a bunch of fixes --- src/Composer/Command/InstallCommand.php | 49 ++++++++++++++----- .../Installer/InstallationManager.php | 1 + src/Composer/Package/AliasPackage.php | 9 ++++ .../Package/Loader/RootPackageLoader.php | 2 +- src/Composer/Package/Locker.php | 12 ++++- src/Composer/Package/PackageInterface.php | 7 +++ .../Package/Version/VersionParser.php | 2 +- tests/Composer/Test/Package/LockerTest.php | 7 +-- .../Package/Version/VersionParserTest.php | 2 +- 9 files changed, 70 insertions(+), 21 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index d90b05b07..89dc3aa87 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -107,13 +107,17 @@ EOT // prepare aliased packages $aliasRepo = new ArrayRepository; - foreach ($composer->getPackage()->getAliases() as $alias) { + if (!$update && $composer->getLocker()->isLocked()) { + $aliases = $composer->getLocker()->getAliases(); + } else { + $aliases = $composer->getPackage()->getAliases(); + } + foreach ($aliases as $alias) { foreach ($repoManager->findPackages($alias['package'], $alias['version']) as $package) { - $aliasRepo->addPackage(new AliasPackage($package, $alias['replaces'])); + $aliasRepo->addPackage(new AliasPackage($package, $alias['alias'])); } - foreach ($repoManager->getLocalRepository()->findPackages($alias['package'], $alias['version']) as $package) { - $repoManager->getLocalRepository()->addPackage(new AliasPackage($package, $alias['replaces'])); + $repoManager->getLocalRepository()->addPackage(new AliasPackage($package, $alias['alias'])); $repoManager->getLocalRepository()->removePackage($package); } } @@ -137,11 +141,11 @@ EOT $request = new Request($pool); if ($update) { $io->write('Updating dependencies'); - $installedPackages = $installedRepo->getPackages(); - $links = $this->collectLinks($composer->getPackage(), $noInstallRecommends, $installSuggests); $request->updateAll(); + $links = $this->collectLinks($composer->getPackage(), $noInstallRecommends, $installSuggests); + foreach ($links as $link) { $request->install($link->getTarget(), $link->getConstraint()); } @@ -154,7 +158,14 @@ EOT } foreach ($composer->getLocker()->getLockedPackages() as $package) { - $constraint = new VersionConstraint('=', $package->getVersion()); + $version = $package->getVersion(); + foreach ($aliases as $alias) { + if ($alias['package'] === $package->getName() && $alias['version'] === $package->getVersion()) { + $version = $alias['alias']; + break; + } + } + $constraint = new VersionConstraint('=', $version); $request->install($package->getName(), $constraint); } } else { @@ -175,14 +186,13 @@ EOT // solve dependencies $operations = $solver->solve($request); - // execute operations - if (!$operations) { - $io->write('Nothing to install/update'); - } - // force dev packages to be updated to latest reference on update if ($update) { foreach ($localRepo->getPackages() as $package) { + if ($package instanceof AliasPackage) { + $package = $package->getAliasOf(); + } + // skip non-dev packages if (!$package->isDev()) { continue; @@ -205,6 +215,19 @@ EOT } } + // anti-alias local repository to allow updates to work fine + foreach ($repoManager->getLocalRepository()->getPackages() as $package) { + if ($package instanceof AliasPackage) { + $repoManager->getLocalRepository()->addPackage(clone $package->getAliasOf()); + $repoManager->getLocalRepository()->removePackage($package); + } + } + + // execute operations + if (!$operations) { + $io->write('Nothing to install/update'); + } + foreach ($operations as $operation) { if ($verbose) { $io->write((string) $operation); @@ -238,7 +261,7 @@ EOT if (!$dryRun) { if ($update || !$composer->getLocker()->isLocked()) { - $composer->getLocker()->lockPackages($localRepo->getPackages()); + $composer->getLocker()->setLockData($localRepo->getPackages(), $aliases); $io->write('Writing lock file'); } diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index d2f0e2a6b..6500e6b00 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -13,6 +13,7 @@ namespace Composer\Installer; use Composer\Package\PackageInterface; +use Composer\Package\AliasPackage; use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\Operation\InstallOperation; use Composer\DependencyResolver\Operation\UpdateOperation; diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index 3665fd30a..02c0a9510 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -60,6 +60,11 @@ class AliasPackage extends BasePackage } } + public function getAliasOf() + { + return $this->aliasOf; + } + /** * {@inheritDoc} */ @@ -204,6 +209,10 @@ class AliasPackage extends BasePackage { return $this->aliasOf->getReleaseDate(); } + public function getBinaries() + { + return $this->aliasOf->getBinaries(); + } public function getKeywords() { return $this->aliasOf->getKeywords(); diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 657ff482c..a6865f01e 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -50,7 +50,7 @@ class RootPackageLoader extends ArrayLoader $aliases[] = array( 'package' => strtolower($reqName), 'version' => $this->versionParser->normalize($match[1]), - 'replaces' => $this->versionParser->normalize($match[2]), + 'alias' => $this->versionParser->normalize($match[2]), ); } } diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index c55c0e467..205372481 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -91,6 +91,12 @@ class Locker return $packages; } + public function getAliases() + { + $lockList = $this->getLockData(); + return isset($lockList['aliases']) ? $lockList['aliases'] : array(); + } + public function getLockData() { if (!$this->isLocked()) { @@ -101,15 +107,17 @@ class Locker } /** - * Locks provided packages into lockfile. + * Locks provided data into lockfile. * * @param array $packages array of packages + * @param array $aliases array of aliases */ - public function lockPackages(array $packages) + public function setLockData(array $packages, array $aliases) { $lock = array( 'hash' => $this->hash, 'packages' => array(), + 'aliases' => $aliases, ); foreach ($packages as $package) { $name = $package->getPrettyName(); diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index 1f19a835f..cf68cc02d 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -293,6 +293,13 @@ interface PackageInterface */ function getDescription(); + /** + * Returns the package binaries + * + * @return string + */ + function getBinaries(); + /** * Returns the package homepage * diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 36a16ae09..19cd240a8 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -36,7 +36,7 @@ class VersionParser // ignore aliases and just assume the alias is required instead of the source if (preg_match('{^([^,\s]+) +as +([^,\s]+)$}', $version, $match)) { - $version = $match[1]; + $version = $match[2]; } // match master-like branches diff --git a/tests/Composer/Test/Package/LockerTest.php b/tests/Composer/Test/Package/LockerTest.php index 8feeabeaf..9c3f3cbe0 100644 --- a/tests/Composer/Test/Package/LockerTest.php +++ b/tests/Composer/Test/Package/LockerTest.php @@ -114,7 +114,7 @@ class LockerTest extends \PHPUnit_Framework_TestCase $locker->getLockedPackages(); } - public function testLockPackages() + public function testSetLockData() { $json = $this->createJsonFileMock(); $repo = $this->createRepositoryManagerMock(); @@ -151,9 +151,10 @@ class LockerTest extends \PHPUnit_Framework_TestCase array('package' => 'pkg1', 'version' => '1.0.0-beta'), array('package' => 'pkg2', 'version' => '0.1.10') ), + 'aliases' => array(), )); - $locker->lockPackages(array($package1, $package2)); + $locker->setLockData(array($package1, $package2), array()); } public function testLockBadPackages() @@ -171,7 +172,7 @@ class LockerTest extends \PHPUnit_Framework_TestCase $this->setExpectedException('LogicException'); - $locker->lockPackages(array($package1)); + $locker->setLockData(array($package1), array()); } public function testIsFresh() diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index d7336bd5a..63efbc2f5 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -53,7 +53,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase 'parses trunk' => array('dev-trunk', '9999999-dev'), 'parses arbitrary' => array('dev-feature-foo', 'dev-feature-foo'), 'parses arbitrary2' => array('DEV-FOOBAR', 'dev-foobar'), - 'ignores aliases' => array('dev-master as 1.0.0', '9999999-dev'), + 'ignores aliases' => array('dev-master as 1.0.0', '1.0.0.0'), ); } From 956b54e5167c6dd9244072c59406fb1d4ba23f62 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Feb 2012 09:40:05 +0100 Subject: [PATCH 6/7] Use policy rule instead of stacking up another repo to prioritize aliased packages --- src/Composer/Command/InstallCommand.php | 4 +--- src/Composer/DependencyResolver/DefaultPolicy.php | 11 +++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 89dc3aa87..426c1fb9e 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -106,7 +106,6 @@ EOT } // prepare aliased packages - $aliasRepo = new ArrayRepository; if (!$update && $composer->getLocker()->isLocked()) { $aliases = $composer->getLocker()->getAliases(); } else { @@ -114,14 +113,13 @@ EOT } foreach ($aliases as $alias) { foreach ($repoManager->findPackages($alias['package'], $alias['version']) as $package) { - $aliasRepo->addPackage(new AliasPackage($package, $alias['alias'])); + $package->getRepository()->addPackage(new AliasPackage($package, $alias['alias'])); } foreach ($repoManager->getLocalRepository()->findPackages($alias['package'], $alias['version']) as $package) { $repoManager->getLocalRepository()->addPackage(new AliasPackage($package, $alias['alias'])); $repoManager->getLocalRepository()->removePackage($package); } } - $repoManager->addRepository($aliasRepo); // creating repository pool $pool = new Pool; diff --git a/src/Composer/DependencyResolver/DefaultPolicy.php b/src/Composer/DependencyResolver/DefaultPolicy.php index 63699d883..277188238 100644 --- a/src/Composer/DependencyResolver/DefaultPolicy.php +++ b/src/Composer/DependencyResolver/DefaultPolicy.php @@ -14,6 +14,7 @@ namespace Composer\DependencyResolver; use Composer\Repository\RepositoryInterface; use Composer\Package\PackageInterface; +use Composer\Package\AliasPackage; use Composer\Package\LinkConstraint\VersionConstraint; /** @@ -103,6 +104,16 @@ class DefaultPolicy implements PolicyInterface public function compareByPriorityPreferInstalled(Pool $pool, array $installedMap, PackageInterface $a, PackageInterface $b, $ignoreReplace = false) { if ($a->getRepository() === $b->getRepository()) { + if ($a->getName() === $b->getName()) { + $aAliased = $a instanceof AliasPackage; + $bAliased = $b instanceof AliasPackage; + if ($aAliased && !$bAliased) { + return -1; // use a + } + if (!$aAliased && $bAliased) { + return 1; // use b + } + } if (!$ignoreReplace) { // return original, not replaced From 1213067d96e2097b5a3e422e70674d7624353857 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Feb 2012 09:40:15 +0100 Subject: [PATCH 7/7] Add test --- tests/Composer/Test/Package/Version/VersionParserTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index 63efbc2f5..f45d0a0dc 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -129,6 +129,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase 'accepts master' => array('>=dev-master', new VersionConstraint('>=', '9999999-dev')), 'accepts master/2' => array('dev-master', new VersionConstraint('=', '9999999-dev')), 'accepts arbitrary' => array('dev-feature-a', new VersionConstraint('=', 'dev-feature-a')), + 'ignores aliases' => array('dev-master as 1.0.0', new VersionConstraint('=', '1.0.0.0')), ); }