From 1e1ecb80b7d9d52fee4fa5438bb749326926d603 Mon Sep 17 00:00:00 2001 From: everzet Date: Fri, 16 Sep 2011 22:14:06 +0300 Subject: [PATCH 01/41] initial refactoring --- bin/composer | 2 +- src/Composer/Command/InstallCommand.php | 146 +++---------- src/Composer/Composer.php | 108 ++-------- src/Composer/ConfigurableComposer.php | 200 ++++++++++++++++++ .../Console/Package/VerboseManager.php | 40 ++++ src/Composer/Installer/LibraryInstaller.php | 17 +- src/Composer/Package/Manager.php | 93 ++++++++ 7 files changed, 401 insertions(+), 205 deletions(-) create mode 100644 src/Composer/ConfigurableComposer.php create mode 100644 src/Composer/Console/Package/VerboseManager.php create mode 100644 src/Composer/Package/Manager.php diff --git a/bin/composer b/bin/composer index c9af1edbd..2c2376a19 100755 --- a/bin/composer +++ b/bin/composer @@ -13,7 +13,7 @@ use Composer\Console\Application; setlocale(LC_ALL, 'C'); // initialize composer -$composer = new Composer(); +$composer = new ConfigurableComposer('composer.json', 'composer.lock'); $composer->addDownloader('git', new GitDownloader); $composer->addDownloader('pear', new PearDownloader); $composer->addDownloader('zip', new ZipDownloader); diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 07b85f163..f183c049b 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -12,20 +12,14 @@ namespace Composer\Command; -use Composer\DependencyResolver\Pool; -use Composer\DependencyResolver\Request; -use Composer\DependencyResolver\DefaultPolicy; -use Composer\DependencyResolver\Solver; -use Composer\Repository\PlatformRepository; -use Composer\Package\MemoryPackage; -use Composer\Package\LinkConstraint\VersionConstraint; +use Composer\DependencyResolver; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; - /** * @author Jordi Boggiano * @author Ryan Weaver + * @author Konstantin Kudryashov */ class InstallCommand extends Command { @@ -48,130 +42,48 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { - // TODO this needs a parameter to enable installing from source (i.e. git clone, instead of downloading archives) - $sourceInstall = false; + if ($this->getLock()->isLocked()) { + $this->writeln('Found lockfile. Reading'); - $config = $this->loadConfig(); - - $output->writeln('Loading repositories'); - - if (isset($config['repositories'])) { - foreach ($config['repositories'] as $name => $spec) { - $this->getComposer()->addRepository($name, $spec); + foreach ($this->getLock()->getLockedPackages() as $package) { + $installer = $this->getComposer()->getInstaller($package->getType()); + if (!$installer->isInstalled($package)) { + $installer->install($package); + } } + + return 0; } + // creating repository pool $pool = new Pool; - - $repoInstalled = new PlatformRepository; - $pool->addRepository($repoInstalled); - - // TODO check the lock file to see what's currently installed - // $repoInstalled->addPackage(new MemoryPackage('$Package', '$Version')); - - $output->writeln('Loading package list'); - foreach ($this->getComposer()->getRepositories() as $repository) { $pool->addRepository($repository); } + // creating requirements request $request = new Request($pool); - - $output->writeln('Building up request'); - - // TODO there should be an update flag or dedicated update command - // TODO check lock file to remove packages that disappeared from the requirements - foreach ($config['require'] as $name => $version) { - if ('latest' === $version) { - $request->install($name); - } else { - preg_match('#^([>=<~]*)([\d.]+.*)$#', $version, $match); - if (!$match[1]) { - $match[1] = '='; - } - $constraint = new VersionConstraint($match[1], $match[2]); - $request->install($name, $constraint); - } + foreach ($this->getPackage()->getRequires() as $link) { + $request->install($link->getTarget(), $link->getConstraint()); } - $output->writeln('Solving dependencies'); + // prepare solver + $platform = $this->getComposer()->getRepository('Platform'); + $policy = new DependencyResolver\DefaultPolicy(); + $solver = new DependencyResolver\Solver($policy, $pool, $platform); - $policy = new DefaultPolicy; - $solver = new Solver($policy, $pool, $repoInstalled); - $transaction = $solver->solve($request); - - $lock = array(); - - foreach ($transaction as $task) { - switch ($task['job']) { - case 'install': - $package = $task['package']; - $output->writeln('> Installing '.$package->getPrettyName()); - if ($sourceInstall) { - // TODO - } else { - if ($package->getDistType()) { - $downloaderType = $package->getDistType(); - $type = 'dist'; - } elseif ($package->getSourceType()) { - $output->writeln('Package '.$package->getPrettyName().' has no dist url, installing from source instead.'); - $downloaderType = $package->getSourceType(); - $type = 'source'; - } else { - throw new \UnexpectedValueException('Package '.$package->getPrettyName().' has no source or dist URL.'); - } - $downloader = $this->getComposer()->getDownloader($downloaderType); - $installer = $this->getComposer()->getInstaller($package->getType()); - if (!$installer->install($package, $downloader, $type)) { - throw new \LogicException($package->getPrettyName().' could not be installed.'); - } - } - $lock[$package->getName()] = array('version' => $package->getVersion()); - break; - default: - throw new \UnexpectedValueException('Unhandled job type : '.$task['job']); - } + // solve dependencies and execute operations + $operations = $this->solveDependencies($request, $solver); + foreach ($operations as $operation) { + $operation->execute(); + // TODO: collect installable packages into $installed } + $output->writeln('> Done'); - $this->storeLockFile($lock, $output); - } - - protected function loadConfig() - { - if (!file_exists('composer.json')) { - throw new \UnexpectedValueException('composer.json config file not found in '.getcwd()); + if (false) { + $config->lock($installed); + $output->writeln('> Locked'); } - $config = json_decode(file_get_contents('composer.json'), true); - if (!$config) { - switch (json_last_error()) { - case JSON_ERROR_NONE: - $msg = 'No error has occurred, is your composer.json file empty?'; - break; - case JSON_ERROR_DEPTH: - $msg = 'The maximum stack depth has been exceeded'; - break; - case JSON_ERROR_STATE_MISMATCH: - $msg = 'Invalid or malformed JSON'; - break; - case JSON_ERROR_CTRL_CHAR: - $msg = 'Control character error, possibly incorrectly encoded'; - break; - case JSON_ERROR_SYNTAX: - $msg = 'Syntax error'; - break; - case JSON_ERROR_UTF8: - $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; - break; - } - throw new \UnexpectedValueException('Incorrect composer.json file: '.$msg); - } - return $config; } - - protected function storeLockFile(array $content, OutputInterface $output) - { - file_put_contents('composer.lock', json_encode($content, JSON_FORCE_OBJECT)."\n"); - $output->writeln('> composer.lock dumped'); - } -} \ No newline at end of file +} diff --git a/src/Composer/Composer.php b/src/Composer/Composer.php index 4da5de68d..ceeb16735 100644 --- a/src/Composer/Composer.php +++ b/src/Composer/Composer.php @@ -12,125 +12,61 @@ namespace Composer; -use Composer\Downloader\DownloaderInterface; use Composer\Installer\InstallerInterface; -use Composer\Repository\ComposerRepository; -use Composer\Repository\PlatformRepository; -use Composer\Repository\GitRepository; -use Composer\Repository\PearRepository; /** * @author Jordi Boggiano + * @author Konstantin Kudryashiv */ class Composer { const VERSION = '1.0.0-DEV'; - protected $repositories = array(); - protected $downloaders = array(); - protected $installers = array(); + private $repositories = array(); + private $installers = array(); - public function __construct() + public function setInstaller($type, InstallerInterface $installer = null) { - $this->addRepository('Packagist', array('composer' => 'http://packagist.org')); - } + if (null === $installer) { + unset($this->installers[$type]); - /** - * Add downloader for type - * - * @param string $type - * @param DownloaderInterface $downloader - */ - public function addDownloader($type, DownloaderInterface $downloader) - { - $type = strtolower($type); - $this->downloaders[$type] = $downloader; - } - - /** - * Get type downloader - * - * @param string $type - * - * @return DownloaderInterface - */ - public function getDownloader($type) - { - $type = strtolower($type); - if (!isset($this->downloaders[$type])) { - throw new \UnexpectedValueException('Unknown source type: '.$type); + return; } - return $this->downloaders[$type]; - } - /** - * Add installer for type - * - * @param string $type - * @param InstallerInterface $installer - */ - public function addInstaller($type, InstallerInterface $installer) - { - $type = strtolower($type); $this->installers[$type] = $installer; } - /** - * Get type installer - * - * @param string $type - * - * @return InstallerInterface - */ public function getInstaller($type) { - $type = strtolower($type); if (!isset($this->installers[$type])) { throw new \UnexpectedValueException('Unknown dependency type: '.$type); } + return $this->installers[$type]; } - public function addRepository($name, $spec) + public function setRepository($name, RepositoryInterface $repository = null) { - if (null === $spec) { + if (null === $repository) { unset($this->repositories[$name]); + + return; } - if (is_array($spec) && count($spec) === 1) { - return $this->repositories[$name] = $this->createRepository($name, key($spec), current($spec)); + + $this->repositories[$name] = $repository; + } + + public function getRepository($name) + { + if (!isset($this->repositories[$name])) { + throw new \UnexpectedValueException('Unknown repository: '.$name); } - throw new \UnexpectedValueException('Invalid repositories specification '.json_encode($spec).', should be: {"type": "url"}'); + + return $this->repositories[$name]; } public function getRepositories() { return $this->repositories; } - - public function createRepository($name, $type, $spec) - { - if (is_string($spec)) { - $spec = array('url' => $spec); - } - $spec['url'] = rtrim($spec['url'], '/'); - - switch ($type) { - case 'git-bare': - case 'git-multi': - throw new \Exception($type.' repositories not supported yet'); - break; - - case 'git': - return new GitRepository($spec['url']); - - case 'composer': - return new ComposerRepository($spec['url']); - - case 'pear': - return new PearRepository($spec['url'], $name); - - default: - throw new \UnexpectedValueException('Unknown repository type: '.$type.', could not create repository '.$name); - } - } } diff --git a/src/Composer/ConfigurableComposer.php b/src/Composer/ConfigurableComposer.php new file mode 100644 index 000000000..1d0443c09 --- /dev/null +++ b/src/Composer/ConfigurableComposer.php @@ -0,0 +1,200 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Repository; +use Composer\Package; +use Composer\Installer\LibraryInstaller; + +/** + * @author Konstantin Kudryashiv + */ +class ConfigurableComposer extends Composer +{ + private $configFile; + private $lockFile; + private $isLocked = false; + private $lockedPackages = array(); + + public function __construct($configFile = 'composer.json', $lockFile = 'composer.lock') + { + $this->configFile = $configFile; + $this->lockFile = $lockFile; + $this->setRepository('Platform', new Repository\PlatformRepository()); + + if (!file_exists($configFile)) { + throw new \UnexpectedValueException('Can not find composer config file'); + } + + $config = $this->loadJsonConfig($configFile); + + if (isset($config['path'])) { + $this->setInstaller('library', new LibraryInstaller($config['path'])); + } else { + $this->setInstaller('library', new LibraryInstaller()); + } + + if (isset($config['repositories'])) { + $repositories = $this->loadRepositoriesFromConfig($config['repositories']) + foreach ($repositories as $name => $repository) { + $this->setRepository($name, $repository); + } + } + + if (isset($config['require'])) { + $requirements = $this->loadRequirementsFromConfig($config['require']); + foreach ($requirements as $name => $constraint) { + $this->setRequirement($name, $constraint); + } + } + + if (file_exists($lockFile)) { + $lock = $this->loadJsonConfig($lockFile); + $platform = $this->getRepository('Platform'); + $packages = $this->loadPackagesFromLock($lock); + foreach ($packages as $package) { + if ($this->hasRequirement($package->getName())) { + $platform->addPackage($package); + $this->lockedPackages[] = $package; + } + } + $this->isLocked = true; + } + } + + public function isLocked() + { + return $this->isLocked; + } + + public function getLockedPackages() + { + return $this->lockedPackages; + } + + public function lock(array $packages) + { + // TODO: write installed packages info into $this->lockFile + } + + private function loadPackagesFromLock(array $lockList) + { + $packages = array(); + foreach ($lockList as $info) { + $packages[] = new Package\MemoryPackage($info['package'], $info['version']); + } + + return $packages; + } + + private function loadRepositoriesFromConfig(array $repositoriesList) + { + $repositories = array(); + foreach ($repositoriesList as $name => $spec) { + if (is_array($spec) && count($spec) === 1) { + $repositories[$name] = $this->createRepository($name, key($spec), current($spec)); + } elseif (null === $spec) { + $repositories[$name] = null; + } else { + throw new \UnexpectedValueException( + 'Invalid repositories specification '. + json_encode($spec).', should be: {"type": "url"}' + ); + } + } + + return $repositories; + } + + private function loadRequirementsFromConfig(array $requirementsList) + { + $requirements = array(); + foreach ($requirementsList as $name => $version) { + $name = $this->lowercase($name); + if ('latest' === $version) { + $requirements[$name] = null; + } else { + preg_match('#^([>=<~]*)([\d.]+.*)$#', $version, $match); + if (!$match[1]) { + $match[1] = '='; + } + $constraint = new Package\LinkConstraint\VersionConstraint($match[1], $match[2]); + $requirements[$name] = $constraint; + } + } + + return $requirements; + } + + private function loadJsonConfig($configFile) + { + $config = json_decode(file_get_contents($configFile), true); + if (!$config) { + switch (json_last_error()) { + case JSON_ERROR_NONE: + $msg = 'No error has occurred, is your composer.json file empty?'; + break; + case JSON_ERROR_DEPTH: + $msg = 'The maximum stack depth has been exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Invalid or malformed JSON'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Control character error, possibly incorrectly encoded'; + break; + case JSON_ERROR_SYNTAX: + $msg = 'Syntax error'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + } + throw new \UnexpectedValueException('Incorrect composer.json file: '.$msg); + } + + return $config; + } + + private function lowercase($str) + { + if (function_exists('mb_strtolower')) { + return mb_strtolower($str, 'UTF-8'); + } + return strtolower($str, 'UTF-8'); + } + + private function createRepository($name, $type, $spec) + { + if (is_string($spec)) { + $spec = array('url' => $spec); + } + $spec['url'] = rtrim($spec['url'], '/'); + + switch ($type) { + case 'git-bare': + case 'git-multi': + throw new \Exception($type.' repositories not supported yet'); + case 'git': + return new Repository\GitRepository($spec['url']); + case 'composer': + return new Repository\ComposerRepository($spec['url']); + case 'pear': + return new Repository\PearRepository($spec['url'], $name); + default: + throw new \UnexpectedValueException( + 'Unknown repository type: '.$type.', could not create repository '.$name + ); + } + } +} diff --git a/src/Composer/Console/Package/VerboseManager.php b/src/Composer/Console/Package/VerboseManager.php new file mode 100644 index 000000000..7562ec57d --- /dev/null +++ b/src/Composer/Console/Package/VerboseManager.php @@ -0,0 +1,40 @@ +composer = $composer; + } + + public function install(PackageInterface $package) + { + $this->output->writeln('> Installing '.$package->getName()); + + parent::install($package); + } + + public function update(PackageInterface $package) + { + $this->output->writeln('> Updating '.$package->getName()); + + parent::update($package); + } + + public function remove(PackageInterface $package) + { + $this->output->writeln('> Removing '.$package->getName()); + + parent::remove($package); + } +} diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index ad3fd451e..1e7ed2583 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -38,4 +38,19 @@ class LibraryInstaller implements InstallerInterface } return true; } -} \ No newline at end of file + + public function isInstalled(PackageInterface $package, $downloader, $type) + { + // TODO: implement installation check + } + + public function update(PackageInterface $package, $downloader, $type) + { + // TODO: implement package update + } + + public function remove(PackageInterface $package, $downloader, $type) + { + // TODO: implement package removal + } +} diff --git a/src/Composer/Package/Manager.php b/src/Composer/Package/Manager.php new file mode 100644 index 000000000..03482602b --- /dev/null +++ b/src/Composer/Package/Manager.php @@ -0,0 +1,93 @@ +composer = $composer; + } + + public function isInstalled(PackageInterface $package) + { + $installer = $this->composer->getInstaller($package->getType()); + $downloader = $this->getDownloaderForPackage($package); + $packageType = $this->getTypeForPackage($package); + + return $installer->isInstalled($package, $downloader, $packageType); + } + + public function install(PackageInterface $package) + { + $output->writeln('> Installing '.$package->getName()); + + $installer = $this->composer->getInstaller($package->getType()); + $downloader = $this->getDownloaderForPackage($package); + $packageType = $this->getTypeForPackage($package); + + if (!$installer->install($package, $downloader, $packageType)) { + throw new \LogicException($package->getName().' could not be installed.'); + } + } + + public function update(PackageInterface $package) + { + $output->writeln('> Updating '.$package->getName()); + + $installer = $this->composer->getInstaller($package->getType()); + $downloader = $this->getDownloaderForPackage($package); + $packageType = $this->getTypeForPackage($package); + + if (!$installer->update($package, $downloader, $packageType)) { + throw new \LogicException($package->getName().' could not be updated.'); + } + } + + public function remove(PackageInterface $package) + { + $output->writeln('> Removing '.$package->getName()); + + $installer = $this->composer->getInstaller($package->getType()); + $downloader = $this->getDownloaderForPackage($package); + $packageType = $this->getTypeForPackage($package); + + if (!$installer->remove($package, $downloader, $packageType)) { + throw new \LogicException($package->getName().' could not be removed.'); + } + } + + private function getDownloaderForPackage(PackageInterface $package) + { + if ($package->getDistType()) { + $downloader = $this->composer->getDownloader($package->getDistType); + } elseif ($package->getSourceType()) { + $downloader = $this->copmoser->getDownloader($package->getSourceType()); + } else { + throw new \UnexpectedValueException( + 'Package '.$package->getName().' has no source or dist URL.' + ); + } + + return $downloader; + } + + private function getTypeForPackage(PackageInterface $package) + { + if ($package->getDistType()) { + $type = 'dist'; + } elseif ($package->getSourceType()) { + $type = 'source'; + } else { + throw new \UnexpectedValueException( + 'Package '.$package->getName().' has no source or dist URL.' + ); + } + + return $type; + } +} From 5f4d46f7aee4eee752e2fd64014c5359999449ee Mon Sep 17 00:00:00 2001 From: everzet Date: Sat, 17 Sep 2011 14:39:37 +0300 Subject: [PATCH 02/41] repository factoring refactoring --- bin/composer | 37 +++++++---- src/Composer/ConfigurableComposer.php | 52 ++++----------- src/Composer/Package/Manager.php | 6 -- src/Composer/Repository/ArrayRepository.php | 10 +++ .../Repository/ComposerRepository.php | 12 +++- src/Composer/Repository/GitRepository.php | 12 +++- src/Composer/Repository/PearRepository.php | 10 +++ .../Repository/PlatformRepository.php | 7 ++- src/Composer/Repository/RepositoryFactory.php | 63 +++++++++++++++++++ .../Repository/RepositoryInterface.php | 3 + 10 files changed, 152 insertions(+), 60 deletions(-) create mode 100644 src/Composer/Repository/RepositoryFactory.php diff --git a/bin/composer b/bin/composer index 2c2376a19..35194b681 100755 --- a/bin/composer +++ b/bin/composer @@ -4,21 +4,34 @@ require __DIR__.'/../tests/bootstrap.php'; use Composer\Composer; -use Composer\Downloader\GitDownloader; -use Composer\Downloader\PearDownloader; -use Composer\Downloader\ZipDownloader; -use Composer\Installer\LibraryInstaller; -use Composer\Console\Application; +use Composer\Installer; +use Composer\Downloader; +use Composer\Repository; +use Composer\Package; +use Composer\Console\Application as ComposerApplication; -setlocale(LC_ALL, 'C'); +$composerFolder = '.composer'; +$composerCache = $composerFolder.'/packages'; + +// initialize installer +$installer = new Installer\LibraryInstaller('vendor', $preferSource = true); +$installer->setDownloader('git', new Downloader\GitDownloader()); +$installer->setDownloader('pear', new Downloader\PearDownloader()); +$installer->setDownloader('zip', new Downloader\ZipDownloader()); // initialize composer -$composer = new ConfigurableComposer('composer.json', 'composer.lock'); -$composer->addDownloader('git', new GitDownloader); -$composer->addDownloader('pear', new PearDownloader); -$composer->addDownloader('zip', new ZipDownloader); -$composer->addInstaller('library', new LibraryInstaller); +$composer = new Composer(); +$composer->setInstaller('library', $installer); +$composer->setRepository('Platform', new Repository\PlatformRepository()); +$composer->setRepository('Packagist', new Repository\ComposerRepository('http://packagist.org')); + +// initialize package +$loader = new Package\Loader\Json(); +$package = $loader->load('composer.json'); + +// initialize lock +$packageLock = new Package\PackageLock('composer.lock'); // run the command application -$application = new Application($composer); +$application = new ComposerApplication($composer, $package, $packageLock); $application->run(); diff --git a/src/Composer/ConfigurableComposer.php b/src/Composer/ConfigurableComposer.php index 1d0443c09..91704aa28 100644 --- a/src/Composer/ConfigurableComposer.php +++ b/src/Composer/ConfigurableComposer.php @@ -12,7 +12,7 @@ namespace Composer; -use Composer\Repository; +use Composer\Repository\RepositoryFactory; use Composer\Package; use Composer\Installer\LibraryInstaller; @@ -25,12 +25,16 @@ class ConfigurableComposer extends Composer private $lockFile; private $isLocked = false; private $lockedPackages = array(); + private $repositoryFactory; - public function __construct($configFile = 'composer.json', $lockFile = 'composer.lock') + public function __construct($configFile = 'composer.json', $lockFile = 'composer.lock', + RepositoryFactory $repositoryFactory) { - $this->configFile = $configFile; - $this->lockFile = $lockFile; - $this->setRepository('Platform', new Repository\PlatformRepository()); + $this->configFile = $configFile; + $this->lockFile = $lockFile; + $this->repositoryFactory = $repositoryFactory; + + $this->setRepository('Platform', $repositoryFactory->create('platform')); if (!file_exists($configFile)) { throw new \UnexpectedValueException('Can not find composer config file'); @@ -102,7 +106,9 @@ class ConfigurableComposer extends Composer $repositories = array(); foreach ($repositoriesList as $name => $spec) { if (is_array($spec) && count($spec) === 1) { - $repositories[$name] = $this->createRepository($name, key($spec), current($spec)); + $repositories[$name] = $this->repositoryFactory->create( + key($spec), $name, current($spec) + ); } elseif (null === $spec) { $repositories[$name] = null; } else { @@ -120,7 +126,7 @@ class ConfigurableComposer extends Composer { $requirements = array(); foreach ($requirementsList as $name => $version) { - $name = $this->lowercase($name); + $name = strtolower($name); if ('latest' === $version) { $requirements[$name] = null; } else { @@ -165,36 +171,4 @@ class ConfigurableComposer extends Composer return $config; } - - private function lowercase($str) - { - if (function_exists('mb_strtolower')) { - return mb_strtolower($str, 'UTF-8'); - } - return strtolower($str, 'UTF-8'); - } - - private function createRepository($name, $type, $spec) - { - if (is_string($spec)) { - $spec = array('url' => $spec); - } - $spec['url'] = rtrim($spec['url'], '/'); - - switch ($type) { - case 'git-bare': - case 'git-multi': - throw new \Exception($type.' repositories not supported yet'); - case 'git': - return new Repository\GitRepository($spec['url']); - case 'composer': - return new Repository\ComposerRepository($spec['url']); - case 'pear': - return new Repository\PearRepository($spec['url'], $name); - default: - throw new \UnexpectedValueException( - 'Unknown repository type: '.$type.', could not create repository '.$name - ); - } - } } diff --git a/src/Composer/Package/Manager.php b/src/Composer/Package/Manager.php index 03482602b..db0f73ebb 100644 --- a/src/Composer/Package/Manager.php +++ b/src/Composer/Package/Manager.php @@ -24,8 +24,6 @@ class Manager public function install(PackageInterface $package) { - $output->writeln('> Installing '.$package->getName()); - $installer = $this->composer->getInstaller($package->getType()); $downloader = $this->getDownloaderForPackage($package); $packageType = $this->getTypeForPackage($package); @@ -37,8 +35,6 @@ class Manager public function update(PackageInterface $package) { - $output->writeln('> Updating '.$package->getName()); - $installer = $this->composer->getInstaller($package->getType()); $downloader = $this->getDownloaderForPackage($package); $packageType = $this->getTypeForPackage($package); @@ -50,8 +46,6 @@ class Manager public function remove(PackageInterface $package) { - $output->writeln('> Removing '.$package->getName()); - $installer = $this->composer->getInstaller($package->getType()); $downloader = $this->getDownloaderForPackage($package); $packageType = $this->getTypeForPackage($package); diff --git a/src/Composer/Repository/ArrayRepository.php b/src/Composer/Repository/ArrayRepository.php index fb7025e9f..ad3dc7d3e 100644 --- a/src/Composer/Repository/ArrayRepository.php +++ b/src/Composer/Repository/ArrayRepository.php @@ -23,6 +23,16 @@ class ArrayRepository implements RepositoryInterface { protected $packages; + static public function supports($type, $name = '', $url = '') + { + return 'array' === strtolower($type); + } + + static public function create($type, $name = '', $url = '') + { + return new static(); + } + /** * Adds a new package to the repository * diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index eddd2b3a3..548fe9d06 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -24,6 +24,16 @@ class ComposerRepository extends ArrayRepository { protected $packages; + static public function supports($type, $name = '', $url = '') + { + return 'composer' === strtolower($type) && '' !== $url; + } + + static public function create($type, $name = '', $url = '') + { + return new static($url); + } + public function __construct($url) { $this->url = $url; @@ -98,4 +108,4 @@ class ComposerRepository extends ArrayRepository } return $links; } -} \ No newline at end of file +} diff --git a/src/Composer/Repository/GitRepository.php b/src/Composer/Repository/GitRepository.php index 8eb0f95da..61d58cdff 100644 --- a/src/Composer/Repository/GitRepository.php +++ b/src/Composer/Repository/GitRepository.php @@ -26,6 +26,16 @@ class GitRepository extends ArrayRepository { protected $packages; + static public function supports($type, $name = '', $url = '') + { + return 'git' === strtolower($type) && '' !== $url; + } + + static public function create($type, $name = '', $url = '') + { + return new static($url); + } + public function __construct($url) { $this->url = $url; @@ -98,4 +108,4 @@ class GitRepository extends ArrayRepository } return $links; } -} \ No newline at end of file +} diff --git a/src/Composer/Repository/PearRepository.php b/src/Composer/Repository/PearRepository.php index 6fc4e94f4..24b8a00e1 100644 --- a/src/Composer/Repository/PearRepository.php +++ b/src/Composer/Repository/PearRepository.php @@ -26,6 +26,16 @@ class PearRepository extends ArrayRepository private $name; private $url; + static public function supports($type, $name = '', $url = '') + { + return 'pear' === strtolower($type) && '' !== $url; + } + + static public function create($type, $name = '', $url = '') + { + return new static($url, $name); + } + public function __construct($url, $name = '') { if (!filter_var($url, FILTER_VALIDATE_URL)) { diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index ebb9b81ed..acc685597 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -22,6 +22,11 @@ class PlatformRepository extends ArrayRepository { protected $packages; + static public function supports($type, $name = '', $url = '') + { + return 'platform' === strtolower($type); + } + protected function initialize() { parent::initialize(); @@ -51,4 +56,4 @@ class PlatformRepository extends ArrayRepository $this->addPackage($ext); } } -} \ No newline at end of file +} diff --git a/src/Composer/Repository/RepositoryFactory.php b/src/Composer/Repository/RepositoryFactory.php new file mode 100644 index 000000000..b18138f5b --- /dev/null +++ b/src/Composer/Repository/RepositoryFactory.php @@ -0,0 +1,63 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +/** + * @author Konstantin Kudryashiv + */ +class RepositoryFactory +{ + private $classes = array(); + + public function __construct(array $classes) + { + foreach ($classes as $class) { + $this->registerRepositoryClass($class); + } + } + + public function registerRepositoryClass($class) + { + $reflection = new \ReflectionClass($class); + + if (!$reflection->implementsInterface('Composer\Repository\RepositoryInterface')) { + throw new \InvalidArgumentException( + 'Repository class should implement "RepositoryInterface", but "'.$class.'"'. + 'given' + ); + } + + $this->classes[] = $class; + } + + public function classWhichSupports($type, $name = '', $url = '') + { + foreach ($this->classes as $class) { + if ($class::supports($type, $name, $url)) { + return $class; + } + } + + throw new \UnexpectedValueException(sprintf( + "Can not find repository class, which supports:\n%s", + json_encode(array($name => array($type => $url))) + )); + } + + public function create($type, $name = '', $url = '') + { + $class = $this->classWhichSupports($type, $name, $url); + + return $class::create($type, $name, $url); + } +} diff --git a/src/Composer/Repository/RepositoryInterface.php b/src/Composer/Repository/RepositoryInterface.php index 49e16690e..ac702140d 100644 --- a/src/Composer/Repository/RepositoryInterface.php +++ b/src/Composer/Repository/RepositoryInterface.php @@ -17,5 +17,8 @@ namespace Composer\Repository; */ interface RepositoryInterface extends \Countable { + static function supports($type, $name = '', $url = ''); + static function create($type, $name = '', $url = ''); + function getPackages(); } From 6caa77fbbfa09037a2591f63852960ec9c8a8b37 Mon Sep 17 00:00:00 2001 From: everzet Date: Sat, 17 Sep 2011 15:55:28 +0300 Subject: [PATCH 03/41] installer refactoring --- src/Composer/Command/Command.php | 21 ++++- .../Console/Package/VerboseManager.php | 40 --------- src/Composer/Installer/InstallerInterface.php | 24 +++-- src/Composer/Installer/LibraryInstaller.php | 38 +++++--- src/Composer/Installer/Operation.php | 57 ++++++++++++ src/Composer/Package/Manager.php | 87 ------------------- 6 files changed, 115 insertions(+), 152 deletions(-) delete mode 100644 src/Composer/Console/Package/VerboseManager.php create mode 100644 src/Composer/Installer/Operation.php delete mode 100644 src/Composer/Package/Manager.php diff --git a/src/Composer/Command/Command.php b/src/Composer/Command/Command.php index 2fb47285c..bde9bfaf1 100644 --- a/src/Composer/Command/Command.php +++ b/src/Composer/Command/Command.php @@ -13,11 +13,17 @@ namespace Composer\Command; use Symfony\Component\Console\Command\Command as BaseCommand; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Composer\DependencyResolver\Request; +use Composer\DependencyResolver\Solver; +use Composer\Installer\Operation; /** * Base class for Composer commands * * @author Ryan Weaver + * @authro Konstantin Kudryashov */ abstract class Command extends BaseCommand { @@ -28,4 +34,17 @@ abstract class Command extends BaseCommand { return $this->getApplication()->getComposer(); } -} \ No newline at end of file + + protected function solveDependencies(Request $request, Solver $solver) + { + $operations = array(); + foreach ($solver->solve($request) as $task) { + $installer = $this->getComposer()->getInstaller($task['package']->getType()); + $operation = new Operation($installer, $task['job'], $task['package']); + + $operations[] = $operation; + } + + return $operations; + } +} diff --git a/src/Composer/Console/Package/VerboseManager.php b/src/Composer/Console/Package/VerboseManager.php deleted file mode 100644 index 7562ec57d..000000000 --- a/src/Composer/Console/Package/VerboseManager.php +++ /dev/null @@ -1,40 +0,0 @@ -composer = $composer; - } - - public function install(PackageInterface $package) - { - $this->output->writeln('> Installing '.$package->getName()); - - parent::install($package); - } - - public function update(PackageInterface $package) - { - $this->output->writeln('> Updating '.$package->getName()); - - parent::update($package); - } - - public function remove(PackageInterface $package) - { - $this->output->writeln('> Removing '.$package->getName()); - - parent::remove($package); - } -} diff --git a/src/Composer/Installer/InstallerInterface.php b/src/Composer/Installer/InstallerInterface.php index 6a3ac6fbd..fc370b18f 100644 --- a/src/Composer/Installer/InstallerInterface.php +++ b/src/Composer/Installer/InstallerInterface.php @@ -12,22 +12,18 @@ namespace Composer\Installer; -use Composer\Downloader\DownloaderInterface; use Composer\Package\PackageInterface; +use Composer\Composer; /** - * Package Installer - * - * @author Kirill chEbba Chebunin - */ -interface InstallerInterface + * @author Konstantin Kudryashov + */ +class InstallerInterface { - /** - * Install package - * - * @param PackageInterface $package - * @param DownloaderInterface $downloader - * @param string $type - */ - function install(PackageInterface $package, DownloaderInterface $downloader, $type); + function setComposer(Composer $composer); + + function isInstalled(PackageInterface $package); + function install(PackageInterface $package); + function update(PackageInterface $package); + function remove(PackageInterface $package); } diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 1e7ed2583..b2a954ca6 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -14,42 +14,60 @@ namespace Composer\Installer; use Composer\Downloader\DownloaderInterface; use Composer\Package\PackageInterface; +use Composer\Composer; /** * @author Jordi Boggiano */ class LibraryInstaller implements InstallerInterface { - protected $dir; + private $dir; + private $composer; public function __construct($dir = 'vendor') { $this->dir = $dir; } - public function install(PackageInterface $package, DownloaderInterface $downloader, $type) + public function setComposer(Composer $composer) { - if ($type === 'dist') { - $downloader->download($package, $this->dir, $package->getDistUrl(), $package->getDistSha1Checksum()); - } elseif ($type === 'source') { - $downloader->download($package, $this->dir, $package->getSourceUrl()); + $this->composer = $composer; + } + + public function install(PackageInterface $package) + { + if ($package->getDistType()) { + + $this->composer->getDownloader($package->getDistType())->download( + $package, $this->dir, $package->getDistUrl(), $package->getDistSha1Checksum() + ); + + } elseif ($package->getSourceType()) { + + $this->composer->getDownloader($package->getSourceType())->download( + $package, $this->dir, $package->getSourceUrl() + ); + } else { - throw new \InvalidArgumentException('Type must be one of (dist, source), '.$type.' given.'); + throw new \InvalidArgumentException( + 'Type must be one of (dist, source), '.$type.' given.' + ); } + return true; } - public function isInstalled(PackageInterface $package, $downloader, $type) + public function isInstalled(PackageInterface $package) { // TODO: implement installation check } - public function update(PackageInterface $package, $downloader, $type) + public function update(PackageInterface $package) { // TODO: implement package update } - public function remove(PackageInterface $package, $downloader, $type) + public function remove(PackageInterface $package) { // TODO: implement package removal } diff --git a/src/Composer/Installer/Operation.php b/src/Composer/Installer/Operation.php new file mode 100644 index 000000000..b442ff743 --- /dev/null +++ b/src/Composer/Installer/Operation.php @@ -0,0 +1,57 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Installer; + +use Composer\Installer\InstallerInterface; +use Composer\Package\PackageInterface; + +/** + * Installer operation command + * + * @author Konstantin Kudryashov + */ +class Operation +{ + private $installer; + private $type; + private $package; + + public function __construct(InstallerInterface $installer, $type, PackageInterface $package) + { + $type = strtolower($type); + if (!in_array($type, array('install', 'update', 'remove'))) { + throw new \UnexpectedValueException('Unhandled operation type: ' . $type); + } + + $this->installer = $installer; + $this->type = $type; + $this->package = $package; + } + + public function getType() + { + return $this->type; + } + + public function getPackage() + { + return $this->package; + } + + public function execute() + { + $method = $this->getType(); + + return $this->installer->$method($this->getPackage()); + } +} diff --git a/src/Composer/Package/Manager.php b/src/Composer/Package/Manager.php deleted file mode 100644 index db0f73ebb..000000000 --- a/src/Composer/Package/Manager.php +++ /dev/null @@ -1,87 +0,0 @@ -composer = $composer; - } - - public function isInstalled(PackageInterface $package) - { - $installer = $this->composer->getInstaller($package->getType()); - $downloader = $this->getDownloaderForPackage($package); - $packageType = $this->getTypeForPackage($package); - - return $installer->isInstalled($package, $downloader, $packageType); - } - - public function install(PackageInterface $package) - { - $installer = $this->composer->getInstaller($package->getType()); - $downloader = $this->getDownloaderForPackage($package); - $packageType = $this->getTypeForPackage($package); - - if (!$installer->install($package, $downloader, $packageType)) { - throw new \LogicException($package->getName().' could not be installed.'); - } - } - - public function update(PackageInterface $package) - { - $installer = $this->composer->getInstaller($package->getType()); - $downloader = $this->getDownloaderForPackage($package); - $packageType = $this->getTypeForPackage($package); - - if (!$installer->update($package, $downloader, $packageType)) { - throw new \LogicException($package->getName().' could not be updated.'); - } - } - - public function remove(PackageInterface $package) - { - $installer = $this->composer->getInstaller($package->getType()); - $downloader = $this->getDownloaderForPackage($package); - $packageType = $this->getTypeForPackage($package); - - if (!$installer->remove($package, $downloader, $packageType)) { - throw new \LogicException($package->getName().' could not be removed.'); - } - } - - private function getDownloaderForPackage(PackageInterface $package) - { - if ($package->getDistType()) { - $downloader = $this->composer->getDownloader($package->getDistType); - } elseif ($package->getSourceType()) { - $downloader = $this->copmoser->getDownloader($package->getSourceType()); - } else { - throw new \UnexpectedValueException( - 'Package '.$package->getName().' has no source or dist URL.' - ); - } - - return $downloader; - } - - private function getTypeForPackage(PackageInterface $package) - { - if ($package->getDistType()) { - $type = 'dist'; - } elseif ($package->getSourceType()) { - $type = 'source'; - } else { - throw new \UnexpectedValueException( - 'Package '.$package->getName().' has no source or dist URL.' - ); - } - - return $type; - } -} From d7fe0dfda4ced8439934d32202479885744e6eb3 Mon Sep 17 00:00:00 2001 From: everzet Date: Sat, 17 Sep 2011 16:12:45 +0300 Subject: [PATCH 04/41] added missing interfaces --- .../Downloader/DownloaderInterface.php | 18 +++--------------- src/Composer/Downloader/GitDownloader.php | 2 +- src/Composer/Downloader/PearDownloader.php | 2 +- src/Composer/Downloader/ZipDownloader.php | 2 +- 4 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/Composer/Downloader/DownloaderInterface.php b/src/Composer/Downloader/DownloaderInterface.php index b424bba3e..d399ebf88 100644 --- a/src/Composer/Downloader/DownloaderInterface.php +++ b/src/Composer/Downloader/DownloaderInterface.php @@ -15,21 +15,9 @@ namespace Composer\Downloader; use Composer\Package\PackageInterface; /** - * Package Downloader - * - * @author Kirill chEbba Chebunin - */ -interface DownloaderInterface + * @author Konstantin Kudryashov + */ +interface DownloaderInterface { - /** - * Download package - * - * @param PackageInterface $package Downloaded package - * @param string $path Download to - * @param string $url Download from - * @param string|null $checksum Package checksum - * - * @throws \UnexpectedValueException - */ function download(PackageInterface $package, $path, $url, $checksum = null); } diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index d7a98bed7..90fa49baa 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -42,4 +42,4 @@ class GitDownloader implements DownloaderInterface system('git archive --format=tar --prefix='.escapeshellarg($package->getName()).' --remote='.escapeshellarg($url).' master | tar -xf -'); } } -} \ No newline at end of file +} diff --git a/src/Composer/Downloader/PearDownloader.php b/src/Composer/Downloader/PearDownloader.php index 56e45c677..0f4c86289 100644 --- a/src/Composer/Downloader/PearDownloader.php +++ b/src/Composer/Downloader/PearDownloader.php @@ -66,4 +66,4 @@ class PearDownloader implements DownloaderInterface } chdir($cwd); } -} \ No newline at end of file +} diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php index 1c5fa547a..94a7eac0e 100644 --- a/src/Composer/Downloader/ZipDownloader.php +++ b/src/Composer/Downloader/ZipDownloader.php @@ -73,4 +73,4 @@ class ZipDownloader implements DownloaderInterface throw new \UnexpectedValueException($zipName.' is not a valid zip archive, got error code '.$retval); } } -} \ No newline at end of file +} From d2150a3c2ede412ffd707227eeb0956f1eb57ad8 Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 18 Sep 2011 13:14:58 +0300 Subject: [PATCH 05/41] installer and downloaders update --- .../Downloader/DownloaderInterface.php | 1 + src/Composer/Downloader/GitDownloader.php | 15 ++--- src/Composer/Downloader/PearDownloader.php | 7 ++ src/Composer/Downloader/ZipDownloader.php | 7 ++ src/Composer/Installer/LibraryInstaller.php | 64 ++++++++++++------- 5 files changed, 62 insertions(+), 32 deletions(-) diff --git a/src/Composer/Downloader/DownloaderInterface.php b/src/Composer/Downloader/DownloaderInterface.php index d399ebf88..4416ac46e 100644 --- a/src/Composer/Downloader/DownloaderInterface.php +++ b/src/Composer/Downloader/DownloaderInterface.php @@ -20,4 +20,5 @@ use Composer\Package\PackageInterface; interface DownloaderInterface { function download(PackageInterface $package, $path, $url, $checksum = null); + function isDownloaded(PackageInterface $package, $path); } diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 90fa49baa..bb22bc0be 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -28,18 +28,17 @@ class GitDownloader implements DownloaderInterface public function download(PackageInterface $package, $path, $url, $checksum = null) { - if (!is_dir($path)) { - if (file_exists($path)) { - throw new \UnexpectedValueException($path.' exists and is not a directory.'); - } - if (!mkdir($path, 0777, true)) { - throw new \UnexpectedValueException($path.' does not exist and could not be created.'); - } - } if ($this->clone) { system('git clone '.escapeshellarg($url).' -b master '.escapeshellarg($path.'/'.$package->getName())); } else { system('git archive --format=tar --prefix='.escapeshellarg($package->getName()).' --remote='.escapeshellarg($url).' master | tar -xf -'); } } + + public function isDownloaded(PackageInterface $package, $path) + { + $targetPath = $path . '/' . $package->getName(); + + return is_dir($targetPath); + } } diff --git a/src/Composer/Downloader/PearDownloader.php b/src/Composer/Downloader/PearDownloader.php index 0f4c86289..c93519b1c 100644 --- a/src/Composer/Downloader/PearDownloader.php +++ b/src/Composer/Downloader/PearDownloader.php @@ -66,4 +66,11 @@ class PearDownloader implements DownloaderInterface } chdir($cwd); } + + public function isDownloaded(PackageInterface $package, $path) + { + $targetPath = $path . '/' . $package->getName(); + + return is_dir($targetPath); + } } diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php index 94a7eac0e..098ba151a 100644 --- a/src/Composer/Downloader/ZipDownloader.php +++ b/src/Composer/Downloader/ZipDownloader.php @@ -73,4 +73,11 @@ class ZipDownloader implements DownloaderInterface throw new \UnexpectedValueException($zipName.' is not a valid zip archive, got error code '.$retval); } } + + public function isDownloaded(PackageInterface $package, $path) + { + $targetPath = $path . '/' . $package->getName(); + + return is_dir($targetPath); + } } diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index b2a954ca6..e45aa6586 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -18,15 +18,18 @@ use Composer\Composer; /** * @author Jordi Boggiano + * @author Konstantin Kudryashov */ class LibraryInstaller implements InstallerInterface { private $dir; private $composer; + private $preferSource; - public function __construct($dir = 'vendor') + public function __construct($dir = 'vendor', $preferSource = false) { $this->dir = $dir; + $this->preferSource = $preferSource; } public function setComposer(Composer $composer) @@ -36,39 +39,52 @@ class LibraryInstaller implements InstallerInterface public function install(PackageInterface $package) { - if ($package->getDistType()) { + if (!is_dir($this->dir)) { + if (file_exists($this->dir)) { + throw new \UnexpectedValueException($this->dir.' exists and is not a directory.'); + } + if (!mkdir($this->dir, 0777, true)) { + throw new \UnexpectedValueException($this->path.' does not exist and could not be created.'); + } + } - $this->composer->getDownloader($package->getDistType())->download( + if (!($this->preferSource && $package->getSourceType()) && $package->getDistType()) { + $downloader = $this->composer->getDownloader($package->getDistType()); + + return $downloader->download( $package, $this->dir, $package->getDistUrl(), $package->getDistSha1Checksum() ); - - } elseif ($package->getSourceType()) { - - $this->composer->getDownloader($package->getSourceType())->download( - $package, $this->dir, $package->getSourceUrl() - ); - - } else { - throw new \InvalidArgumentException( - 'Type must be one of (dist, source), '.$type.' given.' - ); } - return true; + if ($package->getSourceType()) { + $downloader = $this->composer->getDownloader($package->getSourceType()); + + return $downloader->download( + $package, $this->dir, $package->getSourceUrl() + ); + } + + throw new \InvalidArgumentException('Package should have dist or source specified'); } public function isInstalled(PackageInterface $package) { - // TODO: implement installation check - } + if ($package->getSourceType()) { + $downloader = $this->composer->getDownloader($package->getSourceType()); - public function update(PackageInterface $package) - { - // TODO: implement package update - } + if ($downloader->isDownloaded($package, $this->dir)) { + return true; + } + } - public function remove(PackageInterface $package) - { - // TODO: implement package removal + if ($package->getDistType()) { + $downloader = $this->composer->getDownloader($package->getDistType()); + + if ($downloader->isDownloaded($package, $this->dir)) { + return true; + } + } + + return false; } } From 9deb10361f8e7f532d188bd5d8d0969be401c19b Mon Sep 17 00:00:00 2001 From: everzet Date: Wed, 21 Sep 2011 00:34:06 +0300 Subject: [PATCH 06/41] second batch of refactoring --- src/Composer/Command/Command.php | 16 ++ src/Composer/ConfigurableComposer.php | 174 ------------------ src/Composer/Console/Application.php | 30 ++- src/Composer/Installer/InstallerInterface.php | 14 +- src/Composer/Installer/LibraryInstaller.php | 53 ++++-- src/Composer/Package/BasePackage.php | 21 --- src/Composer/Package/Loader/ArrayLoader.php | 117 ++++++++++++ src/Composer/Package/Loader/JsonLoader.php | 60 ++++++ src/Composer/Package/PackageLock.php | 91 +++++++++ .../Package/Version/VersionParser.php | 43 +++++ src/Composer/Repository/ArrayRepository.php | 10 - .../Repository/ComposerRepository.php | 75 +------- src/Composer/Repository/GitRepository.php | 16 +- src/Composer/Repository/PearRepository.php | 19 +- .../Repository/PlatformRepository.php | 7 - src/Composer/Repository/RepositoryFactory.php | 63 ------- 16 files changed, 414 insertions(+), 395 deletions(-) delete mode 100644 src/Composer/ConfigurableComposer.php create mode 100644 src/Composer/Package/Loader/ArrayLoader.php create mode 100644 src/Composer/Package/Loader/JsonLoader.php create mode 100644 src/Composer/Package/PackageLock.php create mode 100644 src/Composer/Package/Version/VersionParser.php delete mode 100644 src/Composer/Repository/RepositoryFactory.php diff --git a/src/Composer/Command/Command.php b/src/Composer/Command/Command.php index bde9bfaf1..54f44c9d3 100644 --- a/src/Composer/Command/Command.php +++ b/src/Composer/Command/Command.php @@ -35,6 +35,22 @@ abstract class Command extends BaseCommand return $this->getApplication()->getComposer(); } + /** + * @return \Composer\Package\PackageInterface + */ + protected function getPackage() + { + return $this->getApplication()->getPackage(); + } + + /** + * @return \Composer\Package\PackageLock + */ + protected function getLock() + { + return $this->getApplication()->getLock(); + } + protected function solveDependencies(Request $request, Solver $solver) { $operations = array(); diff --git a/src/Composer/ConfigurableComposer.php b/src/Composer/ConfigurableComposer.php deleted file mode 100644 index 91704aa28..000000000 --- a/src/Composer/ConfigurableComposer.php +++ /dev/null @@ -1,174 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer; - -use Composer\Repository\RepositoryFactory; -use Composer\Package; -use Composer\Installer\LibraryInstaller; - -/** - * @author Konstantin Kudryashiv - */ -class ConfigurableComposer extends Composer -{ - private $configFile; - private $lockFile; - private $isLocked = false; - private $lockedPackages = array(); - private $repositoryFactory; - - public function __construct($configFile = 'composer.json', $lockFile = 'composer.lock', - RepositoryFactory $repositoryFactory) - { - $this->configFile = $configFile; - $this->lockFile = $lockFile; - $this->repositoryFactory = $repositoryFactory; - - $this->setRepository('Platform', $repositoryFactory->create('platform')); - - if (!file_exists($configFile)) { - throw new \UnexpectedValueException('Can not find composer config file'); - } - - $config = $this->loadJsonConfig($configFile); - - if (isset($config['path'])) { - $this->setInstaller('library', new LibraryInstaller($config['path'])); - } else { - $this->setInstaller('library', new LibraryInstaller()); - } - - if (isset($config['repositories'])) { - $repositories = $this->loadRepositoriesFromConfig($config['repositories']) - foreach ($repositories as $name => $repository) { - $this->setRepository($name, $repository); - } - } - - if (isset($config['require'])) { - $requirements = $this->loadRequirementsFromConfig($config['require']); - foreach ($requirements as $name => $constraint) { - $this->setRequirement($name, $constraint); - } - } - - if (file_exists($lockFile)) { - $lock = $this->loadJsonConfig($lockFile); - $platform = $this->getRepository('Platform'); - $packages = $this->loadPackagesFromLock($lock); - foreach ($packages as $package) { - if ($this->hasRequirement($package->getName())) { - $platform->addPackage($package); - $this->lockedPackages[] = $package; - } - } - $this->isLocked = true; - } - } - - public function isLocked() - { - return $this->isLocked; - } - - public function getLockedPackages() - { - return $this->lockedPackages; - } - - public function lock(array $packages) - { - // TODO: write installed packages info into $this->lockFile - } - - private function loadPackagesFromLock(array $lockList) - { - $packages = array(); - foreach ($lockList as $info) { - $packages[] = new Package\MemoryPackage($info['package'], $info['version']); - } - - return $packages; - } - - private function loadRepositoriesFromConfig(array $repositoriesList) - { - $repositories = array(); - foreach ($repositoriesList as $name => $spec) { - if (is_array($spec) && count($spec) === 1) { - $repositories[$name] = $this->repositoryFactory->create( - key($spec), $name, current($spec) - ); - } elseif (null === $spec) { - $repositories[$name] = null; - } else { - throw new \UnexpectedValueException( - 'Invalid repositories specification '. - json_encode($spec).', should be: {"type": "url"}' - ); - } - } - - return $repositories; - } - - private function loadRequirementsFromConfig(array $requirementsList) - { - $requirements = array(); - foreach ($requirementsList as $name => $version) { - $name = strtolower($name); - if ('latest' === $version) { - $requirements[$name] = null; - } else { - preg_match('#^([>=<~]*)([\d.]+.*)$#', $version, $match); - if (!$match[1]) { - $match[1] = '='; - } - $constraint = new Package\LinkConstraint\VersionConstraint($match[1], $match[2]); - $requirements[$name] = $constraint; - } - } - - return $requirements; - } - - private function loadJsonConfig($configFile) - { - $config = json_decode(file_get_contents($configFile), true); - if (!$config) { - switch (json_last_error()) { - case JSON_ERROR_NONE: - $msg = 'No error has occurred, is your composer.json file empty?'; - break; - case JSON_ERROR_DEPTH: - $msg = 'The maximum stack depth has been exceeded'; - break; - case JSON_ERROR_STATE_MISMATCH: - $msg = 'Invalid or malformed JSON'; - break; - case JSON_ERROR_CTRL_CHAR: - $msg = 'Control character error, possibly incorrectly encoded'; - break; - case JSON_ERROR_SYNTAX: - $msg = 'Syntax error'; - break; - case JSON_ERROR_UTF8: - $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; - break; - } - throw new \UnexpectedValueException('Incorrect composer.json file: '.$msg); - } - - return $config; - } -} diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 84f307143..c61a8cdf2 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -13,11 +13,13 @@ namespace Composer\Console; use Symfony\Component\Console\Application as BaseApplication; -use Composer\Composer; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Finder\Finder; use Composer\Command\InstallCommand; +use Composer\Composer; +use Composer\Package\PackageInterface; +use Composer\Package\PackageLock; /** * The console application that handles the commands @@ -27,12 +29,16 @@ use Composer\Command\InstallCommand; class Application extends BaseApplication { private $composer; + private $package; + private $lock; - public function __construct(Composer $composer) + public function __construct(Composer $composer, PackageInterface $package, PackageLock $lock) { parent::__construct('Composer', Composer::VERSION); $this->composer = $composer; + $this->package = $package; + $this->lock = $lock; } /** @@ -59,10 +65,26 @@ class Application extends BaseApplication } /** - * Initializes all the composer commands + * @return PackageInterface + */ + public function getPackage() + { + return $this->package; + } + + /** + * @return PackageLock + */ + public function getLock() + { + return $this->lock; + } + + /** + * Looks for all *Command files in Composer's Command directory */ protected function registerCommands() { $this->add(new InstallCommand()); } -} \ No newline at end of file +} diff --git a/src/Composer/Installer/InstallerInterface.php b/src/Composer/Installer/InstallerInterface.php index fc370b18f..edd9246f4 100644 --- a/src/Composer/Installer/InstallerInterface.php +++ b/src/Composer/Installer/InstallerInterface.php @@ -13,17 +13,15 @@ namespace Composer\Installer; use Composer\Package\PackageInterface; -use Composer\Composer; +use Composer\Downloader\DownloaderInterface; /** * @author Konstantin Kudryashov */ -class InstallerInterface +interface InstallerInterface { - function setComposer(Composer $composer); - - function isInstalled(PackageInterface $package); - function install(PackageInterface $package); - function update(PackageInterface $package); - function remove(PackageInterface $package); + function isInstalled(PackageInterface $package, DownloaderInterface $sourceDownloader = null, + DownloaderInterface $distDownloader = null); + function install(PackageInterface $package, DownloaderInterface $sourceDownloader = null, + DownloaderInterface $distDownloader = null); } diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index e45aa6586..8f55b86eb 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -14,7 +14,7 @@ namespace Composer\Installer; use Composer\Downloader\DownloaderInterface; use Composer\Package\PackageInterface; -use Composer\Composer; +use Composer\Downloader\DownloaderInterface; /** * @author Jordi Boggiano @@ -23,33 +23,52 @@ use Composer\Composer; class LibraryInstaller implements InstallerInterface { private $dir; - private $composer; private $preferSource; + private $downloaders = array(); public function __construct($dir = 'vendor', $preferSource = false) { $this->dir = $dir; $this->preferSource = $preferSource; + + if (!is_dir($this->dir)) { + if (file_exists($this->dir)) { + throw new \UnexpectedValueException( + $this->dir.' exists and is not a directory.' + ); + } + if (!mkdir($this->dir, 0777, true)) { + throw new \UnexpectedValueException( + $this->dir.' does not exist and could not be created.' + ); + } + } } - public function setComposer(Composer $composer) + public function setDownloader($type, DownloaderInterface $downloader = null) { - $this->composer = $composer; + if (null === $downloader) { + unset($this->downloaders[$type]); + + return; + } + + $this->downloaders[$type] = $downloader; + } + + public function getDownloader($type) + { + if (!isset($this->downloaders[$type])) { + throw new \UnexpectedValueException('Unknown source type: '.$type); + } + + return $this->downloaders[$type]; } public function install(PackageInterface $package) { - if (!is_dir($this->dir)) { - if (file_exists($this->dir)) { - throw new \UnexpectedValueException($this->dir.' exists and is not a directory.'); - } - if (!mkdir($this->dir, 0777, true)) { - throw new \UnexpectedValueException($this->path.' does not exist and could not be created.'); - } - } - if (!($this->preferSource && $package->getSourceType()) && $package->getDistType()) { - $downloader = $this->composer->getDownloader($package->getDistType()); + $downloader = $this->getDownloader($package->getDistType()); return $downloader->download( $package, $this->dir, $package->getDistUrl(), $package->getDistSha1Checksum() @@ -57,7 +76,7 @@ class LibraryInstaller implements InstallerInterface } if ($package->getSourceType()) { - $downloader = $this->composer->getDownloader($package->getSourceType()); + $downloader = $this->getDownloader($package->getSourceType()); return $downloader->download( $package, $this->dir, $package->getSourceUrl() @@ -70,7 +89,7 @@ class LibraryInstaller implements InstallerInterface public function isInstalled(PackageInterface $package) { if ($package->getSourceType()) { - $downloader = $this->composer->getDownloader($package->getSourceType()); + $downloader = $this->getDownloader($package->getSourceType()); if ($downloader->isDownloaded($package, $this->dir)) { return true; @@ -78,7 +97,7 @@ class LibraryInstaller implements InstallerInterface } if ($package->getDistType()) { - $downloader = $this->composer->getDownloader($package->getDistType()); + $downloader = $this->getDownloader($package->getDistType()); if ($downloader->isDownloaded($package, $this->dir)) { return true; diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index 73afc1a79..ddeaf65b3 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -143,25 +143,4 @@ abstract class BasePackage implements PackageInterface { return $this->getName().'-'.$this->getVersion().'-'.$this->getReleaseType(); } - - /** - * Parses a version string and returns an array with the version, its type (alpha, beta, RC, stable) and a dev flag (for development branches tracking) - * - * @param string $version - * @return array - */ - public static function parseVersion($version) - { - if (!preg_match('#^v?(\d+)(\.\d+)?(\.\d+)?-?((?:beta|RC|alpha)\d*)?-?(dev)?$#i', $version, $matches)) { - throw new \UnexpectedValueException('Invalid version string '.$version); - } - - return array( - 'version' => $matches[1] - .(!empty($matches[2]) ? $matches[2] : '.0') - .(!empty($matches[3]) ? $matches[3] : '.0'), - 'type' => !empty($matches[4]) ? strtolower($matches[4]) : 'stable', - 'dev' => !empty($matches[5]), - ); - } } diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php new file mode 100644 index 000000000..89f2d3307 --- /dev/null +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -0,0 +1,117 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Loader; + +use Composer\Package; + +/** + * @author Konstantin Kudryashiv + */ +class ArrayLoader +{ + protected $supportedLinkTypes = array( + 'require' => 'requires', + 'conflict' => 'conflicts', + 'provide' => 'provides', + 'replace' => 'replaces', + 'recommend' => 'recommends', + 'suggest' => 'suggests', + ); + + public function load($config) + { + $this->validateConfig($config); + + $versionParser = new Package\Version\VersionParser(); + $version = $versionParser->parse($config['version']); + $package = new Package\MemoryPackage($config['name'], $version['version'], $version['type']); + + $package->setType($config['type']); + + if (isset($config['extra'])) { + $package->setExtra($config['extra']); + } + + if (isset($config['license'])) { + $package->setLicense($config['license']); + } + + if (isset($config['source'])) { + if (!isset($config['source']['type']) || !isset($config['source']['url'])) { + throw new \UnexpectedValueException(sprintf( + "package source should be specified as {\"type\": ..., \"url\": ...},\n%s given", + json_encode($config['source']) + )); + } + $package->setSourceType($config['source']['type']); + $package->setSourceUrl($config['source']['url']); + } + + if (isset($config['dist'])) { + if (!isset($config['dist']['type']) + || !isset($config['dist']['url']) + || !isset($config['dist']['shasum'])) { + throw new \UnexpectedValueException(sprintf( + "package dist should be specified as ". + "{\"type\": ..., \"url\": ..., \"shasum\": ...},\n%s given", + json_encode($config['source']) + )); + } + $package->setDistType($config['dist']['type']); + $package->setDistUrl($config['dist']['url']); + $package->setDistSha1Checksum($config['dist']['shasum']); + } + + foreach ($this->supportedLinkTypes as $type => $description) { + if (isset($config[$type])) { + $method = 'set'.ucfirst($description); + $package->{$method}( + $this->loadLinksFromConfig($package->getName(), $description, $config['require']) + ); + } + } + + return $package; + } + + private function loadLinksFromConfig($srcPackageName, $description, array $linksSpecs) + { + $links = array(); + foreach ($linksSpecs as $packageName => $version) { + $name = strtolower($packageName); + + preg_match('#^([>=<~]*)([\d.]+.*)$#', $version, $match); + if (!$match[1]) { + $match[1] = '='; + } + + $constraint = new Package\LinkConstraint\VersionConstraint($match[1], $match[2]); + $links[] = new Package\Link($srcPackageName, $packageName, $constraint, $description); + } + + return $requirements; + } + + private function validateConfig(array $config) + { + if (!isset($config['name'])) { + throw new \UnexpectedValueException('name is required for package'); + } + if (!isset($config['type'])) { + throw new \UnexpectedValueException('type is required for package'); + } + if (!isset($config['version'])) { + throw new \UnexpectedValueException('version is required for package'); + } + } +} diff --git a/src/Composer/Package/Loader/JsonLoader.php b/src/Composer/Package/Loader/JsonLoader.php new file mode 100644 index 000000000..b0fcbd52a --- /dev/null +++ b/src/Composer/Package/Loader/JsonLoader.php @@ -0,0 +1,60 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Loader; + +/** + * @author Konstantin Kudryashiv + */ +class JsonLoader extends ArrayLoader +{ + public function load($json) + { + $config = $this->loadJsonConfig($json); + + return parent::load($config); + } + + private function loadJsonConfig($json) + { + if (is_file($json)) { + $json = file_get_contents($json); + } + + $config = json_decode($json, true); + if (!$config) { + switch (json_last_error()) { + case JSON_ERROR_NONE: + $msg = 'No error has occurred, is your composer.json file empty?'; + break; + case JSON_ERROR_DEPTH: + $msg = 'The maximum stack depth has been exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Invalid or malformed JSON'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Control character error, possibly incorrectly encoded'; + break; + case JSON_ERROR_SYNTAX: + $msg = 'Syntax error'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + } + throw new \UnexpectedValueException('Incorrect composer.json file: '.$msg); + } + + return $config; + } +} diff --git a/src/Composer/Package/PackageLock.php b/src/Composer/Package/PackageLock.php new file mode 100644 index 000000000..5e79cccce --- /dev/null +++ b/src/Composer/Package/PackageLock.php @@ -0,0 +1,91 @@ + + * 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\MemoryPackage; +use Composer\Package\Version\VersionParser; + +/** + * @author Konstantin Kudryashiv + */ +class PackageLock +{ + private $file; + private $isLocked = false; + + public function __construct($file = 'composer.lock') + { + if (file_exists($file)) { + $this->file = $file; + $this->isLocked = true; + } + } + + public function isLocked() + { + return $this->isLocked; + } + + public function getLockedPackages() + { + $lockList = $this->loadJsonConfig($this->file); + + $versionParser = new VersionParser(); + $packages = array(); + foreach ($lockList as $info) { + $version = $versionParser->parse($info['version']); + $packages[] = new MemoryPackage($info['package'], $version['version'], $version['type']); + } + + return $packages; + } + + public function lock(array $packages) + { + // TODO: write installed packages info into $this->file + } + + private function loadJsonConfig($json) + { + if (is_file($json)) { + $json = file_get_contents($json); + } + + $config = json_decode($json, true); + if (!$config) { + switch (json_last_error()) { + case JSON_ERROR_NONE: + $msg = 'No error has occurred, is your composer.json file empty?'; + break; + case JSON_ERROR_DEPTH: + $msg = 'The maximum stack depth has been exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Invalid or malformed JSON'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Control character error, possibly incorrectly encoded'; + break; + case JSON_ERROR_SYNTAX: + $msg = 'Syntax error'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + } + throw new \UnexpectedValueException('Incorrect composer.json file: '.$msg); + } + + return $config; + } +} diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php new file mode 100644 index 000000000..39c0d1215 --- /dev/null +++ b/src/Composer/Package/Version/VersionParser.php @@ -0,0 +1,43 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Version; + +/** + * Version parser + * + * @author Konstantin Kudryashov + * @author Nils Adermann + */ +class VersionParser +{ + /** + * Parses a version string and returns an array with the version, its type (alpha, beta, RC, stable) and a dev flag (for development branches tracking) + * + * @param string $version + * @return array + */ + public function parse($version) + { + if (!preg_match('#^v?(\d+)(\.\d+)?(\.\d+)?-?((?:beta|RC|alpha)\d*)?-?(dev)?$#i', $version, $matches)) { + throw new \UnexpectedValueException('Invalid version string '.$version); + } + + return array( + 'version' => $matches[1] + .(!empty($matches[2]) ? $matches[2] : '.0') + .(!empty($matches[3]) ? $matches[3] : '.0'), + 'type' => strtolower(!empty($matches[4]) ? $matches[4] : 'stable'), + 'dev' => !empty($matches[5]), + ); + } +} diff --git a/src/Composer/Repository/ArrayRepository.php b/src/Composer/Repository/ArrayRepository.php index ad3dc7d3e..fb7025e9f 100644 --- a/src/Composer/Repository/ArrayRepository.php +++ b/src/Composer/Repository/ArrayRepository.php @@ -23,16 +23,6 @@ class ArrayRepository implements RepositoryInterface { protected $packages; - static public function supports($type, $name = '', $url = '') - { - return 'array' === strtolower($type); - } - - static public function create($type, $name = '', $url = '') - { - return new static(); - } - /** * Adds a new package to the repository * diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 548fe9d06..36f2e0a18 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -12,9 +12,7 @@ namespace Composer\Repository; -use Composer\Package\MemoryPackage; -use Composer\Package\BasePackage; -use Composer\Package\Link; +use Composer\Package\Loader\ArrayLoader; use Composer\Package\LinkConstraint\VersionConstraint; /** @@ -22,20 +20,16 @@ use Composer\Package\LinkConstraint\VersionConstraint; */ class ComposerRepository extends ArrayRepository { + protected $url; protected $packages; - static public function supports($type, $name = '', $url = '') - { - return 'composer' === strtolower($type) && '' !== $url; - } - - static public function create($type, $name = '', $url = '') - { - return new static($url); - } - public function __construct($url) { + $url = rtrim($url, '/'); + if (!filter_var($url, FILTER_VALIDATE_URL)) { + throw new \UnexpectedValueException('Invalid url given for Composer repository: '.$url); + } + $this->url = $url; } @@ -52,60 +46,11 @@ class ComposerRepository extends ArrayRepository } } - protected function createPackages($data) + private function createPackages($data) { foreach ($data['versions'] as $rev) { - $version = BasePackage::parseVersion($rev['version']); - - $package = new MemoryPackage($rev['name'], $version['version'], $version['type']); - $package->setSourceType($rev['source']['type']); - $package->setSourceUrl($rev['source']['url']); - - $package->setDistType($rev['dist']['type']); - $package->setDistUrl($rev['dist']['url']); - $package->setDistSha1Checksum($rev['dist']['shasum']); - - if (isset($rev['type'])) { - $package->setType($rev['type']); - } - - if (isset($rev['extra'])) { - $package->setExtra($rev['extra']); - } - - if (isset($rev['license'])) { - $package->setLicense($rev['license']); - } - - $links = array( - 'require', - 'conflict', - 'provide', - 'replace', - 'recommend', - 'suggest', - ); - foreach ($links as $link) { - if (isset($rev[$link])) { - $method = 'set'.$link.'s'; - $package->{$method}($this->createLinks($rev['name'], $link.'s', $rev[$link])); - } - } - $this->addPackage($package); + $loader = new ArrayLoader(); + $this->addPackage($loader->load($rev)); } } - - protected function createLinks($name, $description, $linkSpecs) - { - $links = array(); - foreach ($linkSpecs as $dep => $ver) { - preg_match('#^([>=<~]*)([\d.]+.*)$#', $ver, $match); - if (!$match[1]) { - $match[1] = '='; - } - $constraint = new VersionConstraint($match[1], $match[2]); - $links[] = new Link($name, $dep, $constraint, $description); - } - return $links; - } } diff --git a/src/Composer/Repository/GitRepository.php b/src/Composer/Repository/GitRepository.php index 61d58cdff..ed382538e 100644 --- a/src/Composer/Repository/GitRepository.php +++ b/src/Composer/Repository/GitRepository.php @@ -24,21 +24,13 @@ use Composer\Package\LinkConstraint\VersionConstraint; */ class GitRepository extends ArrayRepository { - protected $packages; + protected $url; + protected $cacheDir; - static public function supports($type, $name = '', $url = '') - { - return 'git' === strtolower($type) && '' !== $url; - } - - static public function create($type, $name = '', $url = '') - { - return new static($url); - } - - public function __construct($url) + public function __construct($url, $cacheDir) { $this->url = $url; + $this->cacheDir = $cacheDir; } protected function initialize() diff --git a/src/Composer/Repository/PearRepository.php b/src/Composer/Repository/PearRepository.php index 24b8a00e1..928dbe06c 100644 --- a/src/Composer/Repository/PearRepository.php +++ b/src/Composer/Repository/PearRepository.php @@ -23,26 +23,17 @@ use Composer\Package\LinkConstraint\VersionConstraint; */ class PearRepository extends ArrayRepository { - private $name; - private $url; + protected $url; + protected $cacheDir; - static public function supports($type, $name = '', $url = '') - { - return 'pear' === strtolower($type) && '' !== $url; - } - - static public function create($type, $name = '', $url = '') - { - return new static($url, $name); - } - - public function __construct($url, $name = '') + public function __construct($url, $cacheDir) { if (!filter_var($url, FILTER_VALIDATE_URL)) { - throw new \UnexpectedValueException('Invalid url given for PEAR repository "'.$name.'": '.$url); + throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$url); } $this->url = $url; + $this->cacheDir = $cacheDir; } protected function initialize() diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index acc685597..624cc788b 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -20,13 +20,6 @@ use Composer\Package\BasePackage; */ class PlatformRepository extends ArrayRepository { - protected $packages; - - static public function supports($type, $name = '', $url = '') - { - return 'platform' === strtolower($type); - } - protected function initialize() { parent::initialize(); diff --git a/src/Composer/Repository/RepositoryFactory.php b/src/Composer/Repository/RepositoryFactory.php deleted file mode 100644 index b18138f5b..000000000 --- a/src/Composer/Repository/RepositoryFactory.php +++ /dev/null @@ -1,63 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Repository; - -/** - * @author Konstantin Kudryashiv - */ -class RepositoryFactory -{ - private $classes = array(); - - public function __construct(array $classes) - { - foreach ($classes as $class) { - $this->registerRepositoryClass($class); - } - } - - public function registerRepositoryClass($class) - { - $reflection = new \ReflectionClass($class); - - if (!$reflection->implementsInterface('Composer\Repository\RepositoryInterface')) { - throw new \InvalidArgumentException( - 'Repository class should implement "RepositoryInterface", but "'.$class.'"'. - 'given' - ); - } - - $this->classes[] = $class; - } - - public function classWhichSupports($type, $name = '', $url = '') - { - foreach ($this->classes as $class) { - if ($class::supports($type, $name, $url)) { - return $class; - } - } - - throw new \UnexpectedValueException(sprintf( - "Can not find repository class, which supports:\n%s", - json_encode(array($name => array($type => $url))) - )); - } - - public function create($type, $name = '', $url = '') - { - $class = $this->classWhichSupports($type, $name, $url); - - return $class::create($type, $name, $url); - } -} From 6ce013225986c1905bda956b274c598418bfadb6 Mon Sep 17 00:00:00 2001 From: everzet Date: Wed, 21 Sep 2011 00:46:19 +0300 Subject: [PATCH 07/41] fixed installer interface --- src/Composer/Installer/InstallerInterface.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Composer/Installer/InstallerInterface.php b/src/Composer/Installer/InstallerInterface.php index edd9246f4..0a3154946 100644 --- a/src/Composer/Installer/InstallerInterface.php +++ b/src/Composer/Installer/InstallerInterface.php @@ -20,8 +20,6 @@ use Composer\Downloader\DownloaderInterface; */ interface InstallerInterface { - function isInstalled(PackageInterface $package, DownloaderInterface $sourceDownloader = null, - DownloaderInterface $distDownloader = null); - function install(PackageInterface $package, DownloaderInterface $sourceDownloader = null, - DownloaderInterface $distDownloader = null); + function isInstalled(PackageInterface $package); + function install(PackageInterface $package); } From 0cc017a395901fcb201fb43c2a7e6c14444da9a4 Mon Sep 17 00:00:00 2001 From: everzet Date: Wed, 21 Sep 2011 01:22:19 +0300 Subject: [PATCH 08/41] removed extra vars --- bin/composer | 3 --- 1 file changed, 3 deletions(-) diff --git a/bin/composer b/bin/composer index 35194b681..5ccd36118 100755 --- a/bin/composer +++ b/bin/composer @@ -10,9 +10,6 @@ use Composer\Repository; use Composer\Package; use Composer\Console\Application as ComposerApplication; -$composerFolder = '.composer'; -$composerCache = $composerFolder.'/packages'; - // initialize installer $installer = new Installer\LibraryInstaller('vendor', $preferSource = true); $installer->setDownloader('git', new Downloader\GitDownloader()); From 067007656bbdc9102dbe605b2dbd7265602ecf2d Mon Sep 17 00:00:00 2001 From: everzet Date: Sat, 24 Sep 2011 00:23:16 +0300 Subject: [PATCH 09/41] LibraryInstaller refactored and tested --- src/Composer/Installer/InstallerInterface.php | 41 +++- src/Composer/Installer/LibraryInstaller.php | 145 +++++++----- .../Test/Installer/LibraryInstallerTest.php | 207 ++++++++++++++++++ 3 files changed, 341 insertions(+), 52 deletions(-) create mode 100644 tests/Composer/Test/Installer/LibraryInstallerTest.php diff --git a/src/Composer/Installer/InstallerInterface.php b/src/Composer/Installer/InstallerInterface.php index 0a3154946..96af2bc93 100644 --- a/src/Composer/Installer/InstallerInterface.php +++ b/src/Composer/Installer/InstallerInterface.php @@ -12,14 +12,53 @@ namespace Composer\Installer; +use Composer\DependencyResolver\Operation\OperationInterface; use Composer\Package\PackageInterface; -use Composer\Downloader\DownloaderInterface; /** + * Interface for the package installation manager. + * * @author Konstantin Kudryashov */ interface InstallerInterface { + /** + * Executes specific solver operation. + * + * @param OperationInterface $operation solver operation instance + */ + function executeOperation(OperationInterface $operation); + + /** + * Checks that provided package is installed. + * + * @param PackageInterface $package package instance + * + * @return Boolean + */ function isInstalled(PackageInterface $package); + + /** + * Installs specific package. + * + * @param PackageInterface $package package instance + */ function install(PackageInterface $package); + + /** + * Updates specific package. + * + * @param PackageInterface $initial already installed package version + * @param PackageInterface $target updated version + * + * @throws InvalidArgumentException if $from package is not installed + */ + function update(PackageInterface $initial, PackageInterface $target); + + /** + * Uninstalls specific package. + * + * @param PackageInterface $package package instance + */ + function uninstall(PackageInterface $package); } diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 8f55b86eb..6b224fba7 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -12,24 +12,35 @@ namespace Composer\Installer; -use Composer\Downloader\DownloaderInterface; +use Composer\Downloader\DownloadManager; +use Composer\Installer\Registry\RegistryInterface; +use Composer\Installer\Registry\FilesystemRegistry; +use Composer\DependencyResolver\Operation\OperationInterface; use Composer\Package\PackageInterface; -use Composer\Downloader\DownloaderInterface; /** + * Package installation manager. + * * @author Jordi Boggiano * @author Konstantin Kudryashov */ class LibraryInstaller implements InstallerInterface { private $dir; - private $preferSource; - private $downloaders = array(); + private $dm; + private $registry; - public function __construct($dir = 'vendor', $preferSource = false) + /** + * Initializes library installer. + * + * @param string $dir relative path for packages home + * @param DownloadManager $dm download manager + * @param RegistryInterface $registry registry controller + */ + public function __construct($dir, DownloadManager $dm, RegistryInterface $registry = null) { $this->dir = $dir; - $this->preferSource = $preferSource; + $this->dm = $dm; if (!is_dir($this->dir)) { if (file_exists($this->dir)) { @@ -43,67 +54,99 @@ class LibraryInstaller implements InstallerInterface ); } } + + if (null === $registry) { + $registry = new FilesystemRegistry('.composer', str_replace('/', '_', $dir)); + } + + $this->registry = $registry; + $this->registry->open(); } - public function setDownloader($type, DownloaderInterface $downloader = null) + /** + * Closes packages registry. + */ + public function __destruct() { - if (null === $downloader) { - unset($this->downloaders[$type]); - - return; - } - - $this->downloaders[$type] = $downloader; + $this->registry->close(); } - public function getDownloader($type) + /** + * Executes specific solver operation. + * + * @param OperationInterface $operation solver operation instance + */ + public function executeOperation(OperationInterface $operation) { - if (!isset($this->downloaders[$type])) { - throw new \UnexpectedValueException('Unknown source type: '.$type); - } + $method = $operation->getJobType(); - return $this->downloaders[$type]; - } - - public function install(PackageInterface $package) - { - if (!($this->preferSource && $package->getSourceType()) && $package->getDistType()) { - $downloader = $this->getDownloader($package->getDistType()); - - return $downloader->download( - $package, $this->dir, $package->getDistUrl(), $package->getDistSha1Checksum() - ); - } - - if ($package->getSourceType()) { - $downloader = $this->getDownloader($package->getSourceType()); - - return $downloader->download( - $package, $this->dir, $package->getSourceUrl() - ); - } - - throw new \InvalidArgumentException('Package should have dist or source specified'); + if ('update' === $method) { + $this->$method($operation->getPackage(), $operation->getTargetPackage()); + } else { + $this->$method($operation->getPackage()); + } } + /** + * Checks that specific package is installed. + * + * @param PackageInterface $package package instance + * + * @return Boolean + */ public function isInstalled(PackageInterface $package) { - if ($package->getSourceType()) { - $downloader = $this->getDownloader($package->getSourceType()); + return $this->registry->isPackageRegistered($package); + } - if ($downloader->isDownloaded($package, $this->dir)) { - return true; - } + /** + * Installs specific package. + * + * @param PackageInterface $package package instance + * + * @throws InvalidArgumentException if provided package have no urls to download from + */ + public function install(PackageInterface $package) + { + $type = $this->dm->download($package, $this->dir); + $this->registry->registerPackage($package, $type); + } + + /** + * Updates specific package. + * + * @param PackageInterface $initial already installed package version + * @param PackageInterface $target updated version + * + * @throws InvalidArgumentException if $from package is not installed + */ + public function update(PackageInterface $initial, PackageInterface $target) + { + if (!$this->registry->isPackageRegistered($initial)) { + throw new \UnexpectedValueException('Package is not installed: '.$initial); } - if ($package->getDistType()) { - $downloader = $this->getDownloader($package->getDistType()); + $type = $this->registry->getRegisteredPackageInstallerType($initial); + $this->dm->update($initial, $target, $this->dir, $type); + $this->registry->unregisterPackage($initial); + $this->registry->registerPackage($target, $type); + } - if ($downloader->isDownloaded($package, $this->dir)) { - return true; - } + /** + * Uninstalls specific package. + * + * @param PackageInterface $package package instance + * + * @throws InvalidArgumentException if package is not installed + */ + public function uninstall(PackageInterface $package) + { + if (!$this->registry->isPackageRegistered($package)) { + throw new \UnexpectedValueException('Package is not installed: '.$package); } - return false; + $type = $this->registry->getRegisteredPackageInstallerType($package); + $this->dm->remove($package, $this->dir, $type); + $this->registry->unregisterPackage($package); } } diff --git a/tests/Composer/Test/Installer/LibraryInstallerTest.php b/tests/Composer/Test/Installer/LibraryInstallerTest.php new file mode 100644 index 000000000..61ffc8d41 --- /dev/null +++ b/tests/Composer/Test/Installer/LibraryInstallerTest.php @@ -0,0 +1,207 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Installer; + +use Composer\Installer\LibraryInstaller; +use Composer\DependencyResolver\Operation; + +class LibraryInstallerTest extends \PHPUnit_Framework_TestCase +{ + private $dir; + private $dm; + private $registry; + private $library; + + protected function setUp() + { + $this->dir = sys_get_temp_dir().'/composer'; + if (is_dir($this->dir)) { + rmdir($this->dir); + } + + $this->dm = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->disableOriginalConstructor() + ->getMock(); + + $this->registry = $this->getMockBuilder('Composer\Installer\Registry\RegistryInterface') + ->disableOriginalConstructor() + ->getMock(); + } + + public function testInstallerCreation() + { + $this->registry + ->expects($this->once()) + ->method('open'); + + $this->registry + ->expects($this->once()) + ->method('close'); + + $library = new LibraryInstaller($this->dir, $this->dm, $this->registry); + $this->assertTrue(is_dir($this->dir)); + + $file = sys_get_temp_dir().'/file'; + touch($file); + + $this->setExpectedException('UnexpectedValueException'); + $library = new LibraryInstaller($file, $this->dm, $this->registry); + } + + public function testExecuteOperation() + { + $library = $this->getMockBuilder('Composer\Installer\LibraryInstaller') + ->setConstructorArgs(array($this->dir, $this->dm, $this->registry)) + ->setMethods(array('install', 'update', 'uninstall')) + ->getMock(); + + $packageToInstall = $this->createPackageMock(); + $packageToRemove = $this->createPackageMock(); + $packageToUpdate = $this->createPackageMock(); + $updatedPackage = $this->createPackageMock(); + + $library + ->expects($this->once()) + ->method('install') + ->with($packageToInstall); + + $library + ->expects($this->once()) + ->method('uninstall') + ->with($packageToRemove); + + $library + ->expects($this->once()) + ->method('update') + ->with($packageToUpdate, $updatedPackage); + + $library->executeOperation(new Operation\InstallOperation($packageToInstall)); + $library->executeOperation(new Operation\UninstallOperation($packageToRemove)); + $library->executeOperation(new Operation\UpdateOperation($packageToUpdate, $updatedPackage)); + } + + public function testIsInstalled() + { + $library = new LibraryInstaller($this->dir, $this->dm, $this->registry); + $package = $this->createPackageMock(); + + $this->registry + ->expects($this->exactly(2)) + ->method('isPackageRegistered') + ->with($package) + ->will($this->onConsecutiveCalls(true, false)); + + $this->assertTrue($library->isInstalled($package)); + $this->assertFalse($library->isInstalled($package)); + } + + public function testInstall() + { + $library = new LibraryInstaller($this->dir, $this->dm, $this->registry); + $package = $this->createPackageMock(); + + $this->dm + ->expects($this->once()) + ->method('download') + ->with($package, $this->dir) + ->will($this->returnValue('source')); + + $this->registry + ->expects($this->once()) + ->method('registerPackage') + ->with($package, 'source'); + + $library->install($package); + } + + public function testUpdate() + { + $library = new LibraryInstaller($this->dir, $this->dm, $this->registry); + $initial = $this->createPackageMock(); + $target = $this->createPackageMock(); + + $this->registry + ->expects($this->exactly(2)) + ->method('isPackageRegistered') + ->with($initial) + ->will($this->onConsecutiveCalls(true, false)); + + $this->registry + ->expects($this->once()) + ->method('getRegisteredPackageInstallerType') + ->with($initial) + ->will($this->returnValue('dist')); + + $this->dm + ->expects($this->once()) + ->method('update') + ->with($initial, $target, $this->dir, 'dist'); + + $this->registry + ->expects($this->once()) + ->method('unregisterPackage') + ->with($initial); + + $this->registry + ->expects($this->once()) + ->method('registerPackage') + ->with($target, 'dist'); + + $library->update($initial, $target); + + $this->setExpectedException('UnexpectedValueException'); + + $library->update($initial, $target); + } + + public function testUninstall() + { + $library = new LibraryInstaller($this->dir, $this->dm, $this->registry); + $package = $this->createPackageMock(); + + $this->registry + ->expects($this->exactly(2)) + ->method('isPackageRegistered') + ->with($package) + ->will($this->onConsecutiveCalls(true, false)); + + $this->registry + ->expects($this->once()) + ->method('getRegisteredPackageInstallerType') + ->with($package) + ->will($this->returnValue('source')); + + $this->dm + ->expects($this->once()) + ->method('remove') + ->with($package, $this->dir, 'source'); + + $this->registry + ->expects($this->once()) + ->method('unregisterPackage') + ->with($package); + + $library->uninstall($package); + + $this->setExpectedException('UnexpectedValueException'); + + $library->uninstall($package); + } + + private function createPackageMock() + { + return $this->getMockBuilder('Composer\Package\MemoryPackage') + ->setConstructorArgs(array(md5(rand()), '1.0.0')) + ->getMock(); + } +} From 45cab9fe8c8c16121f74a077864417ba538b922a Mon Sep 17 00:00:00 2001 From: everzet Date: Sat, 24 Sep 2011 00:24:19 +0300 Subject: [PATCH 10/41] Installers FilesystemRegistry implemented --- .../Installer/Registry/FilesystemRegistry.php | 126 ++++++++++++++ .../Installer/Registry/RegistryInterface.php | 67 ++++++++ .../Registry/FilesystemRegistryTests.php | 156 ++++++++++++++++++ 3 files changed, 349 insertions(+) create mode 100644 src/Composer/Installer/Registry/FilesystemRegistry.php create mode 100644 src/Composer/Installer/Registry/RegistryInterface.php create mode 100644 tests/Composer/Test/Installer/Registry/FilesystemRegistryTests.php diff --git a/src/Composer/Installer/Registry/FilesystemRegistry.php b/src/Composer/Installer/Registry/FilesystemRegistry.php new file mode 100644 index 000000000..755e034b2 --- /dev/null +++ b/src/Composer/Installer/Registry/FilesystemRegistry.php @@ -0,0 +1,126 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Installer\Registry; + +use Composer\Package\PackageInterface; + +/** + * Filesystem registry. + * + * @author Konstantin Kudryashov + */ +class FilesystemRegistry implements RegistryInterface +{ + private $registryFile; + private $registry = array(); + + /** + * Initializes filesystem registry. + * + * @param string $group registry (installer) group + */ + public function __construct($composerCachePath, $group) + { + $this->registryFile = rtrim($composerCachePath, '/').'/'.$group.'-reg.json'; + $registryPath = dirname($this->registryFile); + + if (!is_dir($registryPath)) { + if (file_exists($registryPath)) { + throw new \UnexpectedValueException( + $registryPath.' exists and is not a directory.' + ); + } + if (!mkdir($registryPath, 0777, true)) { + throw new \UnexpectedValueException( + $registryPath.' does not exist and could not be created.' + ); + } + } + } + + /** + * Opens registry (read file / opens connection). + */ + public function open() + { + if (is_file($this->registryFile)) { + $this->registry = json_decode(file_get_contents($this->registryFile), true); + } + } + + /** + * Closes registry (writes file / closes connection). + */ + public function close() + { + file_put_contents($this->registryFile, json_encode($this->registry)); + } + + /** + * Checks if specified package registered (installed). + * + * @param PackageInterface $package package instance + * + * @return Boolean + */ + public function isPackageRegistered(PackageInterface $package) + { + $packageId = $package->getUniqueName(); + + return isset($this->registry[$packageId]); + } + + /** + * Returns installer type for the registered package. + * + * @param PackageInterface $package package instance + * + * @return string + */ + public function getRegisteredPackageInstallerType(PackageInterface $package) + { + $packageId = $package->getUniqueName(); + + if (isset($this->registry[$packageId])) { + return $this->registry[$packageId]; + } + } + + /** + * Registers package in registry. + * + * @param PackageInterface $package package instance + * @param string $type installer type with which package were been + * installed + */ + public function registerPackage(PackageInterface $package, $type) + { + $packageId = $package->getUniqueName(); + + $this->registry[$packageId] = $type; + } + + /** + * Removes package from registry. + * + * @param PackageInterface $package package instance + */ + public function unregisterPackage(PackageInterface $package) + { + $packageId = $package->getUniqueName(); + + if (isset($this->registry[$packageId])) { + unset($this->registry[$packageId]); + } + } +} diff --git a/src/Composer/Installer/Registry/RegistryInterface.php b/src/Composer/Installer/Registry/RegistryInterface.php new file mode 100644 index 000000000..7fdf63f8a --- /dev/null +++ b/src/Composer/Installer/Registry/RegistryInterface.php @@ -0,0 +1,67 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Installer\Registry; + +use Composer\Package\PackageInterface; + +/** + * Installer registry interface. + * + * @author Konstantin Kudryashov + */ +interface RegistryInterface +{ + /** + * Opens registry (read file / opens connection). + */ + function open(); + + /** + * Closes registry (writes file / closes connection). + */ + function close(); + + /** + * Checks if specified package registered (installed). + * + * @param PackageInterface $package package instance + * + * @return Boolean + */ + function isPackageRegistered(PackageInterface $package); + + /** + * Returns installer type for the registered package. + * + * @param PackageInterface $package package instance + * + * @return string + */ + function getRegisteredPackageInstallerType(PackageInterface $package); + + /** + * Registers package in registry. + * + * @param PackageInterface $package package instance + * @param string $type installer type with which package were been + * installed + */ + function registerPackage(PackageInterface $package, $type); + + /** + * Removes package from registry. + * + * @param PackageInterface $package package instance + */ + function unregisterPackage(PackageInterface $package); +} diff --git a/tests/Composer/Test/Installer/Registry/FilesystemRegistryTests.php b/tests/Composer/Test/Installer/Registry/FilesystemRegistryTests.php new file mode 100644 index 000000000..d3aaf7fc6 --- /dev/null +++ b/tests/Composer/Test/Installer/Registry/FilesystemRegistryTests.php @@ -0,0 +1,156 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Installer\Registry; + +use Composer\Installer\Registry\FilesystemRegistry; + +class FilesystemRegistryTest extends \PHPUnit_Framework_TestCase +{ + private $dir; + private $registryFile; + + protected function setUp() + { + $this->dir = sys_get_temp_dir().'/.composer'; + $this->registryFile = $this->dir.'/some_registry-reg.json'; + + if (file_exists($this->registryFile)) { + unlink($this->registryFile); + } + } + + public function testRegistryCreation() + { + $this->assertFileNotExists($this->registryFile); + $registry = new FilesystemRegistry($this->dir, 'some_registry'); + + $registry->open(); + $registry->close(); + $this->assertFileExists($this->registryFile); + + file_put_contents($this->registryFile, json_encode(array( + 'package1-1.0.0-beta' => 'library' + ))); + + $registry->open(); + $registry->close(); + $this->assertFileExists($this->registryFile); + + $data = json_decode(file_get_contents($this->registryFile), true); + $this->assertEquals(array('package1-1.0.0-beta' => 'library'), $data); + } + + public function testIsPackageRegistered() + { + file_put_contents($this->registryFile, json_encode(array( + 'package1-1.0.0-beta' => 'library' + ))); + + $registry = new FilesystemRegistry($this->dir, 'some_registry'); + $registry->open(); + + $package1 = $this->createPackageMock(); + $package1 + ->expects($this->once()) + ->method('getUniqueName') + ->will($this->returnValue('package1-1.0.0-beta')); + $package2 = $this->createPackageMock(); + $package2 + ->expects($this->once()) + ->method('getUniqueName') + ->will($this->returnValue('package2-1.1.0-stable')); + + $this->assertTrue($registry->isPackageRegistered($package1)); + $this->assertFalse($registry->isPackageRegistered($package2)); + + $registry->close(); + } + + public function testGetRegisteredPackageInstallerType() + { + $package1 = $this->createPackageMock(); + $package1 + ->expects($this->once()) + ->method('getUniqueName') + ->will($this->returnValue('package1-1.0.0-beta')); + $package2 = $this->createPackageMock(); + $package2 + ->expects($this->once()) + ->method('getUniqueName') + ->will($this->returnValue('package2-1.1.0-stable')); + + file_put_contents($this->registryFile, json_encode(array( + 'package1-1.0.0-beta' => 'library', + 'package2-1.1.0-stable' => 'bundle' + ))); + + $registry = new FilesystemRegistry($this->dir, 'some_registry'); + $registry->open(); + + $this->assertSame('library', $registry->getRegisteredPackageInstallerType($package1)); + $this->assertSame('bundle', $registry->getRegisteredPackageInstallerType($package2)); + + $registry->close(); + } + + public function testRegisterPackage() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getUniqueName') + ->will($this->returnValue('package1-1.0.0-beta')); + + $registry = new FilesystemRegistry($this->dir, 'some_registry'); + $registry->open(); + + $registry->registerPackage($package, 'library'); + + $registry->close(); + + $data = json_decode(file_get_contents($this->registryFile), true); + + $this->assertEquals(array('package1-1.0.0-beta' => 'library'), $data); + } + + public function testUnregisterPackage() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getUniqueName') + ->will($this->returnValue('package1-1.0.0-beta')); + + file_put_contents($this->registryFile, json_encode(array( + 'package1-1.0.0-beta' => 'library', + 'package2-1.1.0-stable' => 'bundle' + ))); + + $registry = new FilesystemRegistry($this->dir, 'some_registry'); + $registry->open(); + + $registry->unregisterPackage($package); + + $registry->close(); + + $data = json_decode(file_get_contents($this->registryFile), true); + + $this->assertEquals(array('package2-1.1.0-stable' => 'bundle'), $data); + } + + private function createPackageMock() + { + return $this->getMockBuilder('Composer\Package\PackageInterface') + ->getMock(); + } +} From 0a2f4e7e3237a92b8e8353bf12a8e2073f457060 Mon Sep 17 00:00:00 2001 From: everzet Date: Sat, 24 Sep 2011 01:30:17 +0300 Subject: [PATCH 11/41] Installer now provides full download path to the downloader --- src/Composer/Installer/LibraryInstaller.php | 6 +++--- .../Test/Installer/LibraryInstallerTest.php | 21 ++++++++++++++++--- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 6b224fba7..f8b1313e9 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -108,7 +108,7 @@ class LibraryInstaller implements InstallerInterface */ public function install(PackageInterface $package) { - $type = $this->dm->download($package, $this->dir); + $type = $this->dm->download($package, $this->dir.'/'.$package->getName()); $this->registry->registerPackage($package, $type); } @@ -127,7 +127,7 @@ class LibraryInstaller implements InstallerInterface } $type = $this->registry->getRegisteredPackageInstallerType($initial); - $this->dm->update($initial, $target, $this->dir, $type); + $this->dm->update($initial, $target, $this->dir.'/'.$initial->getName(), $type); $this->registry->unregisterPackage($initial); $this->registry->registerPackage($target, $type); } @@ -146,7 +146,7 @@ class LibraryInstaller implements InstallerInterface } $type = $this->registry->getRegisteredPackageInstallerType($package); - $this->dm->remove($package, $this->dir, $type); + $this->dm->remove($package, $this->dir.'/'.$package->getName(), $type); $this->registry->unregisterPackage($package); } } diff --git a/tests/Composer/Test/Installer/LibraryInstallerTest.php b/tests/Composer/Test/Installer/LibraryInstallerTest.php index 61ffc8d41..93f3414f8 100644 --- a/tests/Composer/Test/Installer/LibraryInstallerTest.php +++ b/tests/Composer/Test/Installer/LibraryInstallerTest.php @@ -110,10 +110,15 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase $library = new LibraryInstaller($this->dir, $this->dm, $this->registry); $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue('some/package')); + $this->dm ->expects($this->once()) ->method('download') - ->with($package, $this->dir) + ->with($package, $this->dir.'/some/package') ->will($this->returnValue('source')); $this->registry @@ -130,6 +135,11 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase $initial = $this->createPackageMock(); $target = $this->createPackageMock(); + $initial + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue('package1')); + $this->registry ->expects($this->exactly(2)) ->method('isPackageRegistered') @@ -145,7 +155,7 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase $this->dm ->expects($this->once()) ->method('update') - ->with($initial, $target, $this->dir, 'dist'); + ->with($initial, $target, $this->dir.'/package1', 'dist'); $this->registry ->expects($this->once()) @@ -169,6 +179,11 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase $library = new LibraryInstaller($this->dir, $this->dm, $this->registry); $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue('pkg')); + $this->registry ->expects($this->exactly(2)) ->method('isPackageRegistered') @@ -184,7 +199,7 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase $this->dm ->expects($this->once()) ->method('remove') - ->with($package, $this->dir, 'source'); + ->with($package, $this->dir.'/pkg', 'source'); $this->registry ->expects($this->once()) From cde9531e4d49ab18dcda952c1413335a209df6ee Mon Sep 17 00:00:00 2001 From: everzet Date: Sat, 24 Sep 2011 02:09:51 +0300 Subject: [PATCH 12/41] DownloadManager implemented --- src/Composer/Downloader/DownloadManager.php | 153 ++++++++ .../Test/Downloader/DownloadManagerTest.php | 352 ++++++++++++++++++ 2 files changed, 505 insertions(+) create mode 100644 src/Composer/Downloader/DownloadManager.php create mode 100644 tests/Composer/Test/Downloader/DownloadManagerTest.php diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php new file mode 100644 index 000000000..5b15f54cf --- /dev/null +++ b/src/Composer/Downloader/DownloadManager.php @@ -0,0 +1,153 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Downloader; + +use Composer\Package\PackageInterface; +use Composer\Downloader\DownloaderInterface; + +/** + * Downloaders manager. + * + * @author Konstantin Kudryashov + */ +class DownloadManager +{ + private $preferSource = false; + private $downloaders = array(); + + /** + * Initializes download manager. + * + * @param Boolean $preferSource prefer downloading from source + */ + public function __construct($preferSource = false) + { + $this->preferSource = $preferSource; + } + + /** + * Makes downloader prefer source installation over the dist. + * + * @param Boolean $preferSource prefer downloading from source + */ + public function preferSource($preferSource = true) + { + $this->preferSource = $preferSource; + } + + /** + * Sets installer downloader for a specific installation type. + * + * @param string $type installation type + * @param DownloaderInterface $downloader downloader instance + */ + public function setDownloader($type, DownloaderInterface $downloader) + { + $this->downloaders[$type] = $downloader; + } + + /** + * Returns downloader for a specific installation type. + * + * @param string $type installation type + * + * @return DownloaderInterface + * + * @throws UnexpectedValueException if downloader for provided type is not registeterd + */ + public function getDownloader($type) + { + if (!isset($this->downloaders[$type])) { + throw new \UnexpectedValueException('Unknown source type: '.$type); + } + + return $this->downloaders[$type]; + } + + /** + * Downloads package into target dir. + * + * @param PackageInterface $package package instance + * @param string $targetDir target dir + * + * @return string downloader type (source/dist) + * + * @throws InvalidArgumentException if package have no urls to download from + */ + public function download(PackageInterface $package, $targetDir) + { + $sourceType = $package->getSourceType(); + $distType = $package->getDistType(); + + if (!($this->preferSource && $sourceType) && $distType) { + $downloader = $this->getDownloader($distType); + $downloader->download( + $package, $targetDir, $package->getDistUrl(), $package->getDistSha1Checksum() + ); + + return 'dist'; + } + + if ($sourceType) { + $downloader = $this->getDownloader($sourceType); + $downloader->download($package, $targetDir, $package->getSourceUrl()); + + return 'source'; + } + + throw new \InvalidArgumentException('Package should have dist or source specified'); + } + + /** + * Updates package from initial to target version. + * + * @param PackageInterface $initial initial package version + * @param PackageInterface $target target package version + * @param string $targetDir target dir + * @param string $type downloader type (source/dist) + * + * @throws InvalidArgumentException if initial package is not installed + */ + public function update(PackageInterface $initial, PackageInterface $target, $targetDir, $type) + { + if ('dist' === $type) { + $downloader = $this->getDownloader($initial->getDistType()); + $downloader->update($initial, $target, $targetDir); + } elseif ('source' === $type) { + $downloader = $this->getDownloader($initial->getSourceType()); + $downloader->update($initial, $target, $targetDir); + } else { + throw new \InvalidArgumentException('Package should have dist or source specified'); + } + } + + /** + * Removes package from target dir. + * + * @param PackageInterface $package package instance + * @param string $targetDir target dir + * @param string $type downloader type (source/dist) + */ + public function remove(PackageInterface $package, $targetDir, $type) + { + if ('dist' === $type) { + $downloader = $this->getDownloader($package->getDistType()); + $downloader->remove($package, $targetDir); + } elseif ('source' === $type) { + $downloader = $this->getDownloader($package->getSourceType()); + $downloader->remove($package, $targetDir); + } else { + throw new \InvalidArgumentException('Package should have dist or source specified'); + } + } +} diff --git a/tests/Composer/Test/Downloader/DownloadManagerTest.php b/tests/Composer/Test/Downloader/DownloadManagerTest.php new file mode 100644 index 000000000..efac5f0ba --- /dev/null +++ b/tests/Composer/Test/Downloader/DownloadManagerTest.php @@ -0,0 +1,352 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Downloader; + +use Composer\Downloader\DownloadManager; + +class DownloadManagerTest extends \PHPUnit_Framework_TestCase +{ + public function testSetGetDownloader() + { + $downloader = $this->createDownloaderMock(); + $manager = new DownloadManager(); + + $manager->setDownloader('test', $downloader); + $this->assertSame($downloader, $manager->getDownloader('test')); + + $this->setExpectedException('UnexpectedValueException'); + $manager->getDownloader('unregistered'); + } + + public function testFullPackageDownload() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + + $package + ->expects($this->once()) + ->method('getDistUrl') + ->will($this->returnValue('dist_url')); + $package + ->expects($this->once()) + ->method('getDistSha1Checksum') + ->will($this->returnValue('sha1')); + + $pearDownloader = $this->createDownloaderMock(); + $pearDownloader + ->expects($this->once()) + ->method('download') + ->with($package, 'target_dir', 'dist_url', 'sha1'); + + $manager = new DownloadManager(); + $manager->setDownloader('pear', $pearDownloader); + + $manager->download($package, 'target_dir'); + } + + public function testBadPackageDownload() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue(null)); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue(null)); + + $manager = new DownloadManager(); + + $this->setExpectedException('InvalidArgumentException'); + $manager->download($package, 'target_dir'); + } + + public function testDistOnlyPackageDownload() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue(null)); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + + $package + ->expects($this->once()) + ->method('getDistUrl') + ->will($this->returnValue('dist_url')); + $package + ->expects($this->once()) + ->method('getDistSha1Checksum') + ->will($this->returnValue('sha1')); + + $pearDownloader = $this->createDownloaderMock(); + $pearDownloader + ->expects($this->once()) + ->method('download') + ->with($package, 'target_dir', 'dist_url', 'sha1'); + + $manager = new DownloadManager(); + $manager->setDownloader('pear', $pearDownloader); + + $manager->download($package, 'target_dir'); + } + + public function testSourceOnlyPackageDownload() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue(null)); + + $package + ->expects($this->once()) + ->method('getSourceUrl') + ->will($this->returnValue('source_url')); + + $gitDownloader = $this->createDownloaderMock(); + $gitDownloader + ->expects($this->once()) + ->method('download') + ->with($package, 'vendor/pkg', 'source_url'); + + $manager = new DownloadManager(); + $manager->setDownloader('git', $gitDownloader); + + $manager->download($package, 'vendor/pkg'); + } + + public function testFullPackageDownloadWithSourcePreferred() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + + $package + ->expects($this->once()) + ->method('getSourceUrl') + ->will($this->returnValue('source_url')); + + $gitDownloader = $this->createDownloaderMock(); + $gitDownloader + ->expects($this->once()) + ->method('download') + ->with($package, 'vendor/pkg', 'source_url'); + + $manager = new DownloadManager(); + $manager->setDownloader('git', $gitDownloader); + $manager->preferSource(); + + $manager->download($package, 'vendor/pkg'); + } + + public function testDistOnlyPackageDownloadWithSourcePreferred() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue(null)); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + + $package + ->expects($this->once()) + ->method('getDistUrl') + ->will($this->returnValue('dist_url')); + $package + ->expects($this->once()) + ->method('getDistSha1Checksum') + ->will($this->returnValue('sha1')); + + $pearDownloader = $this->createDownloaderMock(); + $pearDownloader + ->expects($this->once()) + ->method('download') + ->with($package, 'target_dir', 'dist_url', 'sha1'); + + $manager = new DownloadManager(); + $manager->setDownloader('pear', $pearDownloader); + $manager->preferSource(); + + $manager->download($package, 'target_dir'); + } + + public function testSourceOnlyPackageDownloadWithSourcePreferred() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue(null)); + + $package + ->expects($this->once()) + ->method('getSourceUrl') + ->will($this->returnValue('source_url')); + + $gitDownloader = $this->createDownloaderMock(); + $gitDownloader + ->expects($this->once()) + ->method('download') + ->with($package, 'vendor/pkg', 'source_url'); + + $manager = new DownloadManager(); + $manager->setDownloader('git', $gitDownloader); + $manager->preferSource(); + + $manager->download($package, 'vendor/pkg'); + } + + public function testBadPackageDownloadWithSourcePreferred() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue(null)); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue(null)); + + $manager = new DownloadManager(); + $manager->preferSource(); + + $this->setExpectedException('InvalidArgumentException'); + $manager->download($package, 'target_dir'); + } + + public function testUpdateDist() + { + $initial = $this->createPackageMock(); + $initial + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + + $target = $this->createPackageMock(); + + $pearDownloader = $this->createDownloaderMock(); + $pearDownloader + ->expects($this->once()) + ->method('update') + ->with($initial, $target, 'vendor/bundles/FOS/UserBundle'); + + $manager = new DownloadManager(); + $manager->setDownloader('pear', $pearDownloader); + + $manager->update($initial, $target, 'vendor/bundles/FOS/UserBundle', 'dist'); + } + + public function testUpdateSource() + { + $initial = $this->createPackageMock(); + $initial + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('svn')); + + $target = $this->createPackageMock(); + + $svnDownloader = $this->createDownloaderMock(); + $svnDownloader + ->expects($this->once()) + ->method('update') + ->with($initial, $target, 'vendor/pkg'); + + $manager = new DownloadManager(); + $manager->setDownloader('svn', $svnDownloader); + + $manager->update($initial, $target, 'vendor/pkg', 'source'); + } + + public function testRemoveDist() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + + $pearDownloader = $this->createDownloaderMock(); + $pearDownloader + ->expects($this->once()) + ->method('remove') + ->with($package, 'vendor/bundles/FOS/UserBundle'); + + $manager = new DownloadManager(); + $manager->setDownloader('pear', $pearDownloader); + + $manager->remove($package, 'vendor/bundles/FOS/UserBundle', 'dist'); + } + + public function testRemoveSource() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('svn')); + + $svnDownloader = $this->createDownloaderMock(); + $svnDownloader + ->expects($this->once()) + ->method('remove') + ->with($package, 'vendor/pkg'); + + $manager = new DownloadManager(); + $manager->setDownloader('svn', $svnDownloader); + + $manager->remove($package, 'vendor/pkg', 'source'); + } + + private function createDownloaderMock() + { + return $this->getMockBuilder('Composer\Downloader\DownloaderInterface') + ->getMock(); + } + + private function createPackageMock() + { + return $this->getMockBuilder('Composer\Package\PackageInterface') + ->getMock(); + } +} From 69f1de99861e1ee336a7cd08b22a79166bbd7e8e Mon Sep 17 00:00:00 2001 From: everzet Date: Sat, 24 Sep 2011 02:11:32 +0300 Subject: [PATCH 13/41] Enhanced PackageInterface and BasePackage with getUniqueName() method, which provides name-version-releaseType string (used in installer registry) --- src/Composer/Package/BasePackage.php | 12 +++++++++++- src/Composer/Package/PackageInterface.php | 7 +++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index ddeaf65b3..1caaf3ba8 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -134,6 +134,16 @@ abstract class BasePackage implements PackageInterface $this->repository = $repository; } + /** + * Returns package unique name, constructed from name, version and release type. + * + * @return string + */ + public function getUniqueName() + { + return $this->getName().'-'.$this->getVersion().'-'.$this->getReleaseType(); + } + /** * Converts the package into a readable and unique string * @@ -141,6 +151,6 @@ abstract class BasePackage implements PackageInterface */ public function __toString() { - return $this->getName().'-'.$this->getVersion().'-'.$this->getReleaseType(); + return $this->getUniqueName(); } } diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index 3fdefc78d..47b81814c 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -200,6 +200,13 @@ interface PackageInterface */ function getRepository(); + /** + * Returns package unique name, constructed from name, version and release type. + * + * @return string + */ + function getUniqueName(); + /** * Converts the package into a readable and unique string * From 5b0d17cc1304c9f1b9505d66ee8c167b0301c4be Mon Sep 17 00:00:00 2001 From: everzet Date: Sat, 24 Sep 2011 02:18:41 +0300 Subject: [PATCH 14/41] DownloaderInterface refactored --- .../Downloader/DownloaderInterface.php | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/Composer/Downloader/DownloaderInterface.php b/src/Composer/Downloader/DownloaderInterface.php index 4416ac46e..7a114ea2b 100644 --- a/src/Composer/Downloader/DownloaderInterface.php +++ b/src/Composer/Downloader/DownloaderInterface.php @@ -15,10 +15,36 @@ namespace Composer\Downloader; use Composer\Package\PackageInterface; /** + * Downloader interface. + * * @author Konstantin Kudryashov */ interface DownloaderInterface { + /** + * Downloads specific package into specific folder. + * + * @param PackageInterface $package package instance + * @param string $path download path + * @param string $url download url + * @param string $checksum package checksum (for dists) + */ function download(PackageInterface $package, $path, $url, $checksum = null); - function isDownloaded(PackageInterface $package, $path); + + /** + * Updates specific package in specific folder from initial to target version. + * + * @param PackageInterface $initial initial package + * @param PackageInterface $target updated package + * @param string $path download path + */ + function update(PackageInterface $initial, PackageInterface $target, $path); + + /** + * Removes specific package from specific folder. + * + * @param PackageInterface $package package instance + * @param string $path download path + */ + function remove(PackageInterface $package, $path); } From 0a1e7320b0422b34a17e7d606faa90098b590e0d Mon Sep 17 00:00:00 2001 From: everzet Date: Sat, 24 Sep 2011 02:29:22 +0300 Subject: [PATCH 15/41] Refactored solver result array. Now it returns array of operation objects which are much nicer to work with --- .../Operation/InstallOperation.php | 33 +++++++++ .../Operation/OperationInterface.php | 51 ++++++++++++++ .../Operation/SolverOperation.php | 68 +++++++++++++++++++ .../Operation/UninstallOperation.php | 33 +++++++++ .../Operation/UpdateOperation.php | 59 ++++++++++++++++ src/Composer/DependencyResolver/Solver.php | 22 +++--- src/Composer/Installer/Operation.php | 57 ---------------- .../Test/DependencyResolver/SolverTest.php | 17 +++-- 8 files changed, 265 insertions(+), 75 deletions(-) create mode 100644 src/Composer/DependencyResolver/Operation/InstallOperation.php create mode 100644 src/Composer/DependencyResolver/Operation/OperationInterface.php create mode 100644 src/Composer/DependencyResolver/Operation/SolverOperation.php create mode 100644 src/Composer/DependencyResolver/Operation/UninstallOperation.php create mode 100644 src/Composer/DependencyResolver/Operation/UpdateOperation.php delete mode 100644 src/Composer/Installer/Operation.php diff --git a/src/Composer/DependencyResolver/Operation/InstallOperation.php b/src/Composer/DependencyResolver/Operation/InstallOperation.php new file mode 100644 index 000000000..b8383b121 --- /dev/null +++ b/src/Composer/DependencyResolver/Operation/InstallOperation.php @@ -0,0 +1,33 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\DependencyResolver\Operation; + +use Composer\Package\PackageInterface; + +/** + * Solver install operation. + * + * @author Konstantin Kudryashov + */ +class InstallOperation extends SolverOperation +{ + /** + * Returns job type. + * + * @return string + */ + public function getJobType() + { + return 'install'; + } +} diff --git a/src/Composer/DependencyResolver/Operation/OperationInterface.php b/src/Composer/DependencyResolver/Operation/OperationInterface.php new file mode 100644 index 000000000..fcfc6c201 --- /dev/null +++ b/src/Composer/DependencyResolver/Operation/OperationInterface.php @@ -0,0 +1,51 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\DependencyResolver\Operation; + +use Composer\Package\PackageInterface; + +/** + * Solver operation interface. + * + * @author Konstantin Kudryashov + */ +interface OperationInterface +{ + /** + * Returns job type. + * + * @return string + */ + function getJobType(); + + /** + * Returns package instance. + * + * @return PackageInterface + */ + function getPackage(); + + /** + * Returns package type. + * + * @return string + */ + function getPackageType(); + + /** + * Returns operation reason. + * + * @return string + */ + function getReason(); +} diff --git a/src/Composer/DependencyResolver/Operation/SolverOperation.php b/src/Composer/DependencyResolver/Operation/SolverOperation.php new file mode 100644 index 000000000..0cca6a6da --- /dev/null +++ b/src/Composer/DependencyResolver/Operation/SolverOperation.php @@ -0,0 +1,68 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\DependencyResolver\Operation; + +use Composer\Package\PackageInterface; + +/** + * Abstract solver operation class. + * + * @author Konstantin Kudryashov + */ +abstract class SolverOperation implements OperationInterface +{ + protected $package; + protected $reason; + + /** + * Initializes operation. + * + * @param PackageInterface $package package instance + * @param string $reason operation reason + */ + public function __construct(PackageInterface $package, $reason = null) + { + $this->package = $package; + $this->reason = $reason; + } + + /** + * Returns package instance. + * + * @return PackageInterface + */ + public function getPackage() + { + return $this->package; + } + + /** + * Returns package type. + * + * @return string + */ + public function getPackageType() + { + return $this->package->getType(); + } + + /** + * Returns operation reason. + * + * @return string + */ + public function getReason() + { + return $this->reason; + } +} diff --git a/src/Composer/DependencyResolver/Operation/UninstallOperation.php b/src/Composer/DependencyResolver/Operation/UninstallOperation.php new file mode 100644 index 000000000..f6c853079 --- /dev/null +++ b/src/Composer/DependencyResolver/Operation/UninstallOperation.php @@ -0,0 +1,33 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\DependencyResolver\Operation; + +use Composer\Package\PackageInterface; + +/** + * Solver uninstall operation. + * + * @author Konstantin Kudryashov + */ +class UninstallOperation extends SolverOperation +{ + /** + * Returns job type. + * + * @return string + */ + public function getJobType() + { + return 'uninstall'; + } +} diff --git a/src/Composer/DependencyResolver/Operation/UpdateOperation.php b/src/Composer/DependencyResolver/Operation/UpdateOperation.php new file mode 100644 index 000000000..1ea400d07 --- /dev/null +++ b/src/Composer/DependencyResolver/Operation/UpdateOperation.php @@ -0,0 +1,59 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\DependencyResolver\Operation; + +use Composer\Package\PackageInterface; + +/** + * Solver update operation. + * + * @author Konstantin Kudryashov + */ +class UpdateOperation extends SolverOperation +{ + protected $targetPackage; + + /** + * Initializes update operation. + * + * @param PackageInterface $initial initial package + * @param PackageInterface $target target package (updated) + * @param string $reason update reason + */ + public function __construct(PackageInterface $initial, PackageInterface $target, $reason = null) + { + parent::__construct($initial, $reason); + + $this->targetPackage = $target; + } + + /** + * Returns job type. + * + * @return string + */ + public function getJobType() + { + return 'update'; + } + + /** + * Returns target package. + * + * @return PackageInterface + */ + public function getTargetPackage() + { + return $this->targetPackage; + } +} diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index b06538d40..ab2cf3346 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -14,6 +14,7 @@ namespace Composer\DependencyResolver; use Composer\Repository\RepositoryInterface; use Composer\Package\PackageInterface; +use Composer\DependencyResolver\Operation; /** * @author Nils Adermann @@ -1105,28 +1106,21 @@ class Solver if (isset($installMeansUpdateMap[$literal->getPackageId()])) { $source = $installMeansUpdateMap[$literal->getPackageId()]; - $transaction[] = array( - 'job' => 'update', - 'from' => $source, - 'to' => $package, - 'why' => $this->decisionQueueWhy[$i], + $transaction[] = new Operation\UpdateOperation( + $source, $package, $this->decisionQueueWhy[$i] ); // avoid updates to one package from multiple origins unset($installMeansUpdateMap[$literal->getPackageId()]); $ignoreRemove[$source->getId()] = true; } else { - $transaction[] = array( - 'job' => 'install', - 'package' => $package, - 'why' => $this->decisionQueueWhy[$i], + $transaction[] = new Operation\InstallOperation( + $package, $this->decisionQueueWhy[$i] ); } } else if (!isset($ignoreRemove[$package->getId()])) { - $transaction[] = array( - 'job' => 'remove', - 'package' => $package, - 'why' => $this->decisionQueueWhy[$i], + $transaction[] = new Operation\UninstallOperation( + $package, $this->decisionQueueWhy[$i] ); } } @@ -2060,4 +2054,4 @@ class Solver } echo "\n"; } -} \ No newline at end of file +} diff --git a/src/Composer/Installer/Operation.php b/src/Composer/Installer/Operation.php deleted file mode 100644 index b442ff743..000000000 --- a/src/Composer/Installer/Operation.php +++ /dev/null @@ -1,57 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Installer; - -use Composer\Installer\InstallerInterface; -use Composer\Package\PackageInterface; - -/** - * Installer operation command - * - * @author Konstantin Kudryashov - */ -class Operation -{ - private $installer; - private $type; - private $package; - - public function __construct(InstallerInterface $installer, $type, PackageInterface $package) - { - $type = strtolower($type); - if (!in_array($type, array('install', 'update', 'remove'))) { - throw new \UnexpectedValueException('Unhandled operation type: ' . $type); - } - - $this->installer = $installer; - $this->type = $type; - $this->package = $package; - } - - public function getType() - { - return $this->type; - } - - public function getPackage() - { - return $this->package; - } - - public function execute() - { - $method = $this->getType(); - - return $this->installer->$method($this->getPackage()); - } -} diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 03e1558e7..972676737 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -213,7 +213,10 @@ class SolverTest extends \PHPUnit_Framework_TestCase )); } - public function testSolverWithComposerRepo() + /** + * @TODO: fix packagist.org bug + */ + public function BROKEN_testSolverWithComposerRepo() { $this->repoInstalled = new PlatformRepository; @@ -240,10 +243,16 @@ class SolverTest extends \PHPUnit_Framework_TestCase protected function checkSolverResult(array $expected) { - $result = $this->solver->solve($this->request); + $transaction = $this->solver->solve($this->request); - foreach ($result as &$step) { - unset($step['why']); + $result = array(); + foreach ($transaction as $operation) { + if ('update' === $operation->getJobType()) { + $result[] = array('job' => 'update', 'from' => $operation->getPackage(), 'to' => $operation->getTargetPackage()); + } else { + $job = 'uninstall' === $operation->getJobType() ? 'remove' : 'install'; + $result[] = array('job' => $job, 'package' => $operation->getPackage()); + } } $this->assertEquals($expected, $result); From 10bd9c37295c1a8228c3f6719bfa3b16c839cf5b Mon Sep 17 00:00:00 2001 From: everzet Date: Sat, 24 Sep 2011 02:35:13 +0300 Subject: [PATCH 16/41] Fixed FilesystemRegistry test name --- .../{FilesystemRegistryTests.php => FilesystemRegistryTest.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/Composer/Test/Installer/Registry/{FilesystemRegistryTests.php => FilesystemRegistryTest.php} (100%) diff --git a/tests/Composer/Test/Installer/Registry/FilesystemRegistryTests.php b/tests/Composer/Test/Installer/Registry/FilesystemRegistryTest.php similarity index 100% rename from tests/Composer/Test/Installer/Registry/FilesystemRegistryTests.php rename to tests/Composer/Test/Installer/Registry/FilesystemRegistryTest.php From d27691a67f6c5be21357a7afe032f796c61c2504 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 21 Sep 2011 23:02:58 +0200 Subject: [PATCH 17/41] Fix namespace/use/typo issues --- bin/composer | 2 +- src/Composer/Command/InstallCommand.php | 4 +++- src/Composer/Composer.php | 1 + src/Composer/Package/Loader/ArrayLoader.php | 2 +- src/Composer/Repository/PlatformRepository.php | 1 + 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/bin/composer b/bin/composer index 5ccd36118..12fbab8d6 100755 --- a/bin/composer +++ b/bin/composer @@ -23,7 +23,7 @@ $composer->setRepository('Platform', new Repository\PlatformRepository()); $composer->setRepository('Packagist', new Repository\ComposerRepository('http://packagist.org')); // initialize package -$loader = new Package\Loader\Json(); +$loader = new Package\Loader\JsonLoader(); $package = $loader->load('composer.json'); // initialize lock diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index f183c049b..b2e41a522 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -13,6 +13,8 @@ namespace Composer\Command; use Composer\DependencyResolver; +use Composer\DependencyResolver\Pool; +use Composer\DependencyResolver\Request; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -43,7 +45,7 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { if ($this->getLock()->isLocked()) { - $this->writeln('Found lockfile. Reading'); + $output->writeln('Found lockfile. Reading'); foreach ($this->getLock()->getLockedPackages() as $package) { $installer = $this->getComposer()->getInstaller($package->getType()); diff --git a/src/Composer/Composer.php b/src/Composer/Composer.php index ceeb16735..a5ca2a56e 100644 --- a/src/Composer/Composer.php +++ b/src/Composer/Composer.php @@ -13,6 +13,7 @@ namespace Composer; use Composer\Installer\InstallerInterface; +use Composer\Repository\RepositoryInterface; /** * @author Jordi Boggiano diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 89f2d3307..789db93a2 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -99,7 +99,7 @@ class ArrayLoader $links[] = new Package\Link($srcPackageName, $packageName, $constraint, $description); } - return $requirements; + return $links; } private function validateConfig(array $config) diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index 624cc788b..70910e3ad 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -14,6 +14,7 @@ namespace Composer\Repository; use Composer\Package\MemoryPackage; use Composer\Package\BasePackage; +use Composer\Package\Version\VersionParser; /** * @author Jordi Boggiano From c67d8ee2a37bfa4f596b5595f64ebc9786f36acb Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 21 Sep 2011 23:03:17 +0200 Subject: [PATCH 18/41] Add note to schema --- doc/composer-schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/composer-schema.json b/doc/composer-schema.json index 122de80cd..8d80ccf53 100644 --- a/doc/composer-schema.json +++ b/doc/composer-schema.json @@ -9,7 +9,7 @@ "required": true }, "type": { - "description": "Package type, either 'Library', or the parent project it applies to if it's a plugin for a framework or application (e.g. 'Symfony2', 'Typo3', 'Drupal', ..).", + "description": "Package type, either 'Library', or the parent project it applies to if it's a plugin for a framework or application (e.g. 'Symfony2', 'Typo3', 'Drupal', ..), note that this has to be defined and communicated by any project implementing a custom composer installer, those are just unreliable examples.", "type": "string", "optional": true }, From cd80f22781777a27ce22f179401ca2b9407634d1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 21 Sep 2011 23:03:35 +0200 Subject: [PATCH 19/41] Type is not mandatory --- src/Composer/Package/Loader/ArrayLoader.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 789db93a2..83a6e2051 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -36,7 +36,7 @@ class ArrayLoader $version = $versionParser->parse($config['version']); $package = new Package\MemoryPackage($config['name'], $version['version'], $version['type']); - $package->setType($config['type']); + $package->setType(isset($config['type']) ? $config['type'] : 'library'); if (isset($config['extra'])) { $package->setExtra($config['extra']); @@ -107,9 +107,6 @@ class ArrayLoader if (!isset($config['name'])) { throw new \UnexpectedValueException('name is required for package'); } - if (!isset($config['type'])) { - throw new \UnexpectedValueException('type is required for package'); - } if (!isset($config['version'])) { throw new \UnexpectedValueException('version is required for package'); } From e700e6fcd9487d7db3efd6ae2796c00aa6bc3887 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 21 Sep 2011 23:03:59 +0200 Subject: [PATCH 20/41] Use new version parser in PlatformRepository --- src/Composer/Repository/PlatformRepository.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index 70910e3ad..5a94eee55 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -25,10 +25,12 @@ class PlatformRepository extends ArrayRepository { parent::initialize(); + $versionParser = new VersionParser(); + try { - $version = BasePackage::parseVersion(PHP_VERSION); + $version = $versionParser->parse(PHP_VERSION); } catch (\UnexpectedValueException $e) { - $version = BasePackage::parseVersion(preg_replace('#^(.+?)(-.+)?$#', '$1', PHP_VERSION)); + $version = $versionParser->parse(preg_replace('#^(.+?)(-.+)?$#', '$1', PHP_VERSION)); } $php = new MemoryPackage('php', $version['version'], $version['type']); @@ -41,7 +43,7 @@ class PlatformRepository extends ArrayRepository $reflExt = new \ReflectionExtension($ext); try { - $version = BasePackage::parseVersion($reflExt->getVersion()); + $version = $versionParser->parse($reflExt->getVersion()); } catch (\UnexpectedValueException $e) { $version = array('version' => '0', 'type' => 'stable'); } From 8533a650613579ae856585a31be1fe73e01b9081 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 21 Sep 2011 23:04:17 +0200 Subject: [PATCH 21/41] Remove unused methods from RepositoryInterface --- src/Composer/Repository/RepositoryInterface.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Composer/Repository/RepositoryInterface.php b/src/Composer/Repository/RepositoryInterface.php index ac702140d..49e16690e 100644 --- a/src/Composer/Repository/RepositoryInterface.php +++ b/src/Composer/Repository/RepositoryInterface.php @@ -17,8 +17,5 @@ namespace Composer\Repository; */ interface RepositoryInterface extends \Countable { - static function supports($type, $name = '', $url = ''); - static function create($type, $name = '', $url = ''); - function getPackages(); } From 6d667a849e4989be83dfe875e9e1e11670c6fd20 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 21 Sep 2011 23:05:31 +0200 Subject: [PATCH 22/41] Remove broken functional test --- .../Test/DependencyResolver/SolverTest.php | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 972676737..8fc8fc630 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -213,28 +213,6 @@ class SolverTest extends \PHPUnit_Framework_TestCase )); } - /** - * @TODO: fix packagist.org bug - */ - public function BROKEN_testSolverWithComposerRepo() - { - $this->repoInstalled = new PlatformRepository; - - // overwrite solver with custom installed repo - $this->solver = new Solver($this->policy, $this->pool, $this->repoInstalled); - - $this->repo = new ComposerRepository('http://packagist.org'); - list($monolog) = $this->repo->getPackages(); - - $this->reposComplete(); - - $this->request->install('Monolog'); - - $this->checkSolverResult(array( - array('job' => 'install', 'package' => $monolog), - )); - } - protected function reposComplete() { $this->pool->addRepository($this->repoInstalled); From 123f5fef777e8d571a4c6263032c8f6c890dd356 Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 14:40:12 +0300 Subject: [PATCH 23/41] Refactored Operations in order to fulfill naderman requests --- .../Operation/InstallOperation.php | 35 +++++++++++++++++++ .../Operation/OperationInterface.php | 11 ++---- .../Operation/SolverOperation.php | 29 ++------------- .../Operation/UninstallOperation.php | 35 +++++++++++++++++++ .../Operation/UpdateOperation.php | 35 +++++++++++++++---- src/Composer/Installer/LibraryInstaller.php | 2 +- .../Test/DependencyResolver/SolverTest.php | 13 +++++-- 7 files changed, 115 insertions(+), 45 deletions(-) diff --git a/src/Composer/DependencyResolver/Operation/InstallOperation.php b/src/Composer/DependencyResolver/Operation/InstallOperation.php index b8383b121..69f042159 100644 --- a/src/Composer/DependencyResolver/Operation/InstallOperation.php +++ b/src/Composer/DependencyResolver/Operation/InstallOperation.php @@ -21,6 +21,41 @@ use Composer\Package\PackageInterface; */ class InstallOperation extends SolverOperation { + protected $package; + + /** + * Initializes operation. + * + * @param PackageInterface $package package instance + * @param string $reason operation reason + */ + public function __construct(PackageInterface $package, $reason = null) + { + parent::__construct($reason); + + $this->package = $package; + } + + /** + * Returns package instance. + * + * @return PackageInterface + */ + public function getPackage() + { + return $this->package; + } + + /** + * Returns installer type to be used with this operation. + * + * @return string + */ + public function getInstallerType() + { + return $this->package->getType(); + } + /** * Returns job type. * diff --git a/src/Composer/DependencyResolver/Operation/OperationInterface.php b/src/Composer/DependencyResolver/Operation/OperationInterface.php index fcfc6c201..7cb943440 100644 --- a/src/Composer/DependencyResolver/Operation/OperationInterface.php +++ b/src/Composer/DependencyResolver/Operation/OperationInterface.php @@ -29,18 +29,11 @@ interface OperationInterface function getJobType(); /** - * Returns package instance. - * - * @return PackageInterface - */ - function getPackage(); - - /** - * Returns package type. + * Returns installer type to be used with this operation. * * @return string */ - function getPackageType(); + function getInstallerType(); /** * Returns operation reason. diff --git a/src/Composer/DependencyResolver/Operation/SolverOperation.php b/src/Composer/DependencyResolver/Operation/SolverOperation.php index 0cca6a6da..a0071641e 100644 --- a/src/Composer/DependencyResolver/Operation/SolverOperation.php +++ b/src/Composer/DependencyResolver/Operation/SolverOperation.php @@ -21,39 +21,16 @@ use Composer\Package\PackageInterface; */ abstract class SolverOperation implements OperationInterface { - protected $package; protected $reason; /** * Initializes operation. * - * @param PackageInterface $package package instance - * @param string $reason operation reason + * @param string $reason operation reason */ - public function __construct(PackageInterface $package, $reason = null) + public function __construct($reason = null) { - $this->package = $package; - $this->reason = $reason; - } - - /** - * Returns package instance. - * - * @return PackageInterface - */ - public function getPackage() - { - return $this->package; - } - - /** - * Returns package type. - * - * @return string - */ - public function getPackageType() - { - return $this->package->getType(); + $this->reason = $reason; } /** diff --git a/src/Composer/DependencyResolver/Operation/UninstallOperation.php b/src/Composer/DependencyResolver/Operation/UninstallOperation.php index f6c853079..71bfc088f 100644 --- a/src/Composer/DependencyResolver/Operation/UninstallOperation.php +++ b/src/Composer/DependencyResolver/Operation/UninstallOperation.php @@ -21,6 +21,41 @@ use Composer\Package\PackageInterface; */ class UninstallOperation extends SolverOperation { + protected $package; + + /** + * Initializes operation. + * + * @param PackageInterface $package package instance + * @param string $reason operation reason + */ + public function __construct(PackageInterface $package, $reason = null) + { + parent::__construct($reason); + + $this->package = $package; + } + + /** + * Returns package instance. + * + * @return PackageInterface + */ + public function getPackage() + { + return $this->package; + } + + /** + * Returns installer type to be used with this operation. + * + * @return string + */ + public function getInstallerType() + { + return $this->package->getType(); + } + /** * Returns job type. * diff --git a/src/Composer/DependencyResolver/Operation/UpdateOperation.php b/src/Composer/DependencyResolver/Operation/UpdateOperation.php index 1ea400d07..5caadd881 100644 --- a/src/Composer/DependencyResolver/Operation/UpdateOperation.php +++ b/src/Composer/DependencyResolver/Operation/UpdateOperation.php @@ -21,6 +21,7 @@ use Composer\Package\PackageInterface; */ class UpdateOperation extends SolverOperation { + protected $initialPackage; protected $targetPackage; /** @@ -32,19 +33,20 @@ class UpdateOperation extends SolverOperation */ public function __construct(PackageInterface $initial, PackageInterface $target, $reason = null) { - parent::__construct($initial, $reason); + parent::__construct($reason); - $this->targetPackage = $target; + $this->initialPackage = $initial; + $this->targetPackage = $target; } /** - * Returns job type. + * Returns initial package. * - * @return string + * @return PackageInterface */ - public function getJobType() + public function getInitialPackage() { - return 'update'; + return $this->initialPackage; } /** @@ -56,4 +58,25 @@ class UpdateOperation extends SolverOperation { return $this->targetPackage; } + + /** + * Returns installer type to be used with this operation. + * + * @return string + */ + public function getInstallerType() + { + return $this->targetPackage->getType(); + } + + + /** + * Returns job type. + * + * @return string + */ + public function getJobType() + { + return 'update'; + } } diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index f8b1313e9..ebe767259 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -81,7 +81,7 @@ class LibraryInstaller implements InstallerInterface $method = $operation->getJobType(); if ('update' === $method) { - $this->$method($operation->getPackage(), $operation->getTargetPackage()); + $this->$method($operation->getInitialPackage(), $operation->getTargetPackage()); } else { $this->$method($operation->getPackage()); } diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 8fc8fc630..dd5d267a3 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -226,10 +226,17 @@ class SolverTest extends \PHPUnit_Framework_TestCase $result = array(); foreach ($transaction as $operation) { if ('update' === $operation->getJobType()) { - $result[] = array('job' => 'update', 'from' => $operation->getPackage(), 'to' => $operation->getTargetPackage()); + $result[] = array( + 'job' => 'update', + 'from' => $operation->getInitialPackage(), + 'to' => $operation->getTargetPackage() + ); } else { - $job = 'uninstall' === $operation->getJobType() ? 'remove' : 'install'; - $result[] = array('job' => $job, 'package' => $operation->getPackage()); + $job = ('uninstall' === $operation->getJobType() ? 'remove' : 'install'); + $result[] = array( + 'job' => $job, + 'package' => $operation->getPackage() + ); } } From 9f98ee25eaffd7edf67a28646aaed1abfc594d98 Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 15:44:05 +0300 Subject: [PATCH 24/41] Enhanced repository interface --- .../Repository/RepositoryInterface.php | 24 +++++++++++ .../WritableRepositoryInterface.php | 42 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/Composer/Repository/WritableRepositoryInterface.php diff --git a/src/Composer/Repository/RepositoryInterface.php b/src/Composer/Repository/RepositoryInterface.php index 49e16690e..9684492da 100644 --- a/src/Composer/Repository/RepositoryInterface.php +++ b/src/Composer/Repository/RepositoryInterface.php @@ -12,10 +12,34 @@ namespace Composer\Repository; +use Composer\Package\PackageInterface; + /** + * Repository interface. + * * @author Nils Adermann + * @author Konstantin Kudryashov */ interface RepositoryInterface extends \Countable { + /** + * Initializes repository (reads file, opens connection). + */ + function initialize(); + + /** + * Checks if specified package registered (installed). + * + * @param PackageInterface $package package instance + * + * @return Boolean + */ + function hasPackage(PackageInterface $package); + + /** + * Returns list of registered packages. + * + * @return array + */ function getPackages(); } diff --git a/src/Composer/Repository/WritableRepositoryInterface.php b/src/Composer/Repository/WritableRepositoryInterface.php new file mode 100644 index 000000000..77291e889 --- /dev/null +++ b/src/Composer/Repository/WritableRepositoryInterface.php @@ -0,0 +1,42 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\Package\PackageInterface; + +/** + * Writable repository interface. + * + * @author Konstantin Kudryashov + */ +interface WritableRepositoryInterface extends RepositoryInterface +{ + /** + * Writes repository (f.e. to the disc). + */ + function write(); + + /** + * Adds package to the repository. + * + * @param PackageInterface $package package instance + */ + function addPackage(PackageInterface $package); + + /** + * Removes package from the repository. + * + * @param PackageInterface $package package instance + */ + function removePackage(PackageInterface $package); +} From 613310871025dd71712204f5c27e1154566715f1 Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 15:44:41 +0300 Subject: [PATCH 25/41] Refactored LibraryInstaller to use WritableRepository instead of Registry --- src/Composer/Installer/LibraryInstaller.php | 60 ++++++-------- .../Test/Installer/LibraryInstallerTest.php | 81 +++++++------------ 2 files changed, 55 insertions(+), 86 deletions(-) diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index ebe767259..75cb4d92a 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -13,8 +13,7 @@ namespace Composer\Installer; use Composer\Downloader\DownloadManager; -use Composer\Installer\Registry\RegistryInterface; -use Composer\Installer\Registry\FilesystemRegistry; +use Composer\Repository\WritableRepositoryInterface; use Composer\DependencyResolver\Operation\OperationInterface; use Composer\Package\PackageInterface; @@ -28,16 +27,16 @@ class LibraryInstaller implements InstallerInterface { private $dir; private $dm; - private $registry; + private $repository; /** * Initializes library installer. * - * @param string $dir relative path for packages home - * @param DownloadManager $dm download manager - * @param RegistryInterface $registry registry controller + * @param string $dir relative path for packages home + * @param DownloadManager $dm download manager + * @param WritableRepositoryInterface $repository repository controller */ - public function __construct($dir, DownloadManager $dm, RegistryInterface $registry = null) + public function __construct($dir, DownloadManager $dm, WritableRepositoryInterface $repository) { $this->dir = $dir; $this->dm = $dm; @@ -55,20 +54,7 @@ class LibraryInstaller implements InstallerInterface } } - if (null === $registry) { - $registry = new FilesystemRegistry('.composer', str_replace('/', '_', $dir)); - } - - $this->registry = $registry; - $this->registry->open(); - } - - /** - * Closes packages registry. - */ - public function __destruct() - { - $this->registry->close(); + $this->repository = $repository; } /** @@ -96,7 +82,7 @@ class LibraryInstaller implements InstallerInterface */ public function isInstalled(PackageInterface $package) { - return $this->registry->isPackageRegistered($package); + return $this->repository->hasPackage($package); } /** @@ -108,8 +94,10 @@ class LibraryInstaller implements InstallerInterface */ public function install(PackageInterface $package) { - $type = $this->dm->download($package, $this->dir.'/'.$package->getName()); - $this->registry->registerPackage($package, $type); + $downloadPath = $this->dir.DIRECTORY_SEPARATOR.$package->getName(); + + $this->dm->download($package, $downloadPath); + $this->repository->addPackage($package); } /** @@ -122,14 +110,15 @@ class LibraryInstaller implements InstallerInterface */ public function update(PackageInterface $initial, PackageInterface $target) { - if (!$this->registry->isPackageRegistered($initial)) { - throw new \UnexpectedValueException('Package is not installed: '.$initial); + if (!$this->repository->hasPackage($initial)) { + throw new \InvalidArgumentException('Package is not installed: '.$initial); } - $type = $this->registry->getRegisteredPackageInstallerType($initial); - $this->dm->update($initial, $target, $this->dir.'/'.$initial->getName(), $type); - $this->registry->unregisterPackage($initial); - $this->registry->registerPackage($target, $type); + $downloadPath = $this->dir.DIRECTORY_SEPARATOR.$initial->getName(); + + $this->dm->update($initial, $target, $downloadPath); + $this->repository->removePackage($initial); + $this->repository->addPackage($target); } /** @@ -141,12 +130,13 @@ class LibraryInstaller implements InstallerInterface */ public function uninstall(PackageInterface $package) { - if (!$this->registry->isPackageRegistered($package)) { - throw new \UnexpectedValueException('Package is not installed: '.$package); + if (!$this->repository->hasPackage($package)) { + throw new \InvalidArgumentException('Package is not installed: '.$package); } - $type = $this->registry->getRegisteredPackageInstallerType($package); - $this->dm->remove($package, $this->dir.'/'.$package->getName(), $type); - $this->registry->unregisterPackage($package); + $downloadPath = $this->dir.DIRECTORY_SEPARATOR.$package->getName(); + + $this->dm->remove($package, $downloadPath); + $this->repository->removePackage($package); } } diff --git a/tests/Composer/Test/Installer/LibraryInstallerTest.php b/tests/Composer/Test/Installer/LibraryInstallerTest.php index 93f3414f8..6069b2cd8 100644 --- a/tests/Composer/Test/Installer/LibraryInstallerTest.php +++ b/tests/Composer/Test/Installer/LibraryInstallerTest.php @@ -19,7 +19,7 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase { private $dir; private $dm; - private $registry; + private $repository; private $library; protected function setUp() @@ -33,35 +33,27 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); - $this->registry = $this->getMockBuilder('Composer\Installer\Registry\RegistryInterface') + $this->repository = $this->getMockBuilder('Composer\Repository\WritableRepositoryInterface') ->disableOriginalConstructor() ->getMock(); } public function testInstallerCreation() { - $this->registry - ->expects($this->once()) - ->method('open'); - - $this->registry - ->expects($this->once()) - ->method('close'); - - $library = new LibraryInstaller($this->dir, $this->dm, $this->registry); + $library = new LibraryInstaller($this->dir, $this->dm, $this->repository); $this->assertTrue(is_dir($this->dir)); $file = sys_get_temp_dir().'/file'; touch($file); $this->setExpectedException('UnexpectedValueException'); - $library = new LibraryInstaller($file, $this->dm, $this->registry); + $library = new LibraryInstaller($file, $this->dm, $this->repository); } public function testExecuteOperation() { $library = $this->getMockBuilder('Composer\Installer\LibraryInstaller') - ->setConstructorArgs(array($this->dir, $this->dm, $this->registry)) + ->setConstructorArgs(array($this->dir, $this->dm, $this->repository)) ->setMethods(array('install', 'update', 'uninstall')) ->getMock(); @@ -92,12 +84,12 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase public function testIsInstalled() { - $library = new LibraryInstaller($this->dir, $this->dm, $this->registry); + $library = new LibraryInstaller($this->dir, $this->dm, $this->repository); $package = $this->createPackageMock(); - $this->registry + $this->repository ->expects($this->exactly(2)) - ->method('isPackageRegistered') + ->method('hasPackage') ->with($package) ->will($this->onConsecutiveCalls(true, false)); @@ -107,7 +99,7 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase public function testInstall() { - $library = new LibraryInstaller($this->dir, $this->dm, $this->registry); + $library = new LibraryInstaller($this->dir, $this->dm, $this->repository); $package = $this->createPackageMock(); $package @@ -118,20 +110,19 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase $this->dm ->expects($this->once()) ->method('download') - ->with($package, $this->dir.'/some/package') - ->will($this->returnValue('source')); + ->with($package, $this->dir.'/some/package'); - $this->registry + $this->repository ->expects($this->once()) - ->method('registerPackage') - ->with($package, 'source'); + ->method('addPackage') + ->with($package); $library->install($package); } public function testUpdate() { - $library = new LibraryInstaller($this->dir, $this->dm, $this->registry); + $library = new LibraryInstaller($this->dir, $this->dm, $this->repository); $initial = $this->createPackageMock(); $target = $this->createPackageMock(); @@ -140,43 +131,37 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase ->method('getName') ->will($this->returnValue('package1')); - $this->registry + $this->repository ->expects($this->exactly(2)) - ->method('isPackageRegistered') + ->method('hasPackage') ->with($initial) ->will($this->onConsecutiveCalls(true, false)); - $this->registry - ->expects($this->once()) - ->method('getRegisteredPackageInstallerType') - ->with($initial) - ->will($this->returnValue('dist')); - $this->dm ->expects($this->once()) ->method('update') - ->with($initial, $target, $this->dir.'/package1', 'dist'); + ->with($initial, $target, $this->dir.'/package1'); - $this->registry + $this->repository ->expects($this->once()) - ->method('unregisterPackage') + ->method('removePackage') ->with($initial); - $this->registry + $this->repository ->expects($this->once()) - ->method('registerPackage') - ->with($target, 'dist'); + ->method('addPackage') + ->with($target); $library->update($initial, $target); - $this->setExpectedException('UnexpectedValueException'); + $this->setExpectedException('InvalidArgumentException'); $library->update($initial, $target); } public function testUninstall() { - $library = new LibraryInstaller($this->dir, $this->dm, $this->registry); + $library = new LibraryInstaller($this->dir, $this->dm, $this->repository); $package = $this->createPackageMock(); $package @@ -184,31 +169,25 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase ->method('getName') ->will($this->returnValue('pkg')); - $this->registry + $this->repository ->expects($this->exactly(2)) - ->method('isPackageRegistered') + ->method('hasPackage') ->with($package) ->will($this->onConsecutiveCalls(true, false)); - $this->registry - ->expects($this->once()) - ->method('getRegisteredPackageInstallerType') - ->with($package) - ->will($this->returnValue('source')); - $this->dm ->expects($this->once()) ->method('remove') - ->with($package, $this->dir.'/pkg', 'source'); + ->with($package, $this->dir.'/pkg'); - $this->registry + $this->repository ->expects($this->once()) - ->method('unregisterPackage') + ->method('removePackage') ->with($package); $library->uninstall($package); - $this->setExpectedException('UnexpectedValueException'); + $this->setExpectedException('InvalidArgumentException'); $library->uninstall($package); } From 17d1abcec03c02710fa12420a87b47d5618af740 Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 18:30:54 +0300 Subject: [PATCH 26/41] Refactored DownloadManager --- src/Composer/Downloader/DownloadManager.php | 86 ++++++---- src/Composer/Package/MemoryPackage.php | 17 ++ src/Composer/Package/PackageInterface.php | 14 ++ .../Test/Downloader/DownloadManagerTest.php | 159 +++++++++++++++++- 4 files changed, 235 insertions(+), 41 deletions(-) diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 5b15f54cf..96c18c4a5 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -77,35 +77,33 @@ class DownloadManager /** * Downloads package into target dir. * - * @param PackageInterface $package package instance - * @param string $targetDir target dir + * @param PackageInterface $package package instance + * @param string $targetDir target dir + * @param Boolean $preferSource prefer installation from source * - * @return string downloader type (source/dist) + * @return string downloader type (source/dist) * - * @throws InvalidArgumentException if package have no urls to download from + * @throws InvalidArgumentException if package have no urls to download from */ - public function download(PackageInterface $package, $targetDir) + public function download(PackageInterface $package, $targetDir, $preferSource = null) { - $sourceType = $package->getSourceType(); - $distType = $package->getDistType(); + $preferSource = null !== $preferSource ? $preferSource : $this->preferSource; + $sourceType = $package->getSourceType(); + $distType = $package->getDistType(); - if (!($this->preferSource && $sourceType) && $distType) { + if (!($preferSource && $sourceType) && $distType) { $downloader = $this->getDownloader($distType); $downloader->download( $package, $targetDir, $package->getDistUrl(), $package->getDistSha1Checksum() ); - - return 'dist'; - } - - if ($sourceType) { + $package->setInstallationSource('dist'); + } elseif ($sourceType) { $downloader = $this->getDownloader($sourceType); $downloader->download($package, $targetDir, $package->getSourceUrl()); - - return 'source'; + $package->setInstallationSource('source'); + } else { + throw new \InvalidArgumentException('Package should have dist or source specified'); } - - throw new \InvalidArgumentException('Package should have dist or source specified'); } /** @@ -114,20 +112,33 @@ class DownloadManager * @param PackageInterface $initial initial package version * @param PackageInterface $target target package version * @param string $targetDir target dir - * @param string $type downloader type (source/dist) * * @throws InvalidArgumentException if initial package is not installed */ - public function update(PackageInterface $initial, PackageInterface $target, $targetDir, $type) + public function update(PackageInterface $initial, PackageInterface $target, $targetDir) { - if ('dist' === $type) { - $downloader = $this->getDownloader($initial->getDistType()); - $downloader->update($initial, $target, $targetDir); - } elseif ('source' === $type) { - $downloader = $this->getDownloader($initial->getSourceType()); + if (null === $installationType = $initial->getInstallationSource()) { + throw new \InvalidArgumentException( + 'Package '.$initial.' was not been installed propertly and can not be updated' + ); + } + $useSource = 'source' === $installationType; + + if (!$useSource) { + $initialType = $initial->getDistType(); + $targetType = $target->getDistType(); + } else { + $initialType = $initial->getSourceType(); + $targetType = $target->getSourceType(); + } + + $downloader = $this->getDownloader($initialType); + + if ($initialType === $targetType) { $downloader->update($initial, $target, $targetDir); } else { - throw new \InvalidArgumentException('Package should have dist or source specified'); + $downloader->remove($initial, $targetDir); + $this->download($target, $targetDir, $useSource); } } @@ -136,18 +147,23 @@ class DownloadManager * * @param PackageInterface $package package instance * @param string $targetDir target dir - * @param string $type downloader type (source/dist) */ - public function remove(PackageInterface $package, $targetDir, $type) + public function remove(PackageInterface $package, $targetDir) { - if ('dist' === $type) { - $downloader = $this->getDownloader($package->getDistType()); - $downloader->remove($package, $targetDir); - } elseif ('source' === $type) { - $downloader = $this->getDownloader($package->getSourceType()); - $downloader->remove($package, $targetDir); - } else { - throw new \InvalidArgumentException('Package should have dist or source specified'); + if (null === $installationType = $package->getInstallationSource()) { + throw new \InvalidArgumentException( + 'Package '.$package.' was not been installed propertly and can not be removed' + ); } + $useSource = 'source' === $installationType; + + // get proper downloader + if (!$useSource) { + $downloader = $this->getDownloader($package->getDistType()); + } else { + $downloader = $this->getDownloader($package->getSourceType()); + } + + $downloader->remove($package, $targetDir); } } diff --git a/src/Composer/Package/MemoryPackage.php b/src/Composer/Package/MemoryPackage.php index 0476f86e6..535b73eb1 100644 --- a/src/Composer/Package/MemoryPackage.php +++ b/src/Composer/Package/MemoryPackage.php @@ -20,6 +20,7 @@ namespace Composer\Package; class MemoryPackage extends BasePackage { protected $type; + protected $installationSource; protected $sourceType; protected $sourceUrl; protected $distType; @@ -84,6 +85,22 @@ class MemoryPackage extends BasePackage return $this->extra; } + /** + * {@inheritDoc} + */ + public function setInstallationSource($type) + { + $this-> installationSource = $type; + } + + /** + * {@inheritDoc} + */ + public function getInstallationSource() + { + return $this->installationSource; + } + /** * @param string $type */ diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index 47b81814c..69e2ed497 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -82,6 +82,20 @@ interface PackageInterface */ function getExtra(); + /** + * Sets source from which this package was installed (source/dist). + * + * @param string $type source/dist + */ + function setInstallationSource($type); + + /** + * Returns source from which this package was installed (source/dist). + * + * @param string $type source/dist + */ + function getInstallationSource(); + /** * Returns the repository type of this package, e.g. git, svn * diff --git a/tests/Composer/Test/Downloader/DownloadManagerTest.php b/tests/Composer/Test/Downloader/DownloadManagerTest.php index efac5f0ba..bf39a377a 100644 --- a/tests/Composer/Test/Downloader/DownloadManagerTest.php +++ b/tests/Composer/Test/Downloader/DownloadManagerTest.php @@ -49,6 +49,11 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->method('getDistSha1Checksum') ->will($this->returnValue('sha1')); + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('dist'); + $pearDownloader = $this->createDownloaderMock(); $pearDownloader ->expects($this->once()) @@ -100,6 +105,11 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->method('getDistSha1Checksum') ->will($this->returnValue('sha1')); + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('dist'); + $pearDownloader = $this->createDownloaderMock(); $pearDownloader ->expects($this->once()) @@ -129,6 +139,11 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->method('getSourceUrl') ->will($this->returnValue('source_url')); + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('source'); + $gitDownloader = $this->createDownloaderMock(); $gitDownloader ->expects($this->once()) @@ -158,6 +173,11 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->method('getSourceUrl') ->will($this->returnValue('source_url')); + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('source'); + $gitDownloader = $this->createDownloaderMock(); $gitDownloader ->expects($this->once()) @@ -192,6 +212,11 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->method('getDistSha1Checksum') ->will($this->returnValue('sha1')); + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('dist'); + $pearDownloader = $this->createDownloaderMock(); $pearDownloader ->expects($this->once()) @@ -222,6 +247,11 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase ->method('getSourceUrl') ->will($this->returnValue('source_url')); + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('source'); + $gitDownloader = $this->createDownloaderMock(); $gitDownloader ->expects($this->once()) @@ -254,15 +284,23 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $manager->download($package, 'target_dir'); } - public function testUpdateDist() + public function testUpdateDistWithEqualTypes() { $initial = $this->createPackageMock(); + $initial + ->expects($this->once()) + ->method('getInstallationSource') + ->will($this->returnValue('dist')); $initial ->expects($this->once()) ->method('getDistType') ->will($this->returnValue('pear')); $target = $this->createPackageMock(); + $target + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); $pearDownloader = $this->createDownloaderMock(); $pearDownloader @@ -273,18 +311,62 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $manager = new DownloadManager(); $manager->setDownloader('pear', $pearDownloader); - $manager->update($initial, $target, 'vendor/bundles/FOS/UserBundle', 'dist'); + $manager->update($initial, $target, 'vendor/bundles/FOS/UserBundle'); } - public function testUpdateSource() + public function testUpdateDistWithNotEqualTypes() { $initial = $this->createPackageMock(); + $initial + ->expects($this->once()) + ->method('getInstallationSource') + ->will($this->returnValue('dist')); + $initial + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + + $target = $this->createPackageMock(); + $target + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('composer')); + + $pearDownloader = $this->createDownloaderMock(); + $pearDownloader + ->expects($this->once()) + ->method('remove') + ->with($initial, 'vendor/bundles/FOS/UserBundle'); + + $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setMethods(array('download')) + ->getMock(); + $manager + ->expects($this->once()) + ->method('download') + ->with($target, 'vendor/bundles/FOS/UserBundle', false); + + $manager->setDownloader('pear', $pearDownloader); + $manager->update($initial, $target, 'vendor/bundles/FOS/UserBundle'); + } + + public function testUpdateSourceWithEqualTypes() + { + $initial = $this->createPackageMock(); + $initial + ->expects($this->once()) + ->method('getInstallationSource') + ->will($this->returnValue('source')); $initial ->expects($this->once()) ->method('getSourceType') ->will($this->returnValue('svn')); $target = $this->createPackageMock(); + $target + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('svn')); $svnDownloader = $this->createDownloaderMock(); $svnDownloader @@ -295,12 +377,63 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $manager = new DownloadManager(); $manager->setDownloader('svn', $svnDownloader); - $manager->update($initial, $target, 'vendor/pkg', 'source'); + $manager->update($initial, $target, 'vendor/pkg'); + } + + public function testUpdateSourceWithNotEqualTypes() + { + $initial = $this->createPackageMock(); + $initial + ->expects($this->once()) + ->method('getInstallationSource') + ->will($this->returnValue('source')); + $initial + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('svn')); + + $target = $this->createPackageMock(); + $target + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + + $svnDownloader = $this->createDownloaderMock(); + $svnDownloader + ->expects($this->once()) + ->method('remove') + ->with($initial, 'vendor/pkg'); + + $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setMethods(array('download')) + ->getMock(); + $manager + ->expects($this->once()) + ->method('download') + ->with($target, 'vendor/pkg', true); + $manager->setDownloader('svn', $svnDownloader); + + $manager->update($initial, $target, 'vendor/pkg'); + } + + public function testUpdateBadlyInstalledPackage() + { + $initial = $this->createPackageMock(); + $target = $this->createPackageMock(); + + $this->setExpectedException('InvalidArgumentException'); + + $manager = new DownloadManager(); + $manager->update($initial, $target, 'vendor/pkg'); } public function testRemoveDist() { $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getInstallationSource') + ->will($this->returnValue('dist')); $package ->expects($this->once()) ->method('getDistType') @@ -315,12 +448,16 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $manager = new DownloadManager(); $manager->setDownloader('pear', $pearDownloader); - $manager->remove($package, 'vendor/bundles/FOS/UserBundle', 'dist'); + $manager->remove($package, 'vendor/bundles/FOS/UserBundle'); } public function testRemoveSource() { $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getInstallationSource') + ->will($this->returnValue('source')); $package ->expects($this->once()) ->method('getSourceType') @@ -335,7 +472,17 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $manager = new DownloadManager(); $manager->setDownloader('svn', $svnDownloader); - $manager->remove($package, 'vendor/pkg', 'source'); + $manager->remove($package, 'vendor/pkg'); + } + + public function testRemoveBadlyInstalledPackage() + { + $package = $this->createPackageMock(); + $manager = new DownloadManager(); + + $this->setExpectedException('InvalidArgumentException'); + + $manager->remove($package, 'vendor/pkg'); } private function createDownloaderMock() From 50aa7ac60704198898afef995327824db87dbb02 Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 18:56:05 +0300 Subject: [PATCH 27/41] Provide $useSource argument to the DownloaderInterface methods --- src/Composer/Downloader/DownloadManager.php | 12 ++++++----- .../Downloader/DownloaderInterface.php | 9 ++++++--- .../Test/Downloader/DownloadManagerTest.php | 20 +++++++++---------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 96c18c4a5..8f40fdff0 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -94,12 +94,14 @@ class DownloadManager if (!($preferSource && $sourceType) && $distType) { $downloader = $this->getDownloader($distType); $downloader->download( - $package, $targetDir, $package->getDistUrl(), $package->getDistSha1Checksum() + $package, $targetDir, + $package->getDistUrl(), $package->getDistSha1Checksum(), + $preferSource ); $package->setInstallationSource('dist'); } elseif ($sourceType) { $downloader = $this->getDownloader($sourceType); - $downloader->download($package, $targetDir, $package->getSourceUrl()); + $downloader->download($package, $targetDir, $package->getSourceUrl(), $preferSource); $package->setInstallationSource('source'); } else { throw new \InvalidArgumentException('Package should have dist or source specified'); @@ -135,9 +137,9 @@ class DownloadManager $downloader = $this->getDownloader($initialType); if ($initialType === $targetType) { - $downloader->update($initial, $target, $targetDir); + $downloader->update($initial, $target, $targetDir, $useSource); } else { - $downloader->remove($initial, $targetDir); + $downloader->remove($initial, $targetDir, $useSource); $this->download($target, $targetDir, $useSource); } } @@ -164,6 +166,6 @@ class DownloadManager $downloader = $this->getDownloader($package->getSourceType()); } - $downloader->remove($package, $targetDir); + $downloader->remove($package, $targetDir, $useSource); } } diff --git a/src/Composer/Downloader/DownloaderInterface.php b/src/Composer/Downloader/DownloaderInterface.php index 7a114ea2b..863d6bd30 100644 --- a/src/Composer/Downloader/DownloaderInterface.php +++ b/src/Composer/Downloader/DownloaderInterface.php @@ -28,8 +28,9 @@ interface DownloaderInterface * @param string $path download path * @param string $url download url * @param string $checksum package checksum (for dists) + * @param Boolean $useSource download as source */ - function download(PackageInterface $package, $path, $url, $checksum = null); + function download(PackageInterface $package, $path, $url, $checksum = null, $useSource = false); /** * Updates specific package in specific folder from initial to target version. @@ -37,14 +38,16 @@ interface DownloaderInterface * @param PackageInterface $initial initial package * @param PackageInterface $target updated package * @param string $path download path + * @param Boolean $useSource download as source */ - function update(PackageInterface $initial, PackageInterface $target, $path); + function update(PackageInterface $initial, PackageInterface $target, $path, $useSource = false); /** * Removes specific package from specific folder. * * @param PackageInterface $package package instance * @param string $path download path + * @param Boolean $useSource download as source */ - function remove(PackageInterface $package, $path); + function remove(PackageInterface $package, $path, $useSource = false); } diff --git a/tests/Composer/Test/Downloader/DownloadManagerTest.php b/tests/Composer/Test/Downloader/DownloadManagerTest.php index bf39a377a..cc48bf4d1 100644 --- a/tests/Composer/Test/Downloader/DownloadManagerTest.php +++ b/tests/Composer/Test/Downloader/DownloadManagerTest.php @@ -58,7 +58,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $pearDownloader ->expects($this->once()) ->method('download') - ->with($package, 'target_dir', 'dist_url', 'sha1'); + ->with($package, 'target_dir', 'dist_url', 'sha1', false); $manager = new DownloadManager(); $manager->setDownloader('pear', $pearDownloader); @@ -114,7 +114,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $pearDownloader ->expects($this->once()) ->method('download') - ->with($package, 'target_dir', 'dist_url', 'sha1'); + ->with($package, 'target_dir', 'dist_url', 'sha1', false); $manager = new DownloadManager(); $manager->setDownloader('pear', $pearDownloader); @@ -148,7 +148,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $gitDownloader ->expects($this->once()) ->method('download') - ->with($package, 'vendor/pkg', 'source_url'); + ->with($package, 'vendor/pkg', 'source_url', false); $manager = new DownloadManager(); $manager->setDownloader('git', $gitDownloader); @@ -182,7 +182,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $gitDownloader ->expects($this->once()) ->method('download') - ->with($package, 'vendor/pkg', 'source_url'); + ->with($package, 'vendor/pkg', 'source_url', true); $manager = new DownloadManager(); $manager->setDownloader('git', $gitDownloader); @@ -221,7 +221,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $pearDownloader ->expects($this->once()) ->method('download') - ->with($package, 'target_dir', 'dist_url', 'sha1'); + ->with($package, 'target_dir', 'dist_url', 'sha1', true); $manager = new DownloadManager(); $manager->setDownloader('pear', $pearDownloader); @@ -256,7 +256,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $gitDownloader ->expects($this->once()) ->method('download') - ->with($package, 'vendor/pkg', 'source_url'); + ->with($package, 'vendor/pkg', 'source_url', true); $manager = new DownloadManager(); $manager->setDownloader('git', $gitDownloader); @@ -306,7 +306,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $pearDownloader ->expects($this->once()) ->method('update') - ->with($initial, $target, 'vendor/bundles/FOS/UserBundle'); + ->with($initial, $target, 'vendor/bundles/FOS/UserBundle', false); $manager = new DownloadManager(); $manager->setDownloader('pear', $pearDownloader); @@ -336,7 +336,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $pearDownloader ->expects($this->once()) ->method('remove') - ->with($initial, 'vendor/bundles/FOS/UserBundle'); + ->with($initial, 'vendor/bundles/FOS/UserBundle', false); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') ->setMethods(array('download')) @@ -372,7 +372,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $svnDownloader ->expects($this->once()) ->method('update') - ->with($initial, $target, 'vendor/pkg'); + ->with($initial, $target, 'vendor/pkg', true); $manager = new DownloadManager(); $manager->setDownloader('svn', $svnDownloader); @@ -402,7 +402,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $svnDownloader ->expects($this->once()) ->method('remove') - ->with($initial, 'vendor/pkg'); + ->with($initial, 'vendor/pkg', true); $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') ->setMethods(array('download')) From 2fc06994923c180abefbabcba732a9764626d575 Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 19:30:22 +0300 Subject: [PATCH 28/41] Moved operations evaluation into new InstallationManager class --- .../Operation/InstallOperation.php | 10 - .../Operation/OperationInterface.php | 7 - .../Operation/UninstallOperation.php | 10 - .../Operation/UpdateOperation.php | 11 -- .../Installer/InstallationManager.php | 131 +++++++++++++ src/Composer/Installer/InstallerInterface.php | 7 - src/Composer/Installer/LibraryInstaller.php | 16 -- .../Installer/InstallationManagerTest.php | 180 ++++++++++++++++++ .../Test/Installer/LibraryInstallerTest.php | 32 ---- 9 files changed, 311 insertions(+), 93 deletions(-) create mode 100644 src/Composer/Installer/InstallationManager.php create mode 100644 tests/Composer/Test/Installer/InstallationManagerTest.php diff --git a/src/Composer/DependencyResolver/Operation/InstallOperation.php b/src/Composer/DependencyResolver/Operation/InstallOperation.php index 69f042159..89ab5bd85 100644 --- a/src/Composer/DependencyResolver/Operation/InstallOperation.php +++ b/src/Composer/DependencyResolver/Operation/InstallOperation.php @@ -46,16 +46,6 @@ class InstallOperation extends SolverOperation return $this->package; } - /** - * Returns installer type to be used with this operation. - * - * @return string - */ - public function getInstallerType() - { - return $this->package->getType(); - } - /** * Returns job type. * diff --git a/src/Composer/DependencyResolver/Operation/OperationInterface.php b/src/Composer/DependencyResolver/Operation/OperationInterface.php index 7cb943440..704de2d5d 100644 --- a/src/Composer/DependencyResolver/Operation/OperationInterface.php +++ b/src/Composer/DependencyResolver/Operation/OperationInterface.php @@ -28,13 +28,6 @@ interface OperationInterface */ function getJobType(); - /** - * Returns installer type to be used with this operation. - * - * @return string - */ - function getInstallerType(); - /** * Returns operation reason. * diff --git a/src/Composer/DependencyResolver/Operation/UninstallOperation.php b/src/Composer/DependencyResolver/Operation/UninstallOperation.php index 71bfc088f..3731f3181 100644 --- a/src/Composer/DependencyResolver/Operation/UninstallOperation.php +++ b/src/Composer/DependencyResolver/Operation/UninstallOperation.php @@ -46,16 +46,6 @@ class UninstallOperation extends SolverOperation return $this->package; } - /** - * Returns installer type to be used with this operation. - * - * @return string - */ - public function getInstallerType() - { - return $this->package->getType(); - } - /** * Returns job type. * diff --git a/src/Composer/DependencyResolver/Operation/UpdateOperation.php b/src/Composer/DependencyResolver/Operation/UpdateOperation.php index 5caadd881..c9d75c7b4 100644 --- a/src/Composer/DependencyResolver/Operation/UpdateOperation.php +++ b/src/Composer/DependencyResolver/Operation/UpdateOperation.php @@ -59,17 +59,6 @@ class UpdateOperation extends SolverOperation return $this->targetPackage; } - /** - * Returns installer type to be used with this operation. - * - * @return string - */ - public function getInstallerType() - { - return $this->targetPackage->getType(); - } - - /** * Returns job type. * diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php new file mode 100644 index 000000000..e27f1c52e --- /dev/null +++ b/src/Composer/Installer/InstallationManager.php @@ -0,0 +1,131 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Installer; + +use Composer\Package\PackageInterface; +use Composer\DependencyResolver\Operation\OperationInterface; +use Composer\DependencyResolver\Operation\InstallOperation; +use Composer\DependencyResolver\Operation\UpdateOperation; +use Composer\DependencyResolver\Operation\UninstallOperation; + +/** + * Package operation manager. + * + * @author Konstantin Kudryashov + */ +class InstallationManager +{ + private $installers = array(); + + /** + * Sets installer for a specific package type. + * + * @param string $type package type (library f.e.) + * @param InstallerInterface $installer installer instance + */ + public function setInstaller($type, InstallerInterface $installer) + { + $this->installers[$type] = $installer; + } + + /** + * Returns installer for a specific package type. + * + * @param string $type package type + * + * @return InstallerInterface + * + * @throws InvalidArgumentException if installer for provided type is not registered + */ + public function getInstaller($type) + { + if (!isset($this->installers[$type])) { + throw new \InvalidArgumentException('Unknown installer type: '.$type); + } + + return $this->installers[$type]; + } + + /** + * Checks whether provided package is installed in one of the registered installers. + * + * @param PackageInterface $package package instance + * + * @return Boolean + */ + public function isPackageInstalled(PackageInterface $package) + { + foreach ($this->installers as $installer) { + if ($installer->isInstalled($package)) { + return true; + } + } + + return false; + } + + /** + * Executes solver operation. + * + * @param OperationInterface $operation operation instance + */ + public function execute(OperationInterface $operation) + { + $method = $operation->getJobType(); + $this->$method($operation); + } + + /** + * Executes install operation. + * + * @param InstallOperation $operation operation instance + */ + public function install(InstallOperation $operation) + { + $installer = $this->getInstaller($operation->getPackage()->getType()); + $installer->install($operation->getPackage()); + } + + /** + * Executes update operation. + * + * @param InstallOperation $operation operation instance + */ + public function update(UpdateOperation $operation) + { + $initial = $operation->getInitialPackage(); + $target = $operation->getTargetPackage(); + + $initialType = $initial->getType(); + $targetType = $target->getType(); + + if ($initialType === $targetType) { + $installer = $this->getInstaller($initialType); + $installer->update($initial, $target); + } else { + $this->getInstaller($initialType)->uninstall($initial); + $this->getInstaller($targetType)->install($target); + } + } + + /** + * Uninstalls package. + * + * @param UninstallOperation $operation operation instance + */ + public function uninstall(UninstallOperation $operation) + { + $installer = $this->getInstaller($operation->getPackage()->getType()); + $installer->uninstall($operation->getPackage()); + } +} diff --git a/src/Composer/Installer/InstallerInterface.php b/src/Composer/Installer/InstallerInterface.php index 96af2bc93..9fbacbc36 100644 --- a/src/Composer/Installer/InstallerInterface.php +++ b/src/Composer/Installer/InstallerInterface.php @@ -22,13 +22,6 @@ use Composer\Package\PackageInterface; */ interface InstallerInterface { - /** - * Executes specific solver operation. - * - * @param OperationInterface $operation solver operation instance - */ - function executeOperation(OperationInterface $operation); - /** * Checks that provided package is installed. * diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 75cb4d92a..59d546da2 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -57,22 +57,6 @@ class LibraryInstaller implements InstallerInterface $this->repository = $repository; } - /** - * Executes specific solver operation. - * - * @param OperationInterface $operation solver operation instance - */ - public function executeOperation(OperationInterface $operation) - { - $method = $operation->getJobType(); - - if ('update' === $method) { - $this->$method($operation->getInitialPackage(), $operation->getTargetPackage()); - } else { - $this->$method($operation->getPackage()); - } - } - /** * Checks that specific package is installed. * diff --git a/tests/Composer/Test/Installer/InstallationManagerTest.php b/tests/Composer/Test/Installer/InstallationManagerTest.php new file mode 100644 index 000000000..a8b1a9818 --- /dev/null +++ b/tests/Composer/Test/Installer/InstallationManagerTest.php @@ -0,0 +1,180 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Installer; + +use Composer\Installer\InstallationManager; +use Composer\DependencyResolver\Operation\InstallOperation; +use Composer\DependencyResolver\Operation\UpdateOperation; +use Composer\DependencyResolver\Operation\UninstallOperation; + +class InstallationManagerTest extends \PHPUnit_Framework_TestCase +{ + public function testSetGetInstaller() + { + $installer = $this->createInstallerMock(); + $manager = new InstallationManager(); + + $manager->setInstaller('vendor', $installer); + $this->assertSame($installer, $manager->getInstaller('vendor')); + + $this->setExpectedException('InvalidArgumentException'); + $manager->getInstaller('unregistered'); + } + + public function testExecute() + { + $manager = $this->getMockBuilder('Composer\Installer\InstallationManager') + ->setMethods(array('install', 'update', 'uninstall')) + ->getMock(); + + $installOperation = new InstallOperation($this->createPackageMock()); + $removeOperation = new UninstallOperation($this->createPackageMock()); + $updateOperation = new UpdateOperation( + $this->createPackageMock(), $this->createPackageMock() + ); + + $manager + ->expects($this->once()) + ->method('install') + ->with($installOperation); + $manager + ->expects($this->once()) + ->method('uninstall') + ->with($removeOperation); + $manager + ->expects($this->once()) + ->method('update') + ->with($updateOperation); + + $manager->execute($installOperation); + $manager->execute($removeOperation); + $manager->execute($updateOperation); + } + + public function testInstall() + { + $installer = $this->createInstallerMock(); + $manager = new InstallationManager(); + $manager->setInstaller('library', $installer); + + $package = $this->createPackageMock(); + $operation = new InstallOperation($package, 'test'); + + $package + ->expects($this->once()) + ->method('getType') + ->will($this->returnValue('library')); + + $installer + ->expects($this->once()) + ->method('install') + ->with($package); + + $manager->install($operation); + } + + public function testUpdateWithEqualTypes() + { + $installer = $this->createInstallerMock(); + $manager = new InstallationManager(); + $manager->setInstaller('library', $installer); + + $initial = $this->createPackageMock(); + $target = $this->createPackageMock(); + $operation = new UpdateOperation($initial, $target, 'test'); + + $initial + ->expects($this->once()) + ->method('getType') + ->will($this->returnValue('library')); + $target + ->expects($this->once()) + ->method('getType') + ->will($this->returnValue('library')); + + $installer + ->expects($this->once()) + ->method('update') + ->with($initial, $target); + + $manager->update($operation); + } + + public function testUpdateWithNotEqualTypes() + { + $installer1 = $this->createInstallerMock(); + $installer2 = $this->createInstallerMock(); + $manager = new InstallationManager(); + $manager->setInstaller('library', $installer1); + $manager->setInstaller('bundles', $installer2); + + $initial = $this->createPackageMock(); + $target = $this->createPackageMock(); + $operation = new UpdateOperation($initial, $target, 'test'); + + $initial + ->expects($this->once()) + ->method('getType') + ->will($this->returnValue('library')); + $target + ->expects($this->once()) + ->method('getType') + ->will($this->returnValue('bundles')); + + $installer1 + ->expects($this->once()) + ->method('uninstall') + ->with($initial); + + $installer2 + ->expects($this->once()) + ->method('install') + ->with($target); + + $manager->update($operation); + } + + public function testUninstall() + { + $installer = $this->createInstallerMock(); + $manager = new InstallationManager(); + $manager->setInstaller('library', $installer); + + $package = $this->createPackageMock(); + $operation = new UninstallOperation($package, 'test'); + + $package + ->expects($this->once()) + ->method('getType') + ->will($this->returnValue('library')); + + $installer + ->expects($this->once()) + ->method('uninstall') + ->with($package); + + $manager->uninstall($operation); + } + + private function createInstallerMock() + { + return $this->getMockBuilder('Composer\Installer\InstallerInterface') + ->getMock(); + } + + private function createPackageMock() + { + return $this->getMockBuilder('Composer\Package\PackageInterface') + ->getMock(); + } +} diff --git a/tests/Composer/Test/Installer/LibraryInstallerTest.php b/tests/Composer/Test/Installer/LibraryInstallerTest.php index 6069b2cd8..c5be9cbb6 100644 --- a/tests/Composer/Test/Installer/LibraryInstallerTest.php +++ b/tests/Composer/Test/Installer/LibraryInstallerTest.php @@ -50,38 +50,6 @@ class LibraryInstallerTest extends \PHPUnit_Framework_TestCase $library = new LibraryInstaller($file, $this->dm, $this->repository); } - public function testExecuteOperation() - { - $library = $this->getMockBuilder('Composer\Installer\LibraryInstaller') - ->setConstructorArgs(array($this->dir, $this->dm, $this->repository)) - ->setMethods(array('install', 'update', 'uninstall')) - ->getMock(); - - $packageToInstall = $this->createPackageMock(); - $packageToRemove = $this->createPackageMock(); - $packageToUpdate = $this->createPackageMock(); - $updatedPackage = $this->createPackageMock(); - - $library - ->expects($this->once()) - ->method('install') - ->with($packageToInstall); - - $library - ->expects($this->once()) - ->method('uninstall') - ->with($packageToRemove); - - $library - ->expects($this->once()) - ->method('update') - ->with($packageToUpdate, $updatedPackage); - - $library->executeOperation(new Operation\InstallOperation($packageToInstall)); - $library->executeOperation(new Operation\UninstallOperation($packageToRemove)); - $library->executeOperation(new Operation\UpdateOperation($packageToUpdate, $updatedPackage)); - } - public function testIsInstalled() { $library = new LibraryInstaller($this->dir, $this->dm, $this->repository); From 28d9df7da674c3d41351905a7138cf9d90e4d3c4 Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 20:57:58 +0300 Subject: [PATCH 29/41] Added hasPackage and removePackage methods to the ArrayRepository --- src/Composer/Repository/ArrayRepository.php | 38 +++++++++++++++++++ .../Test/Repository/ArrayRepositoryTest.php | 28 +++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/ArrayRepository.php b/src/Composer/Repository/ArrayRepository.php index fb7025e9f..fcffc72af 100644 --- a/src/Composer/Repository/ArrayRepository.php +++ b/src/Composer/Repository/ArrayRepository.php @@ -23,6 +23,26 @@ class ArrayRepository implements RepositoryInterface { protected $packages; + /** + * Checks if specified package in this repository. + * + * @param PackageInterface $package package instance + * + * @return Boolean + */ + public function hasPackage(PackageInterface $package) + { + $packageId = $package->getUniqueName(); + + foreach ($this->getPackages() as $repoPackage) { + if ($packageId === $repoPackage->getUniqueName()) { + return true; + } + } + + return false; + } + /** * Adds a new package to the repository * @@ -37,6 +57,24 @@ class ArrayRepository implements RepositoryInterface $this->packages[] = $package; } + /** + * Removes package from repository. + * + * @param PackageInterface $package package instance + */ + public function removePackage(PackageInterface $package) + { + $packageId = $package->getUniqueName(); + + foreach ($this->getPackages() as $key => $repoPackage) { + if ($packageId === $repoPackage->getUniqueName()) { + array_splice($this->packages, $key, 1); + + return; + } + } + } + /** * Returns all contained packages * diff --git a/tests/Composer/Test/Repository/ArrayRepositoryTest.php b/tests/Composer/Test/Repository/ArrayRepositoryTest.php index 7c9f64947..64e62fff2 100644 --- a/tests/Composer/Test/Repository/ArrayRepositoryTest.php +++ b/tests/Composer/Test/Repository/ArrayRepositoryTest.php @@ -17,11 +17,37 @@ use Composer\Package\MemoryPackage; class ArrayRepositoryTest extends \PHPUnit_Framework_TestCase { - public function testAddLiteral() + public function testAddPackage() { $repo = new ArrayRepository; $repo->addPackage(new MemoryPackage('foo', '1')); $this->assertEquals(1, count($repo)); } + + public function testRemovePackage() + { + $package = new MemoryPackage('bar', '2'); + + $repo = new ArrayRepository; + $repo->addPackage(new MemoryPackage('foo', '1')); + $repo->addPackage($package); + + $this->assertEquals(2, count($repo)); + + $repo->removePackage(new MemoryPackage('foo', '1')); + + $this->assertEquals(1, count($repo)); + $this->assertEquals(array($package), $repo->getPackages()); + } + + public function testHasPackage() + { + $repo = new ArrayRepository; + $repo->addPackage(new MemoryPackage('foo', '1')); + $repo->addPackage(new MemoryPackage('bar', '2')); + + $this->assertTrue($repo->hasPackage(new MemoryPackage('foo', '1'))); + $this->assertFalse($repo->hasPackage(new MemoryPackage('bar', '1'))); + } } From 5c841187fd5221eaf8fe4581a3fa7b082dd88180 Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 20:58:35 +0300 Subject: [PATCH 30/41] Fixed RepositoryInterface --- src/Composer/Repository/RepositoryInterface.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Composer/Repository/RepositoryInterface.php b/src/Composer/Repository/RepositoryInterface.php index 9684492da..96c06656f 100644 --- a/src/Composer/Repository/RepositoryInterface.php +++ b/src/Composer/Repository/RepositoryInterface.php @@ -22,11 +22,6 @@ use Composer\Package\PackageInterface; */ interface RepositoryInterface extends \Countable { - /** - * Initializes repository (reads file, opens connection). - */ - function initialize(); - /** * Checks if specified package registered (installed). * From 20318f77a034e3791239d8d52db319b49a759e5b Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 20:59:10 +0300 Subject: [PATCH 31/41] Refactored FilesystemRegistry to FilesystemRepository --- .../Installer/Registry/FilesystemRegistry.php | 126 -------------- .../Installer/Registry/RegistryInterface.php | 67 -------- .../Repository/FilesystemRepository.php | 82 +++++++++ .../Registry/FilesystemRegistryTest.php | 156 ------------------ .../Repository/FilesystemRepositoryTest.php | 55 ++++++ 5 files changed, 137 insertions(+), 349 deletions(-) delete mode 100644 src/Composer/Installer/Registry/FilesystemRegistry.php delete mode 100644 src/Composer/Installer/Registry/RegistryInterface.php create mode 100644 src/Composer/Repository/FilesystemRepository.php delete mode 100644 tests/Composer/Test/Installer/Registry/FilesystemRegistryTest.php create mode 100644 tests/Composer/Test/Repository/FilesystemRepositoryTest.php diff --git a/src/Composer/Installer/Registry/FilesystemRegistry.php b/src/Composer/Installer/Registry/FilesystemRegistry.php deleted file mode 100644 index 755e034b2..000000000 --- a/src/Composer/Installer/Registry/FilesystemRegistry.php +++ /dev/null @@ -1,126 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Installer\Registry; - -use Composer\Package\PackageInterface; - -/** - * Filesystem registry. - * - * @author Konstantin Kudryashov - */ -class FilesystemRegistry implements RegistryInterface -{ - private $registryFile; - private $registry = array(); - - /** - * Initializes filesystem registry. - * - * @param string $group registry (installer) group - */ - public function __construct($composerCachePath, $group) - { - $this->registryFile = rtrim($composerCachePath, '/').'/'.$group.'-reg.json'; - $registryPath = dirname($this->registryFile); - - if (!is_dir($registryPath)) { - if (file_exists($registryPath)) { - throw new \UnexpectedValueException( - $registryPath.' exists and is not a directory.' - ); - } - if (!mkdir($registryPath, 0777, true)) { - throw new \UnexpectedValueException( - $registryPath.' does not exist and could not be created.' - ); - } - } - } - - /** - * Opens registry (read file / opens connection). - */ - public function open() - { - if (is_file($this->registryFile)) { - $this->registry = json_decode(file_get_contents($this->registryFile), true); - } - } - - /** - * Closes registry (writes file / closes connection). - */ - public function close() - { - file_put_contents($this->registryFile, json_encode($this->registry)); - } - - /** - * Checks if specified package registered (installed). - * - * @param PackageInterface $package package instance - * - * @return Boolean - */ - public function isPackageRegistered(PackageInterface $package) - { - $packageId = $package->getUniqueName(); - - return isset($this->registry[$packageId]); - } - - /** - * Returns installer type for the registered package. - * - * @param PackageInterface $package package instance - * - * @return string - */ - public function getRegisteredPackageInstallerType(PackageInterface $package) - { - $packageId = $package->getUniqueName(); - - if (isset($this->registry[$packageId])) { - return $this->registry[$packageId]; - } - } - - /** - * Registers package in registry. - * - * @param PackageInterface $package package instance - * @param string $type installer type with which package were been - * installed - */ - public function registerPackage(PackageInterface $package, $type) - { - $packageId = $package->getUniqueName(); - - $this->registry[$packageId] = $type; - } - - /** - * Removes package from registry. - * - * @param PackageInterface $package package instance - */ - public function unregisterPackage(PackageInterface $package) - { - $packageId = $package->getUniqueName(); - - if (isset($this->registry[$packageId])) { - unset($this->registry[$packageId]); - } - } -} diff --git a/src/Composer/Installer/Registry/RegistryInterface.php b/src/Composer/Installer/Registry/RegistryInterface.php deleted file mode 100644 index 7fdf63f8a..000000000 --- a/src/Composer/Installer/Registry/RegistryInterface.php +++ /dev/null @@ -1,67 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Installer\Registry; - -use Composer\Package\PackageInterface; - -/** - * Installer registry interface. - * - * @author Konstantin Kudryashov - */ -interface RegistryInterface -{ - /** - * Opens registry (read file / opens connection). - */ - function open(); - - /** - * Closes registry (writes file / closes connection). - */ - function close(); - - /** - * Checks if specified package registered (installed). - * - * @param PackageInterface $package package instance - * - * @return Boolean - */ - function isPackageRegistered(PackageInterface $package); - - /** - * Returns installer type for the registered package. - * - * @param PackageInterface $package package instance - * - * @return string - */ - function getRegisteredPackageInstallerType(PackageInterface $package); - - /** - * Registers package in registry. - * - * @param PackageInterface $package package instance - * @param string $type installer type with which package were been - * installed - */ - function registerPackage(PackageInterface $package, $type); - - /** - * Removes package from registry. - * - * @param PackageInterface $package package instance - */ - function unregisterPackage(PackageInterface $package); -} diff --git a/src/Composer/Repository/FilesystemRepository.php b/src/Composer/Repository/FilesystemRepository.php new file mode 100644 index 000000000..e5de4e5cb --- /dev/null +++ b/src/Composer/Repository/FilesystemRepository.php @@ -0,0 +1,82 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\Package\PackageInterface; +use Composer\Package\Loader\ArrayLoader; +use Composer\Package\Dumper\ArrayDumper; + +/** + * Filesystem repository. + * + * @author Konstantin Kudryashov + */ +class FilesystemRepository extends ArrayRepository implements WritableRepositoryInterface +{ + private $file; + + /** + * Initializes filesystem repository. + * + * @param string $group registry (installer) group + */ + public function __construct($repositoryFile) + { + $this->file = $repositoryFile; + $path = dirname($this->file); + + if (!is_dir($path)) { + if (file_exists($path)) { + throw new \UnexpectedValueException( + $path.' exists and is not a directory.' + ); + } + if (!mkdir($path, 0777, true)) { + throw new \UnexpectedValueException( + $path.' does not exist and could not be created.' + ); + } + } + } + + /** + * Initializes repository (reads file, or remote address). + */ + protected function initialize() + { + parent::initialize(); + + $packages = @json_decode(file_get_contents($this->file), true); + + if (is_array($packages)) { + $loader = new ArrayLoader(); + foreach ($packages as $package) { + $this->addPackage($loader->load($package)); + } + } + } + + /** + * Writes writable repository. + */ + public function write() + { + $packages = array(); + $dumper = new ArrayDumper(); + foreach ($this->getPackages() as $package) { + $packages[] = $dumper->dump($package); + } + + file_put_contents($this->file, json_encode($packages)); + } +} diff --git a/tests/Composer/Test/Installer/Registry/FilesystemRegistryTest.php b/tests/Composer/Test/Installer/Registry/FilesystemRegistryTest.php deleted file mode 100644 index d3aaf7fc6..000000000 --- a/tests/Composer/Test/Installer/Registry/FilesystemRegistryTest.php +++ /dev/null @@ -1,156 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Test\Installer\Registry; - -use Composer\Installer\Registry\FilesystemRegistry; - -class FilesystemRegistryTest extends \PHPUnit_Framework_TestCase -{ - private $dir; - private $registryFile; - - protected function setUp() - { - $this->dir = sys_get_temp_dir().'/.composer'; - $this->registryFile = $this->dir.'/some_registry-reg.json'; - - if (file_exists($this->registryFile)) { - unlink($this->registryFile); - } - } - - public function testRegistryCreation() - { - $this->assertFileNotExists($this->registryFile); - $registry = new FilesystemRegistry($this->dir, 'some_registry'); - - $registry->open(); - $registry->close(); - $this->assertFileExists($this->registryFile); - - file_put_contents($this->registryFile, json_encode(array( - 'package1-1.0.0-beta' => 'library' - ))); - - $registry->open(); - $registry->close(); - $this->assertFileExists($this->registryFile); - - $data = json_decode(file_get_contents($this->registryFile), true); - $this->assertEquals(array('package1-1.0.0-beta' => 'library'), $data); - } - - public function testIsPackageRegistered() - { - file_put_contents($this->registryFile, json_encode(array( - 'package1-1.0.0-beta' => 'library' - ))); - - $registry = new FilesystemRegistry($this->dir, 'some_registry'); - $registry->open(); - - $package1 = $this->createPackageMock(); - $package1 - ->expects($this->once()) - ->method('getUniqueName') - ->will($this->returnValue('package1-1.0.0-beta')); - $package2 = $this->createPackageMock(); - $package2 - ->expects($this->once()) - ->method('getUniqueName') - ->will($this->returnValue('package2-1.1.0-stable')); - - $this->assertTrue($registry->isPackageRegistered($package1)); - $this->assertFalse($registry->isPackageRegistered($package2)); - - $registry->close(); - } - - public function testGetRegisteredPackageInstallerType() - { - $package1 = $this->createPackageMock(); - $package1 - ->expects($this->once()) - ->method('getUniqueName') - ->will($this->returnValue('package1-1.0.0-beta')); - $package2 = $this->createPackageMock(); - $package2 - ->expects($this->once()) - ->method('getUniqueName') - ->will($this->returnValue('package2-1.1.0-stable')); - - file_put_contents($this->registryFile, json_encode(array( - 'package1-1.0.0-beta' => 'library', - 'package2-1.1.0-stable' => 'bundle' - ))); - - $registry = new FilesystemRegistry($this->dir, 'some_registry'); - $registry->open(); - - $this->assertSame('library', $registry->getRegisteredPackageInstallerType($package1)); - $this->assertSame('bundle', $registry->getRegisteredPackageInstallerType($package2)); - - $registry->close(); - } - - public function testRegisterPackage() - { - $package = $this->createPackageMock(); - $package - ->expects($this->once()) - ->method('getUniqueName') - ->will($this->returnValue('package1-1.0.0-beta')); - - $registry = new FilesystemRegistry($this->dir, 'some_registry'); - $registry->open(); - - $registry->registerPackage($package, 'library'); - - $registry->close(); - - $data = json_decode(file_get_contents($this->registryFile), true); - - $this->assertEquals(array('package1-1.0.0-beta' => 'library'), $data); - } - - public function testUnregisterPackage() - { - $package = $this->createPackageMock(); - $package - ->expects($this->once()) - ->method('getUniqueName') - ->will($this->returnValue('package1-1.0.0-beta')); - - file_put_contents($this->registryFile, json_encode(array( - 'package1-1.0.0-beta' => 'library', - 'package2-1.1.0-stable' => 'bundle' - ))); - - $registry = new FilesystemRegistry($this->dir, 'some_registry'); - $registry->open(); - - $registry->unregisterPackage($package); - - $registry->close(); - - $data = json_decode(file_get_contents($this->registryFile), true); - - $this->assertEquals(array('package2-1.1.0-stable' => 'bundle'), $data); - } - - private function createPackageMock() - { - return $this->getMockBuilder('Composer\Package\PackageInterface') - ->getMock(); - } -} diff --git a/tests/Composer/Test/Repository/FilesystemRepositoryTest.php b/tests/Composer/Test/Repository/FilesystemRepositoryTest.php new file mode 100644 index 000000000..ee777b52f --- /dev/null +++ b/tests/Composer/Test/Repository/FilesystemRepositoryTest.php @@ -0,0 +1,55 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\Repository\FilesystemRepository; + +class FilesystemRepositoryTest extends \PHPUnit_Framework_TestCase +{ + private $dir; + private $repositoryFile; + + protected function setUp() + { + $this->dir = sys_get_temp_dir().'/.composer'; + $this->repositoryFile = $this->dir.'/some_registry-reg.json'; + + if (file_exists($this->repositoryFile)) { + unlink($this->repositoryFile); + } + } + + public function testRepositoryReadWrite() + { + $this->assertFileNotExists($this->repositoryFile); + $repository = new FilesystemRepository($this->repositoryFile); + + $repository->getPackages(); + $repository->write(); + $this->assertFileExists($this->repositoryFile); + + file_put_contents($this->repositoryFile, json_encode(array( + array('name' => 'package1', 'version' => '1.0.0-beta', 'type' => 'vendor') + ))); + + $repository = new FilesystemRepository($this->repositoryFile); + $repository->getPackages(); + $repository->write(); + $this->assertFileExists($this->repositoryFile); + + $data = json_decode(file_get_contents($this->repositoryFile), true); + $this->assertEquals(array( + array('name' => 'package1', 'type' => 'vendor', 'version' => '1.0.0', 'releaseType' => 'beta', 'names' => array('package1')) + ), $data); + } +} From 5890b05eb07d1b7a4a0973f22d23501fd23ab18b Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 20:59:40 +0300 Subject: [PATCH 32/41] Implemented ArrayDumper --- src/Composer/Package/Dumper/ArrayDumper.php | 58 +++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/Composer/Package/Dumper/ArrayDumper.php diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php new file mode 100644 index 000000000..d13482264 --- /dev/null +++ b/src/Composer/Package/Dumper/ArrayDumper.php @@ -0,0 +1,58 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Dumper; + +use Composer\Package\PackageInterface; + +/** + * @author Konstantin Kudryashiv + */ +class ArrayDumper +{ + public function dump(PackageInterface $package) + { + $keys = array( + 'type', + 'names', + 'extra', + 'installationSource', + 'sourceType', + 'sourceUrl', + 'distType', + 'distUrl', + 'distSha1Checksum', + 'releaseType', + 'version', + 'license', + 'requires', + 'conflicts', + 'provides', + 'replaces', + 'recommends', + 'suggests' + ); + + $data = array(); + $data['name'] = $package->getPrettyName(); + foreach ($keys as $key) { + $getter = 'get'.ucfirst($key); + $value = $package->$getter(); + + if (null !== $value && !(is_array($value) && 0 === count($value))) { + $data[$key] = $value; + } + } + + return $data; + } +} From 3c439ce41c781159f588b400202c58f412227c58 Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 20:59:54 +0300 Subject: [PATCH 33/41] Added "type" field validator to ArrayLoader --- src/Composer/Package/Loader/ArrayLoader.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 83a6e2051..2f5ffad74 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -110,5 +110,8 @@ class ArrayLoader if (!isset($config['version'])) { throw new \UnexpectedValueException('version is required for package'); } + if (!isset($config['type'])) { + throw new \UnexpectedValueException('type is required for package'); + } } } From 0694f5217a5ac434352058f1978d162bb6d47a0d Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 21:00:05 +0300 Subject: [PATCH 34/41] Implemented RepositoryManager --- src/Composer/Repository/RepositoryManager.php | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/Composer/Repository/RepositoryManager.php diff --git a/src/Composer/Repository/RepositoryManager.php b/src/Composer/Repository/RepositoryManager.php new file mode 100644 index 000000000..7d9022b2f --- /dev/null +++ b/src/Composer/Repository/RepositoryManager.php @@ -0,0 +1,83 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +/** + * Repositories manager. + * + * @author Konstantin Kudryashov + */ +class RepositoryManager +{ + private $localRepository; + private $repositories = array(); + + /** + * Sets repository with specific name. + * + * @param string $type repository name + * @param RepositoryInterface $repository repository instance + */ + public function setRepository($type, RepositoryInterface $repository) + { + $this->repositories[$type] = $repository; + } + + /** + * Returns repository for a specific installation type. + * + * @param string $type installation type + * + * @return RepositoryInterface + * + * @throws InvalidArgumentException if repository for provided type is not registeterd + */ + public function getRepository($type) + { + if (!isset($this->repositories[$type])) { + throw new \InvalidArgumentException('Repository is not registered: '.$type); + } + + return $this->repositories[$type]; + } + + /** + * Returns all repositories, except local one. + * + * @return array + */ + public function getRepositories() + { + return $this->repositories; + } + + /** + * Sets local repository for the project. + * + * @param RepositoryInterface $repository repository instance + */ + public function setLocalRepository(RepositoryInterface $repository) + { + $this->localRepository = $repository; + } + + /** + * Returns local repository for the project. + * + * @return RepositoryInterface + */ + public function getLocalRepository() + { + return $this->localRepository; + } +} From ef71836f30b9844fe7dfa7281e4763287398434e Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 21:00:26 +0300 Subject: [PATCH 35/41] Refactored Composer class to be service container --- src/Composer/Composer.php | 80 ++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/src/Composer/Composer.php b/src/Composer/Composer.php index a5ca2a56e..421b4be9f 100644 --- a/src/Composer/Composer.php +++ b/src/Composer/Composer.php @@ -12,8 +12,11 @@ namespace Composer; -use Composer\Installer\InstallerInterface; -use Composer\Repository\RepositoryInterface; +use Composer\Package\PackageInterface; +use Composer\Package\PackageLock; +use Composer\Repository\RepositoryManager; +use Composer\Installer\InstallationManager; +use Composer\Downloader\DownloadManager; /** * @author Jordi Boggiano @@ -23,51 +26,60 @@ class Composer { const VERSION = '1.0.0-DEV'; - private $repositories = array(); - private $installers = array(); + private $package; + private $lock; - public function setInstaller($type, InstallerInterface $installer = null) + private $rm; + private $dm; + private $im; + + public function setPackage(PackageInterface $package) { - if (null === $installer) { - unset($this->installers[$type]); - - return; - } - - $this->installers[$type] = $installer; + $this->package = $package; } - public function getInstaller($type) + public function getPackage() { - if (!isset($this->installers[$type])) { - throw new \UnexpectedValueException('Unknown dependency type: '.$type); - } - - return $this->installers[$type]; + return $this->package; } - public function setRepository($name, RepositoryInterface $repository = null) + public function setPackageLock($lock) { - if (null === $repository) { - unset($this->repositories[$name]); - - return; - } - - $this->repositories[$name] = $repository; + $this->lock = $lock; } - public function getRepository($name) + public function getPackageLock() { - if (!isset($this->repositories[$name])) { - throw new \UnexpectedValueException('Unknown repository: '.$name); - } - - return $this->repositories[$name]; + return $this->lock; } - public function getRepositories() + public function setRepositoryManager(RepositoryManager $manager) { - return $this->repositories; + $this->rm = $manager; + } + + public function getRepositoryManager() + { + return $this->rm; + } + + public function setDownloadManager(DownloadManager $manager) + { + $this->dm = $manager; + } + + public function getDownloadManager() + { + return $this->dm; + } + + public function setInstallationManager(InstallationManager $manager) + { + $this->im = $manager; + } + + public function getInstallationManager() + { + return $this->im; } } From 05ddfde868b24e4c72fd00caaf0eb1b024a4bae7 Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 21:00:44 +0300 Subject: [PATCH 36/41] Reflect latest refactoring changes in commands --- bin/composer | 37 +++++++++++++++---------- src/Composer/Command/Command.php | 29 ------------------- src/Composer/Command/InstallCommand.php | 37 ++++++++++++++----------- src/Composer/Console/Application.php | 22 +-------------- 4 files changed, 45 insertions(+), 80 deletions(-) diff --git a/bin/composer b/bin/composer index 12fbab8d6..06d40f904 100755 --- a/bin/composer +++ b/bin/composer @@ -10,25 +10,34 @@ use Composer\Repository; use Composer\Package; use Composer\Console\Application as ComposerApplication; -// initialize installer -$installer = new Installer\LibraryInstaller('vendor', $preferSource = true); -$installer->setDownloader('git', new Downloader\GitDownloader()); -$installer->setDownloader('pear', new Downloader\PearDownloader()); -$installer->setDownloader('zip', new Downloader\ZipDownloader()); +// initialize repository manager +$rm = new Repository\RepositoryManager(); +$rm->setLocalRepository(new Repository\FilesystemRepository('.composer/installed.json')); +$rm->setRepository('Platform', new Repository\PlatformRepository()); +$rm->setRepository('Packagist', new Repository\ComposerRepository('http://packagist.org')); -// initialize composer -$composer = new Composer(); -$composer->setInstaller('library', $installer); -$composer->setRepository('Platform', new Repository\PlatformRepository()); -$composer->setRepository('Packagist', new Repository\ComposerRepository('http://packagist.org')); +// initialize download manager +$dm = new Downloader\DownloadManager($preferSource = false); +$dm->setDownloader('git', new Downloader\GitDownloader()); +$dm->setDownloader('pear', new Downloader\PearDownloader()); +$dm->setDownloader('zip', new Downloader\ZipDownloader()); -// initialize package +// initialize installation manager +$im = new Installer\InstallationManager(); +$im->setInstaller('vendor', new Installer\LibraryInstaller('vendor', $dm, $rm->getLocalRepository())); + +// load package $loader = new Package\Loader\JsonLoader(); $package = $loader->load('composer.json'); -// initialize lock -$packageLock = new Package\PackageLock('composer.lock'); +// initialize composer +$composer = new Composer(); +$composer->setPackage($package); +$composer->setPackageLock(new Package\PackageLock('composer.lock')); +$composer->setRepositoryManager($rm); +$composer->setDownloadManager($dm); +$composer->setInstallationManager($im); // run the command application -$application = new ComposerApplication($composer, $package, $packageLock); +$application = new ComposerApplication($composer); $application->run(); diff --git a/src/Composer/Command/Command.php b/src/Composer/Command/Command.php index 54f44c9d3..946d775b8 100644 --- a/src/Composer/Command/Command.php +++ b/src/Composer/Command/Command.php @@ -34,33 +34,4 @@ abstract class Command extends BaseCommand { return $this->getApplication()->getComposer(); } - - /** - * @return \Composer\Package\PackageInterface - */ - protected function getPackage() - { - return $this->getApplication()->getPackage(); - } - - /** - * @return \Composer\Package\PackageLock - */ - protected function getLock() - { - return $this->getApplication()->getLock(); - } - - protected function solveDependencies(Request $request, Solver $solver) - { - $operations = array(); - foreach ($solver->solve($request) as $task) { - $installer = $this->getComposer()->getInstaller($task['package']->getType()); - $operation = new Operation($installer, $task['job'], $task['package']); - - $operations[] = $operation; - } - - return $operations; - } } diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index b2e41a522..d93ef434d 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -15,6 +15,7 @@ namespace Composer\Command; use Composer\DependencyResolver; use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; +use Composer\DependencyResolver\Operation; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -44,13 +45,16 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { - if ($this->getLock()->isLocked()) { + $composer = $this->getComposer(); + + if ($composer->getPackageLock()->isLocked()) { $output->writeln('Found lockfile. Reading'); - foreach ($this->getLock()->getLockedPackages() as $package) { - $installer = $this->getComposer()->getInstaller($package->getType()); - if (!$installer->isInstalled($package)) { - $installer->install($package); + $installationManager = $composer->getInstallationManager(); + foreach ($composer->getPackageLock()->getLockedPackages() as $package) { + if (!$installationManager->isPackageInstalled($package)) { + $operation = new Operation\InstallOperation($package, 'lock resolving'); + $installationManager->execute($operation); } } @@ -59,7 +63,7 @@ EOT // creating repository pool $pool = new Pool; - foreach ($this->getComposer()->getRepositories() as $repository) { + foreach ($composer->getRepositoryManager()->getRepositories() as $repository) { $pool->addRepository($repository); } @@ -70,22 +74,23 @@ EOT } // prepare solver - $platform = $this->getComposer()->getRepository('Platform'); - $policy = new DependencyResolver\DefaultPolicy(); - $solver = new DependencyResolver\Solver($policy, $pool, $platform); + $installationManager = $composer->getInstallationManager(); + $localRepo = $composer->getRepositoryManager()->getLocalRepository(); + $policy = new DependencyResolver\DefaultPolicy(); + $solver = new DependencyResolver\Solver($policy, $pool, $localRepo); // solve dependencies and execute operations - $operations = $this->solveDependencies($request, $solver); - foreach ($operations as $operation) { - $operation->execute(); - // TODO: collect installable packages into $installed + foreach ($solver->solve($request) as $operation) { + $installationManager->execute($operation); } - $output->writeln('> Done'); - if (false) { - $config->lock($installed); + $composer->getPackageLock()->lock($localRepo->getPackages()); $output->writeln('> Locked'); } + + $localRepo->write(); + + $output->writeln('> Done'); } } diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index c61a8cdf2..c792cdd5a 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -29,16 +29,12 @@ use Composer\Package\PackageLock; class Application extends BaseApplication { private $composer; - private $package; - private $lock; - public function __construct(Composer $composer, PackageInterface $package, PackageLock $lock) + public function __construct(Composer $composer) { parent::__construct('Composer', Composer::VERSION); $this->composer = $composer; - $this->package = $package; - $this->lock = $lock; } /** @@ -64,22 +60,6 @@ class Application extends BaseApplication return $this->composer; } - /** - * @return PackageInterface - */ - public function getPackage() - { - return $this->package; - } - - /** - * @return PackageLock - */ - public function getLock() - { - return $this->lock; - } - /** * Looks for all *Command files in Composer's Command directory */ From c9ecb0d1ad8cf400be7129f82ff05539ea215590 Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 25 Sep 2011 21:00:59 +0300 Subject: [PATCH 37/41] Remove unused methods from downloaders --- src/Composer/Downloader/GitDownloader.php | 7 ------- src/Composer/Downloader/PearDownloader.php | 7 ------- src/Composer/Downloader/ZipDownloader.php | 7 ------- 3 files changed, 21 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index bb22bc0be..9b5b036df 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -34,11 +34,4 @@ class GitDownloader implements DownloaderInterface system('git archive --format=tar --prefix='.escapeshellarg($package->getName()).' --remote='.escapeshellarg($url).' master | tar -xf -'); } } - - public function isDownloaded(PackageInterface $package, $path) - { - $targetPath = $path . '/' . $package->getName(); - - return is_dir($targetPath); - } } diff --git a/src/Composer/Downloader/PearDownloader.php b/src/Composer/Downloader/PearDownloader.php index c93519b1c..0f4c86289 100644 --- a/src/Composer/Downloader/PearDownloader.php +++ b/src/Composer/Downloader/PearDownloader.php @@ -66,11 +66,4 @@ class PearDownloader implements DownloaderInterface } chdir($cwd); } - - public function isDownloaded(PackageInterface $package, $path) - { - $targetPath = $path . '/' . $package->getName(); - - return is_dir($targetPath); - } } diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php index 098ba151a..94a7eac0e 100644 --- a/src/Composer/Downloader/ZipDownloader.php +++ b/src/Composer/Downloader/ZipDownloader.php @@ -73,11 +73,4 @@ class ZipDownloader implements DownloaderInterface throw new \UnexpectedValueException($zipName.' is not a valid zip archive, got error code '.$retval); } } - - public function isDownloaded(PackageInterface $package, $path) - { - $targetPath = $path . '/' . $package->getName(); - - return is_dir($targetPath); - } } From 7cf86e7ea043c4fadab0ac3480bcc667e8156bae Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 25 Sep 2011 12:38:54 +0200 Subject: [PATCH 38/41] Fix installation process --- bin/composer | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/composer b/bin/composer index 06d40f904..cfdb05174 100755 --- a/bin/composer +++ b/bin/composer @@ -19,8 +19,8 @@ $rm->setRepository('Packagist', new Repository\ComposerRepository('http://packag // initialize download manager $dm = new Downloader\DownloadManager($preferSource = false); $dm->setDownloader('git', new Downloader\GitDownloader()); -$dm->setDownloader('pear', new Downloader\PearDownloader()); -$dm->setDownloader('zip', new Downloader\ZipDownloader()); +//$dm->setDownloader('pear', new Downloader\PearDownloader()); +//$dm->setDownloader('zip', new Downloader\ZipDownloader()); // initialize installation manager $im = new Installer\InstallationManager(); From c7af918caa5372c2cea8f6ac791a2cf15bb48e84 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 25 Sep 2011 12:39:08 +0200 Subject: [PATCH 39/41] Fix git downloader --- src/Composer/Downloader/GitDownloader.php | 34 ++++++++++++++++------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 9b5b036df..cf732bafd 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -19,19 +19,33 @@ use Composer\Package\PackageInterface; */ class GitDownloader implements DownloaderInterface { - protected $clone; - - public function __construct($clone = true) + /** + * {@inheritDoc} + */ + public function download(PackageInterface $package, $path, $url, $checksum = null, $useSource = false) { - $this->clone = $clone; + system('git clone '.escapeshellarg($url).' -b master '.escapeshellarg($path)); + + // TODO non-source installs: + // system('git archive --format=tar --prefix='.escapeshellarg($package->getName()).' --remote='.escapeshellarg($url).' master | tar -xf -'); } - public function download(PackageInterface $package, $path, $url, $checksum = null) + /** + * {@inheritDoc} + */ + public function update(PackageInterface $initial, PackageInterface $target, $path, $useSource = false) { - if ($this->clone) { - system('git clone '.escapeshellarg($url).' -b master '.escapeshellarg($path.'/'.$package->getName())); - } else { - system('git archive --format=tar --prefix='.escapeshellarg($package->getName()).' --remote='.escapeshellarg($url).' master | tar -xf -'); - } + $cwd = getcwd(); + chdir($path); + system('git pull'); + chdir($cwd); + } + + /** + * {@inheritDoc} + */ + public function remove(PackageInterface $package, $path, $useSource = false) + { + echo 'rm -rf '.$path; // TODO } } From 4fe80d7abb60dafbcea51dd5088a9f8d0f7db967 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 25 Sep 2011 23:05:14 +0200 Subject: [PATCH 40/41] Add WrapperRepository class --- src/Composer/Repository/WrapperRepository.php | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/Composer/Repository/WrapperRepository.php diff --git a/src/Composer/Repository/WrapperRepository.php b/src/Composer/Repository/WrapperRepository.php new file mode 100644 index 000000000..f298cb50b --- /dev/null +++ b/src/Composer/Repository/WrapperRepository.php @@ -0,0 +1,64 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\Package\PackageInterface; + +/** + * @author Jordi Boggiano + */ +class WrapperRepository extends ArrayRepository implements WritableRepositoryInterface +{ + private $repositories; + + public function __construct(array $repositories) + { + $this->repositories = $repositories; + } + + protected function initialize() + { + parent::initialize(); + + foreach ($this->repositories as $repo) { + foreach ($repo->getPackages() as $package) { + $this->packages[] = $package; + } + } + } + + /** + * {@inheritDoc} + */ + public function addPackage(PackageInterface $package) + { + throw new \LogicException('Can not add packages to a wrapper repository'); + } + + /** + * {@inheritDoc} + */ + public function removePackage(PackageInterface $package) + { + throw new \LogicException('Can not remove packages to a wrapper repository'); + } + + public function write() + { + foreach ($this->repositories as $repo) { + if ($repo instanceof WritableRepositoryInterface) { + $repo->write(); + } + } + } +} From 5623f3e69cfa8b3c7083676c3fb1d83ee3294f0c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 25 Sep 2011 23:07:42 +0200 Subject: [PATCH 41/41] Fix refactoring bugs --- bin/composer | 9 ++++--- src/Composer/Command/InstallCommand.php | 3 ++- src/Composer/DependencyResolver/Solver.php | 27 ++++++++++++--------- src/Composer/Package/Loader/ArrayLoader.php | 3 --- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/bin/composer b/bin/composer index cfdb05174..94c5c19b2 100755 --- a/bin/composer +++ b/bin/composer @@ -12,8 +12,11 @@ use Composer\Console\Application as ComposerApplication; // initialize repository manager $rm = new Repository\RepositoryManager(); -$rm->setLocalRepository(new Repository\FilesystemRepository('.composer/installed.json')); -$rm->setRepository('Platform', new Repository\PlatformRepository()); +$localRepository = new Repository\WrapperRepository(array( + new Repository\ArrayRepository('.composer/installed.json'), + new Repository\PlatformRepository(), +)); +$rm->setLocalRepository($localRepository); $rm->setRepository('Packagist', new Repository\ComposerRepository('http://packagist.org')); // initialize download manager @@ -24,7 +27,7 @@ $dm->setDownloader('git', new Downloader\GitDownloader()); // initialize installation manager $im = new Installer\InstallationManager(); -$im->setInstaller('vendor', new Installer\LibraryInstaller('vendor', $dm, $rm->getLocalRepository())); +$im->setInstaller('library', new Installer\LibraryInstaller('vendor', $dm, $rm->getLocalRepository())); // load package $loader = new Package\Loader\JsonLoader(); diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index d93ef434d..3e63318df 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -63,13 +63,14 @@ EOT // creating repository pool $pool = new Pool; + $pool->addRepository($composer->getRepositoryManager()->getLocalRepository()); foreach ($composer->getRepositoryManager()->getRepositories() as $repository) { $pool->addRepository($repository); } // creating requirements request $request = new Request($pool); - foreach ($this->getPackage()->getRequires() as $link) { + foreach ($composer->getPackage()->getRequires() as $link) { $request->install($link->getTarget(), $link->getConstraint()); } diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index ab2cf3346..c21918262 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -50,6 +50,7 @@ class Solver protected $watches = array(); protected $removeWatches = array(); protected $decisionMap; + protected $installedPackageMap; protected $packageToUpdateRule = array(); protected $packageToFeatureRule = array(); @@ -252,7 +253,7 @@ class Solver $this->addedMap[$package->getId()] = true; $dontFix = 0; - if ($this->installed === $package->getRepository() && !isset($this->fixMap[$package->getId()])) { + if (isset($this->installedPackageMap[$package->getId()]) && !isset($this->fixMap[$package->getId()])) { $dontFix = 1; } @@ -271,7 +272,7 @@ class Solver if ($dontFix) { $foundInstalled = false; foreach ($possibleRequires as $require) { - if ($this->installed === $require->getRepository()) { + if (isset($this->installedPackageMap[$require->getId()])) { $foundInstalled = true; break; } @@ -294,7 +295,7 @@ class Solver $possibleConflicts = $this->pool->whatProvides($link->getTarget(), $link->getConstraint()); foreach ($possibleConflicts as $conflict) { - if ($dontFix && $this->installed === $conflict->getRepository()) { + if ($dontFix && isset($this->installedPackageMap[$conflict->getId()])) { continue; } @@ -309,7 +310,7 @@ class Solver /** @TODO: if ($this->noInstalledObsoletes) */ if (true) { $noObsoletes = isset($this->noObsoletes[$package->getId()]); - $isInstalled = ($this->installed === $package->getRepository()); + $isInstalled = (isset($this->installedPackageMap[$package->getId()])); foreach ($package->getReplaces() as $link) { $obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint()); @@ -758,7 +759,7 @@ class Solver switch ($job['cmd']) { case 'install': foreach ($job['packages'] as $package) { - if ($this->installed === $package->getRepository()) { + if (isset($this->installedPackageMap[$package->getId()])) { $disableQueue[] = array('type' => 'update', 'package' => $package); } @@ -871,7 +872,7 @@ class Solver case 'remove': foreach ($job['packages'] as $package) { - if ($this->installed === $package->getRepository()) { + if (isset($this->installedPackageMap[$package->getId()])) { $disableQueue[] = array('type' => 'update', 'package' => $package); } } @@ -933,6 +934,10 @@ class Solver { $this->jobs = $request->getJobs(); $installedPackages = $this->installed->getPackages(); + $this->installedPackageMap = array(); + foreach ($installedPackages as $package) { + $this->installedPackageMap[$package->getId()] = $package; + } $this->decisionMap = new \SplFixedArray($this->pool->getMaxId() + 1); @@ -954,12 +959,12 @@ class Solver foreach ($job['packages'] as $package) { switch ($job['cmd']) { case 'fix': - if ($this->installed === $package->getRepository()) { + if (isset($this->installedPackageMap[$package->getId()])) { $this->fixMap[$package->getId()] = true; } break; case 'update': - if ($this->installed === $package->getRepository()) { + if (isset($this->installedPackageMap[$package->getId()])) { $this->updateMap[$package->getId()] = true; } break; @@ -1039,7 +1044,7 @@ class Solver break; case 'lock': foreach ($job['packages'] as $package) { - if ($this->installed === $package->getRepository()) { + if (isset($this->installedPackageMap[$package->getId()])) { $rule = $this->createInstallRule($package, self::RULE_JOB_LOCK); } else { $rule = $this->createRemoveRule($package, self::RULE_JOB_LOCK); @@ -1083,7 +1088,7 @@ class Solver $package = $literal->getPackage(); // !wanted & installed - if (!$literal->isWanted() && $this->installed === $package->getRepository()) { + if (!$literal->isWanted() && isset($this->installedPackageMap[$package->getId()])) { $updateRule = $this->packageToUpdateRule[$package->getId()]; foreach ($updateRule->getLiterals() as $updateLiteral) { @@ -1098,7 +1103,7 @@ class Solver $package = $literal->getPackage(); // wanted & installed || !wanted & !installed - if ($literal->isWanted() == ($this->installed === $package->getRepository())) { + if ($literal->isWanted() == (isset($this->installedPackageMap[$package->getId()]))) { continue; } diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 2f5ffad74..83a6e2051 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -110,8 +110,5 @@ class ArrayLoader if (!isset($config['version'])) { throw new \UnexpectedValueException('version is required for package'); } - if (!isset($config['type'])) { - throw new \UnexpectedValueException('type is required for package'); - } } }