From bbf745f3e6160d08171042630bce31dbb91daf27 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 19 Feb 2012 12:00:14 +0100 Subject: [PATCH 01/57] Add command to install a package as a new project into a non-existant directory. This fetches the given package from packagist or a different packagist-source and installs it into a given path. --- .../Command/InstallProjectCommand.php | 121 ++++++++++++++++++ src/Composer/Console/Application.php | 1 + src/Composer/Installer/ProjectInstaller.php | 111 ++++++++++++++++ 3 files changed, 233 insertions(+) create mode 100644 src/Composer/Command/InstallProjectCommand.php create mode 100644 src/Composer/Installer/ProjectInstaller.php diff --git a/src/Composer/Command/InstallProjectCommand.php b/src/Composer/Command/InstallProjectCommand.php new file mode 100644 index 000000000..3d38ea1a0 --- /dev/null +++ b/src/Composer/Command/InstallProjectCommand.php @@ -0,0 +1,121 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Command; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Output\OutputInterface; +use Composer\IO\IOInterface; +use Composer\Downloader\DownloadManager; +use Composer\Downloader; +use Composer\Repository\ComposerRepository; +use Composer\Installer\ProjectInstaller; + +/** + * Install a package as new project into new directory. + * + * @author Benjamin Eberlei + */ +class InstallProjectCommand extends Command +{ + protected function configure() + { + $this + ->setName('install-project') + ->setDescription('Install a package as new project into given directory.') + ->setDefinition(array( + new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), + new InputOption('packagist-url', null, InputOption::VALUE_REQUIRED, 'Pick a different packagist url to look for the package.'), + new InputArgument('package', InputArgument::REQUIRED), + new InputArgument('version', InputArgument::OPTIONAL), + new InputArgument('directory', InputArgument::OPTIONAL), + )) + ->setHelp(<<install-project command installs a given package into a new directory. +You can use this command to bootstrap new projects or setup a clean installation for +new developers of your project. + +php composer.phar install-project vendor/project intodirectory + +To setup a developer workable version you should create the project using the source +controlled code by appending the '--prefer-source' flag. + +To install a package from another packagist repository than the default one you +can pass the '--packagist-url=http://mypackagist.org' flag. + +EOT + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = $this->getApplication()->getIO(); + + return $this->installProject( + $io, + $input->getArgument('package'), + $input->getArgument('directory'), + $input->getArgument('version'), + (Boolean)$input->getOption('prefer-source'), + $input->getOption('packagist-url') + ); + } + + public function installProject(IOInterface $io, $packageName, $directory = null, $version = null, $preferSource = false, $packagistUrl = null) + { + $dm = $this->createDownloadManager($io); + if ($preferSource) { + $dm->setPreferSource(true); + } + + if ($packagistUrl === null) { + $sourceRepo = new ComposerRepository(array('url' => 'http://packagist.org')); + } else { + $sourceRepo = new ComposerRepository(array('url' => $packagistUrl)); + } + $package = $sourceRepo->findPackage($packageName, $version); + if (!$package) { + throw new \InvalidArgumentException("Could not find package $packageName with version $version."); + } + + if ($directory === null) { + $parts = explode("/", $packageName); + $directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts); + } + + $projectInstaller = new ProjectInstaller($directory, $dm); + $projectInstaller->install($package); + + $io->write('Created new project directory for ' . $package->getName(), true); + $io->write('Next steps:', true); + $io->write('1. cd ' . $directory, true); + $io->write('2. composer install', true); + } + + protected function createDownloadManager(IOInterface $io) + { + $dm = new Downloader\DownloadManager(); + $dm->setDownloader('git', new Downloader\GitDownloader($io)); + $dm->setDownloader('svn', new Downloader\SvnDownloader($io)); + $dm->setDownloader('hg', new Downloader\HgDownloader($io)); + $dm->setDownloader('pear', new Downloader\PearDownloader($io)); + $dm->setDownloader('zip', new Downloader\ZipDownloader($io)); + $dm->setDownloader('tar', new Downloader\TarDownloader($io)); + $dm->setDownloader('phar', new Downloader\PharDownloader($io)); + + return $dm; + } +} + diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index fe6b6cb13..5802e2f86 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -107,6 +107,7 @@ class Application extends BaseApplication $this->add(new Command\DependsCommand()); $this->add(new Command\InitCommand()); $this->add(new Command\InstallCommand()); + $this->add(new Command\InstallProjectCommand()); $this->add(new Command\UpdateCommand()); $this->add(new Command\SearchCommand()); $this->add(new Command\ValidateCommand()); diff --git a/src/Composer/Installer/ProjectInstaller.php b/src/Composer/Installer/ProjectInstaller.php new file mode 100644 index 000000000..da9236a97 --- /dev/null +++ b/src/Composer/Installer/ProjectInstaller.php @@ -0,0 +1,111 @@ + + * 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\DependencyResolver\Operation\OperationInterface; +use Composer\Package\PackageInterface; +use Composer\Downloader\DownloadManager; + +/** + * Project Installer is used to install a single package into a directory as + * root project. + * + * @author Benjamin Eberlei + */ +class ProjectInstaller implements InstallerInterface +{ + private $installPath; + private $downloadManager; + + public function __construct($installPath, DownloadManager $dm) + { + $this->installPath = $installPath; + $this->downloadManager = $dm; + } + + /** + * Decides if the installer supports the given type + * + * @param string $packageType + * @return Boolean + */ + public function supports($packageType) + { + return true; + } + + /** + * Checks that provided package is installed. + * + * @param PackageInterface $package package instance + * + * @return Boolean + */ + public function isInstalled(PackageInterface $package) + { + return false; + } + + /** + * Installs specific package. + * + * @param PackageInterface $package package instance + */ + public function install(PackageInterface $package) + { + $installPath = $this->installPath; + if (file_exists($installPath)) { + throw new \InvalidArgumentException("Project directory $installPath already exists."); + } + if (!file_exists(dirname($installPath))) { + throw new \InvalidArgumentException("Project root " . dirname($installPath) . " does not exist."); + } + mkdir($installPath, 0777); + $this->downloadManager->download($package, $installPath); + } + + /** + * 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) + { + throw new \InvalidArgumentException("not supported"); + } + + /** + * Uninstalls specific package. + * + * @param PackageInterface $package package instance + */ + public function uninstall(PackageInterface $package) + { + throw new \InvalidArgumentException("not supported"); + } + + /** + * Returns the installation path of a package + * + * @param PackageInterface $package + * @return string path + */ + public function getInstallPath(PackageInterface $package) + { + return $this->installPath; + } +} + From 1c67633c7087da6fa3f28e0fae0737e2f423e7ab Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 19 Feb 2012 12:08:38 +0100 Subject: [PATCH 02/57] Refactored downloader code and made packagist configuration flexible and validated. --- .../Command/InstallProjectCommand.php | 22 ++++++++----------- src/Composer/Factory.php | 2 +- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/Composer/Command/InstallProjectCommand.php b/src/Composer/Command/InstallProjectCommand.php index 3d38ea1a0..6d493c5f2 100644 --- a/src/Composer/Command/InstallProjectCommand.php +++ b/src/Composer/Command/InstallProjectCommand.php @@ -17,8 +17,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Output\OutputInterface; use Composer\IO\IOInterface; -use Composer\Downloader\DownloadManager; -use Composer\Downloader; +use Composer\Factory; use Composer\Repository\ComposerRepository; use Composer\Installer\ProjectInstaller; @@ -82,9 +81,14 @@ EOT if ($packagistUrl === null) { $sourceRepo = new ComposerRepository(array('url' => 'http://packagist.org')); - } else { + } else if (substr($packagistUrl, -5) === ".json") { + $sourceRepo = new FilesystemRepository($packagistUrl); + } else if (strpos($packagistUrl, 'http') === 0) { $sourceRepo = new ComposerRepository(array('url' => $packagistUrl)); + } else { + throw new \InvalidArgumentException("Invalid Packagist Url given. Has to be a .json file or an http url."); } + $package = $sourceRepo->findPackage($packageName, $version); if (!$package) { throw new \InvalidArgumentException("Could not find package $packageName with version $version."); @@ -106,16 +110,8 @@ EOT protected function createDownloadManager(IOInterface $io) { - $dm = new Downloader\DownloadManager(); - $dm->setDownloader('git', new Downloader\GitDownloader($io)); - $dm->setDownloader('svn', new Downloader\SvnDownloader($io)); - $dm->setDownloader('hg', new Downloader\HgDownloader($io)); - $dm->setDownloader('pear', new Downloader\PearDownloader($io)); - $dm->setDownloader('zip', new Downloader\ZipDownloader($io)); - $dm->setDownloader('tar', new Downloader\TarDownloader($io)); - $dm->setDownloader('phar', new Downloader\PharDownloader($io)); - - return $dm; + $factory = new Factory(); + return $factory->createDownloadManager($io); } } diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index cf70c4c74..61284acae 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -138,7 +138,7 @@ class Factory $rm->addRepository(new Repository\ComposerRepository(array('url' => 'http://packagist.org'))); } - protected function createDownloadManager(IOInterface $io) + public function createDownloadManager(IOInterface $io) { $dm = new Downloader\DownloadManager(); $dm->setDownloader('git', new Downloader\GitDownloader($io)); From f60fe5622aa1f32ae6eb41b6e138f7e5c87e4174 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 19 Feb 2012 12:15:39 +0100 Subject: [PATCH 03/57] Add missing use stmt. --- src/Composer/Command/InstallProjectCommand.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Command/InstallProjectCommand.php b/src/Composer/Command/InstallProjectCommand.php index 6d493c5f2..48227f8bb 100644 --- a/src/Composer/Command/InstallProjectCommand.php +++ b/src/Composer/Command/InstallProjectCommand.php @@ -19,6 +19,7 @@ use Symfony\Component\Console\Output\OutputInterface; use Composer\IO\IOInterface; use Composer\Factory; use Composer\Repository\ComposerRepository; +use Composer\Repository\FilesystemRepository; use Composer\Installer\ProjectInstaller; /** From 23719b3b1d2d562f1c02eca3c96d4d9496815b66 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 19 Feb 2012 12:17:10 +0100 Subject: [PATCH 04/57] Rename packagist url to repository url --- .../Command/InstallProjectCommand.php | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Composer/Command/InstallProjectCommand.php b/src/Composer/Command/InstallProjectCommand.php index 48227f8bb..c03cfeb0c 100644 --- a/src/Composer/Command/InstallProjectCommand.php +++ b/src/Composer/Command/InstallProjectCommand.php @@ -36,7 +36,7 @@ class InstallProjectCommand extends Command ->setDescription('Install a package as new project into given directory.') ->setDefinition(array( new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), - new InputOption('packagist-url', null, InputOption::VALUE_REQUIRED, 'Pick a different packagist url to look for the package.'), + new InputOption('repository-url', null, InputOption::VALUE_REQUIRED, 'Pick a different repository url to look for the package.'), new InputArgument('package', InputArgument::REQUIRED), new InputArgument('version', InputArgument::OPTIONAL), new InputArgument('directory', InputArgument::OPTIONAL), @@ -51,8 +51,8 @@ new developers of your project. To setup a developer workable version you should create the project using the source controlled code by appending the '--prefer-source' flag. -To install a package from another packagist repository than the default one you -can pass the '--packagist-url=http://mypackagist.org' flag. +To install a package from another repository repository than the default one you +can pass the '--repository-url=http://myrepository.org' flag. EOT ) @@ -69,25 +69,25 @@ EOT $input->getArgument('directory'), $input->getArgument('version'), (Boolean)$input->getOption('prefer-source'), - $input->getOption('packagist-url') + $input->getOption('repository-url') ); } - public function installProject(IOInterface $io, $packageName, $directory = null, $version = null, $preferSource = false, $packagistUrl = null) + public function installProject(IOInterface $io, $packageName, $directory = null, $version = null, $preferSource = false, $repositoryUrl = null) { $dm = $this->createDownloadManager($io); if ($preferSource) { $dm->setPreferSource(true); } - if ($packagistUrl === null) { + if ($repositoryUrl === null) { $sourceRepo = new ComposerRepository(array('url' => 'http://packagist.org')); - } else if (substr($packagistUrl, -5) === ".json") { - $sourceRepo = new FilesystemRepository($packagistUrl); - } else if (strpos($packagistUrl, 'http') === 0) { - $sourceRepo = new ComposerRepository(array('url' => $packagistUrl)); + } else if (substr($repositoryUrl, -5) === ".json") { + $sourceRepo = new FilesystemRepository($repositoryUrl); + } else if (strpos($repositoryUrl, 'http') === 0) { + $sourceRepo = new ComposerRepository(array('url' => $repositoryUrl)); } else { - throw new \InvalidArgumentException("Invalid Packagist Url given. Has to be a .json file or an http url."); + throw new \InvalidArgumentException("Invalid repository url given. Has to be a .json file or an http url."); } $package = $sourceRepo->findPackage($packageName, $version); From 62bdaaf4087cdb2cac3292131438cb80edd79db2 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 19 Feb 2012 12:31:58 +0100 Subject: [PATCH 05/57] Add docs --- doc/create-projects.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 doc/create-projects.md diff --git a/doc/create-projects.md b/doc/create-projects.md new file mode 100644 index 000000000..4a373dbac --- /dev/null +++ b/doc/create-projects.md @@ -0,0 +1,16 @@ +# Create Projects + +You can use Composer to create new projects from existing packages. There are several applications for this: + +1. You can deploy application packages. +2. You can check out any package and start developing on patches for example. +3. Projects with multiple developers can use this feature to bootstrap the initial application for development. + +To create a new project using composer you can use the "install-project", pass it a package name + version and a directory to create the project in. The directory is not allowed to exist, it will be created during installation. + + php composer.phar install-project doctrine/orm 2.2.0 /path/to/new-project + +By default the command checks for the packages on packagist.org. To change this behavior you can use the --repository-url parameter and either point it to an HTTP url for your own packagist repository or to a packages.json file. + +If you want to get a development version of the code directly checked out from version control you have to add the --prefer-source parameter. + From db64917a83bdd0ff56f3f7d75cc7aac736b14d89 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 19 Feb 2012 12:42:00 +0100 Subject: [PATCH 06/57] Renamed install-project to create-project to differentiate from just "install". --- doc/create-projects.md | 6 +++--- ...rojectCommand.php => CreateProjectCommand.php} | 15 ++++++++------- src/Composer/Console/Application.php | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) rename src/Composer/Command/{InstallProjectCommand.php => CreateProjectCommand.php} (89%) diff --git a/doc/create-projects.md b/doc/create-projects.md index 4a373dbac..0cf187959 100644 --- a/doc/create-projects.md +++ b/doc/create-projects.md @@ -1,14 +1,14 @@ # Create Projects -You can use Composer to create new projects from existing packages. There are several applications for this: +You can use Composer to create new projects from an existing package. There are several applications for this: 1. You can deploy application packages. 2. You can check out any package and start developing on patches for example. 3. Projects with multiple developers can use this feature to bootstrap the initial application for development. -To create a new project using composer you can use the "install-project", pass it a package name + version and a directory to create the project in. The directory is not allowed to exist, it will be created during installation. +To create a new project using composer you can use the "create-project", pass it a package name + version and a directory to create the project in. The directory is not allowed to exist, it will be created during installation. - php composer.phar install-project doctrine/orm 2.2.0 /path/to/new-project + php composer.phar create-project doctrine/orm 2.2.0 /path/to/new-project By default the command checks for the packages on packagist.org. To change this behavior you can use the --repository-url parameter and either point it to an HTTP url for your own packagist repository or to a packages.json file. diff --git a/src/Composer/Command/InstallProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php similarity index 89% rename from src/Composer/Command/InstallProjectCommand.php rename to src/Composer/Command/CreateProjectCommand.php index c03cfeb0c..b1d1c9854 100644 --- a/src/Composer/Command/InstallProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -27,13 +27,13 @@ use Composer\Installer\ProjectInstaller; * * @author Benjamin Eberlei */ -class InstallProjectCommand extends Command +class CreateProjectCommand extends Command { protected function configure() { $this - ->setName('install-project') - ->setDescription('Install a package as new project into given directory.') + ->setName('create-project') + ->setDescription('Create new project from a package into given directory.') ->setDefinition(array( new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), new InputOption('repository-url', null, InputOption::VALUE_REQUIRED, 'Pick a different repository url to look for the package.'), @@ -42,11 +42,12 @@ class InstallProjectCommand extends Command new InputArgument('directory', InputArgument::OPTIONAL), )) ->setHelp(<<install-project command installs a given package into a new directory. -You can use this command to bootstrap new projects or setup a clean installation for -new developers of your project. +The create-project command creates a new project from a given +package into a new directory. You can use this command to bootstrap new +projects or setup a clean version-controlled installation +for developers of your project. -php composer.phar install-project vendor/project intodirectory +php composer.phar create-project vendor/project intodirectory To setup a developer workable version you should create the project using the source controlled code by appending the '--prefer-source' flag. diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 5802e2f86..e836d65c8 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -107,7 +107,7 @@ class Application extends BaseApplication $this->add(new Command\DependsCommand()); $this->add(new Command\InitCommand()); $this->add(new Command\InstallCommand()); - $this->add(new Command\InstallProjectCommand()); + $this->add(new Command\CreateProjectCommand()); $this->add(new Command\UpdateCommand()); $this->add(new Command\SearchCommand()); $this->add(new Command\ValidateCommand()); From 24de082fd51a8099e8f4bcfac66426199b241193 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 19 Feb 2012 12:45:37 +0100 Subject: [PATCH 07/57] Refactored --- doc/create-projects.md | 14 ++++++++++---- src/Composer/Command/CreateProjectCommand.php | 8 ++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/doc/create-projects.md b/doc/create-projects.md index 0cf187959..c1e220139 100644 --- a/doc/create-projects.md +++ b/doc/create-projects.md @@ -1,16 +1,22 @@ # Create Projects -You can use Composer to create new projects from an existing package. There are several applications for this: +You can use Composer to create new projects from an existing package. +There are several applications for this: 1. You can deploy application packages. 2. You can check out any package and start developing on patches for example. 3. Projects with multiple developers can use this feature to bootstrap the initial application for development. -To create a new project using composer you can use the "create-project", pass it a package name + version and a directory to create the project in. The directory is not allowed to exist, it will be created during installation. +To create a new project using composer you can use the "create-project", +pass it a package name + version and a directory to create the project in. +The directory is not allowed to exist, it will be created during installation. php composer.phar create-project doctrine/orm 2.2.0 /path/to/new-project -By default the command checks for the packages on packagist.org. To change this behavior you can use the --repository-url parameter and either point it to an HTTP url for your own packagist repository or to a packages.json file. +By default the command checks for the packages on packagist.org. To change this behavior +you can use the --repository-url parameter and either point it to an HTTP url +for your own packagist repository or to a packages.json file. -If you want to get a development version of the code directly checked out from version control you have to add the --prefer-source parameter. +If you want to get a development version of the code directly checked out +from version control you have to add the --prefer-source parameter. diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index b1d1c9854..6cbf0f3fc 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -81,11 +81,11 @@ EOT $dm->setPreferSource(true); } - if ($repositoryUrl === null) { + if (null === $repositoryUrl) { $sourceRepo = new ComposerRepository(array('url' => 'http://packagist.org')); - } else if (substr($repositoryUrl, -5) === ".json") { + } elseif (".json" === substr($repositoryUrl, -5)) { $sourceRepo = new FilesystemRepository($repositoryUrl); - } else if (strpos($repositoryUrl, 'http') === 0) { + } elseif (0 === strpos($repositoryUrl, 'http')) { $sourceRepo = new ComposerRepository(array('url' => $repositoryUrl)); } else { throw new \InvalidArgumentException("Invalid repository url given. Has to be a .json file or an http url."); @@ -96,7 +96,7 @@ EOT throw new \InvalidArgumentException("Could not find package $packageName with version $version."); } - if ($directory === null) { + if (null === $directory) { $parts = explode("/", $packageName); $directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts); } From 36ccd38aa75d7ec9afb820745b49f875022df7f4 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 27 Feb 2012 20:52:41 +0100 Subject: [PATCH 08/57] Directly install dependencies --- src/Composer/Command/CreateProjectCommand.php | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 6cbf0f3fc..5677a0553 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -15,6 +15,7 @@ namespace Composer\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\OutputInterface; use Composer\IO\IOInterface; use Composer\Factory; @@ -66,6 +67,7 @@ EOT return $this->installProject( $io, + $this->getInstallCommand($input, $output), $input->getArgument('package'), $input->getArgument('directory'), $input->getArgument('version'), @@ -74,7 +76,16 @@ EOT ); } - public function installProject(IOInterface $io, $packageName, $directory = null, $version = null, $preferSource = false, $repositoryUrl = null) + protected function getInstallCommand($input, $output) + { + $app = $this->getApplication(); + return function() use ($app, $input, $output) { + $newInput = new ArrayInput(array('command' => 'install')); + $app->doRUn($newInput, $output); + }; + } + + public function installProject(IOInterface $io, $installCommand, $packageName, $directory = null, $version = null, $preferSource = false, $repositoryUrl = null) { $dm = $this->createDownloadManager($io); if ($preferSource) { @@ -101,13 +112,13 @@ EOT $directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts); } + $io->write('Installing ' . $package->getName() . ' as new project.', true); $projectInstaller = new ProjectInstaller($directory, $dm); $projectInstaller->install($package); - $io->write('Created new project directory for ' . $package->getName(), true); - $io->write('Next steps:', true); - $io->write('1. cd ' . $directory, true); - $io->write('2. composer install', true); + $io->write('Created project into directory ' . $directory . '', true); + chdir($directory); + $installCommand(); } protected function createDownloadManager(IOInterface $io) From 1af36043038f5221a4b7b5978fe34e9b558152c5 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 5 Mar 2012 13:26:46 +0100 Subject: [PATCH 09/57] Add support for class-map generation. --- res/composer-schema.json | 4 + src/Composer/Autoload/AutoloadGenerator.php | 13 +- src/Composer/Autoload/ClassLoader.php | 22 ++++ src/Composer/Autoload/ClassMapGenerator.php | 138 ++++++++++++++++++++ 4 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 src/Composer/Autoload/ClassMapGenerator.php diff --git a/res/composer-schema.json b/res/composer-schema.json index 62d6be0ed..308b0d9ba 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -128,6 +128,10 @@ "type": "object", "description": "This is a hash of namespaces (keys) and the directories they can be found into (values) by the autoloader.", "additionalProperties": true + }, + "classmap": { + "type": "array", + "description": "This is an array of directories that contain classes to be included in the class-map generation process." } } }, diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index e33c8ff99..38e44ca2a 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -44,6 +44,11 @@ return call_user_func(function() { $loader->add($namespace, $path); } + $classMap = require __DIR__.'/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + $loader->register(); return $loader; @@ -107,9 +112,15 @@ EOF; } } } - $namespacesFile .= ");\n"; + if (isset($autoloads['classmap'])) { + $it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($autoloads['classmap'])); + ClassMapGenerator::dump(iterator_to_array($it), $targetDir.'/autoload_classmap.php'); + } else { + file_put_contents($targetDir.'/autoload_classmap.php', 'fallbackDirs; } + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + /** * Registers a set of classes * @@ -142,6 +160,10 @@ class ClassLoader */ public function findFile($class) { + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ('\\' == $class[0]) { $class = substr($class, 1); } diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php new file mode 100644 index 000000000..dad422929 --- /dev/null +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @license MIT + */ + +namespace Composer\Autoload; + +/** + * ClassMapGenerator + * + * @author Gyula Sallai + */ +class ClassMapGenerator +{ + /** + * Generate a class map file + * + * @param array|string $dirs Directories or a single path to search in + * @param string $file The name of the class map file + */ + static public function dump($dirs, $file) + { + $dirs = (array) $dirs; + $maps = array(); + + foreach ($dirs as $dir) { + $maps = array_merge($maps, static::createMap($dir)); + } + + file_put_contents($file, sprintf('isFile()) { + continue; + } + + $path = $file->getRealPath(); + + if (pathinfo($path, PATHINFO_EXTENSION) !== 'php') { + continue; + } + + $classes = self::findClasses($path); + + foreach ($classes as $class) { + $map[$class] = $path; + } + + } + + return $map; + } + + /** + * Extract the classes in the given file + * + * @param string $path The file to check + * + * @return array The found classes + */ + static private function findClasses($path) + { + $contents = file_get_contents($path); + $tokens = token_get_all($contents); + + $classes = array(); + + $namespace = ''; + for ($i = 0, $max = count($tokens); $i < $max; $i++) { + $token = $tokens[$i]; + + if (is_string($token)) { + continue; + } + + $class = ''; + + switch ($token[0]) { + case T_NAMESPACE: + $namespace = ''; + // If there is a namespace, extract it + while (($t = $tokens[++$i]) && is_array($t)) { + if (in_array($t[0], array(T_STRING, T_NS_SEPARATOR))) { + $namespace .= $t[1]; + } + } + $namespace .= '\\'; + break; + case T_CLASS: + case T_INTERFACE: + // Find the classname + while (($t = $tokens[++$i]) && is_array($t)) { + if (T_STRING === $t[0]) { + $class .= $t[1]; + } elseif ($class !== '' && T_WHITESPACE == $t[0]) { + break; + } + } + + if (empty($namespace)) { + $classes[] = $class; + } else { + $classes[] = $namespace . $class; + } + break; + default: + break; + } + } + + return $classes; + } +} + From 671cd5ee08b1135dc25527ded0c38e3062417688 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 5 Mar 2012 14:10:01 +0100 Subject: [PATCH 10/57] Add tests for ClassMapGenerator --- src/Composer/Autoload/AutoloadGenerator.php | 8 +- src/Composer/Autoload/ClassMapGenerator.php | 3 +- .../Test/Autoload/AutoloadGeneratorTest.php | 34 ++++++++ .../Test/Autoload/ClassMapGeneratorTest.php | 83 +++++++++++++++++++ .../Test/Autoload/Fixtures/Namespaced/Bar.php | 8 ++ .../Test/Autoload/Fixtures/Namespaced/Baz.php | 8 ++ .../Test/Autoload/Fixtures/Namespaced/Foo.php | 8 ++ .../Test/Autoload/Fixtures/Pearlike/Bar.php | 6 ++ .../Test/Autoload/Fixtures/Pearlike/Baz.php | 6 ++ .../Test/Autoload/Fixtures/Pearlike/Foo.php | 6 ++ .../beta/NamespaceCollision/A/B/Bar.php | 8 ++ .../beta/NamespaceCollision/A/B/Foo.php | 8 ++ .../Fixtures/beta/PrefixCollision/A/B/Bar.php | 6 ++ .../Fixtures/beta/PrefixCollision/A/B/Foo.php | 6 ++ .../Autoload/Fixtures/classmap/SomeClass.php | 8 ++ .../Fixtures/classmap/SomeInterface.php | 8 ++ .../Autoload/Fixtures/classmap/SomeParent.php | 8 ++ .../Autoload/Fixtures/classmap/multipleNs.php | 11 +++ .../Autoload/Fixtures/classmap/notAClass.php | 3 + .../Autoload/Fixtures/classmap/notPhpFile.md | 1 + .../classmap/sameNsMultipleClasses.php | 6 ++ 21 files changed, 238 insertions(+), 5 deletions(-) create mode 100644 tests/Composer/Test/Autoload/ClassMapGeneratorTest.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/Namespaced/Bar.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/Namespaced/Baz.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/Namespaced/Foo.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/Pearlike/Bar.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/Pearlike/Baz.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/Pearlike/Foo.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/beta/NamespaceCollision/A/B/Bar.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/beta/NamespaceCollision/A/B/Foo.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/beta/PrefixCollision/A/B/Bar.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/beta/PrefixCollision/A/B/Foo.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/classmap/SomeClass.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/classmap/SomeInterface.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/classmap/SomeParent.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/classmap/multipleNs.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/classmap/notAClass.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/classmap/notPhpFile.md create mode 100644 tests/Composer/Test/Autoload/Fixtures/classmap/sameNsMultipleClasses.php diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 38e44ca2a..59addd0b4 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -115,12 +115,14 @@ EOF; $namespacesFile .= ");\n"; if (isset($autoloads['classmap'])) { - $it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($autoloads['classmap'])); - ClassMapGenerator::dump(iterator_to_array($it), $targetDir.'/autoload_classmap.php'); + // flatten array + $autoloads['classmap'] = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($autoloads['classmap'])); } else { - file_put_contents($targetDir.'/autoload_classmap.php', 'vendorDir.'/.composer', 0777, true); $this->generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); $this->assertAutoloadFiles('vendors', $this->vendorDir.'/.composer'); + $this->assertTrue(file_exists($this->vendorDir.'/.composer/autoload_classmap.php'), "ClassMap file needs to be generated, even if empty."); + } + + public function testVendorsClassMapAutoloading() + { + $package = new MemoryPackage('a', '1.0', '1.0'); + + $packages = array(); + $packages[] = $a = new MemoryPackage('a/a', '1.0', '1.0'); + $packages[] = $b = new MemoryPackage('b/b', '1.0', '1.0'); + $a->setAutoload(array('classmap' => array('src/'))); + $b->setAutoload(array('classmap' => array('src/', 'lib/'))); + + $this->repository->expects($this->once()) + ->method('getPackages') + ->will($this->returnValue($packages)); + + @mkdir($this->vendorDir.'/.composer', 0777, true); + mkdir($this->vendorDir.'/a/a/src', 0777, true); + mkdir($this->vendorDir.'/b/b/src', 0777, true); + mkdir($this->vendorDir.'/b/b/lib', 0777, true); + file_put_contents($this->vendorDir.'/a/a/src/a.php', 'vendorDir.'/b/b/src/b.php', 'vendorDir.'/b/b/lib/c.php', 'generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); + $this->assertTrue(file_exists($this->vendorDir.'/.composer/autoload_classmap.php'), "ClassMap file needs to be generated, even if empty."); + $this->assertEquals(array( + 'ClassMapFoo' => $this->vendorDir.'/a/a/src/a.php', + 'ClassMapBar' => $this->vendorDir.'/b/b/src/b.php', + 'ClassMapBaz' => $this->vendorDir.'/b/b/lib/c.php', + ), + include ($this->vendorDir.'/.composer/autoload_classmap.php') + ); } public function testOverrideVendorsAutoloading() diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php new file mode 100644 index 000000000..d40321768 --- /dev/null +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Autoload; + +use Composer\Autoload\ClassMapGenerator; + +class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getTestCreateMapTests + */ + public function testCreateMap($directory, $expected) + { + $this->assertEqualsNormalized($expected, ClassMapGenerator::createMap($directory)); + } + + public function getTestCreateMapTests() + { + return array( + array(__DIR__.'/Fixtures/Namespaced', array( + 'Namespaced\\Bar' => realpath(__DIR__).'/Fixtures/Namespaced/Bar.php', + 'Namespaced\\Foo' => realpath(__DIR__).'/Fixtures/Namespaced/Foo.php', + 'Namespaced\\Baz' => realpath(__DIR__).'/Fixtures/Namespaced/Baz.php', + ) + ), + array(__DIR__.'/Fixtures/beta/NamespaceCollision', array( + 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', + 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', + )), + array(__DIR__.'/Fixtures/Pearlike', array( + 'Pearlike_Foo' => realpath(__DIR__).'/Fixtures/Pearlike/Foo.php', + 'Pearlike_Bar' => realpath(__DIR__).'/Fixtures/Pearlike/Bar.php', + 'Pearlike_Baz' => realpath(__DIR__).'/Fixtures/Pearlike/Baz.php', + )), + array(__DIR__.'/Fixtures/classmap', array( + 'Foo\\Bar\\A' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', + 'Foo\\Bar\\B' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', + 'Alpha\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Alpha\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Beta\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Beta\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'ClassMap\\SomeInterface' => realpath(__DIR__).'/Fixtures/classmap/SomeInterface.php', + 'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php', + 'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php', + )), + ); + } + + public function testCreateMapFinderSupport() + { + if (!class_exists('Symfony\\Component\\Finder\\Finder')) { + $this->markTestSkipped('Finder component is not available'); + } + + $finder = new \Symfony\Component\Finder\Finder(); + $finder->files()->in(__DIR__ . '/Fixtures/beta/NamespaceCollision'); + + $this->assertEqualsNormalized(array( + 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', + 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', + ), ClassMapGenerator::createMap($finder)); + } + + protected function assertEqualsNormalized($expected, $actual, $message = null) + { + foreach ($expected as $ns => $path) { + $expected[$ns] = strtr($path, '\\', '/'); + } + foreach ($actual as $ns => $path) { + $actual[$ns] = strtr($path, '\\', '/'); + } + $this->assertEquals($expected, $actual, $message); + } +} diff --git a/tests/Composer/Test/Autoload/Fixtures/Namespaced/Bar.php b/tests/Composer/Test/Autoload/Fixtures/Namespaced/Bar.php new file mode 100644 index 000000000..f9c519a66 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/Namespaced/Bar.php @@ -0,0 +1,8 @@ + Date: Mon, 5 Mar 2012 14:13:06 +0100 Subject: [PATCH 11/57] Explain classmap generation in documentation --- doc/04-schema.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 0572da36f..5bf6b49d7 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -183,9 +183,10 @@ Optional. Autoload mapping for a PHP autoloader. -Currently only [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) -autoloading is supported. Under the -`psr-0` key you define a mapping from namespaces to paths, relative to the +Currently [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) +autoloading and ClassMap generation is supported. + +Under the `psr-0` key you define a mapping from namespaces to paths, relative to the package root. Example: @@ -198,6 +199,18 @@ Example: Optional, but it is highly recommended that you follow PSR-0 and use this. +You can use the classmap generation support to define autoloading for all libraries +that do not follow "PSR-0". To configure this you specify all directories +to search for classes. + +Example: + + { + "autoload: { + "classmap": ["src/", "lib/"] + } + } + ## target-dir Defines the installation target. From 590ee419bd3a355f60f46612e3d9bd80a50b49f0 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 5 Mar 2012 14:21:51 +0100 Subject: [PATCH 12/57] Fix docs --- doc/04-schema.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 5bf6b49d7..7f451b3c3 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -184,7 +184,7 @@ Optional. Autoload mapping for a PHP autoloader. Currently [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) -autoloading and ClassMap generation is supported. +autoloading and ClassMap generation are supported. Under the `psr-0` key you define a mapping from namespaces to paths, relative to the package root. From 0b7896cdadf7823e75fbbfbeb0b305ca9575a154 Mon Sep 17 00:00:00 2001 From: Konstantin Tjuterev Date: Tue, 6 Mar 2012 20:08:15 +0200 Subject: [PATCH 13/57] Simplified & fixed ConsoleIO->overwrite, corrected output when downloading with progress --- src/Composer/Downloader/FileDownloader.php | 2 -- src/Composer/IO/ConsoleIO.php | 32 +++++++++++++------- src/Composer/Util/RemoteFilesystem.php | 2 +- tests/Composer/Test/IO/ConsoleIOTest.php | 34 +++++++++++----------- 4 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 698ebfd46..d3e668c93 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -84,8 +84,6 @@ class FileDownloader implements DownloaderInterface if ($checksum && hash_file('sha1', $fileName) !== $checksum) { throw new \UnexpectedValueException('The checksum verification of the file failed (downloaded from '.$url.')'); } - - $this->io->write(''); } /** diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index e1d33b5d2..30c80355c 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -31,6 +31,7 @@ class ConsoleIO implements IOInterface protected $authorizations = array(); protected $lastUsername; protected $lastPassword; + protected $lastMessage; /** * Constructor. @@ -60,31 +61,40 @@ class ConsoleIO implements IOInterface public function write($messages, $newline = true) { $this->output->write($messages, $newline); + $this->lastMessage = join($newline ? "\n" : '', (array) $messages); } /** * {@inheritDoc} */ - public function overwrite($messages, $newline = true, $size = 80) + public function overwrite($messages, $newline = true, $size = null) { - for ($place = $size; $place > 0; $place--) { - $this->write("\x08", false); - } + // messages can be an array, let's convert it to string anyway + $messages = join($newline ? "\n" : '', (array) $messages); + // since overwrite is supposed to overwrite last message... + if (!isset($size)) { + // removing possible formatting of lastMessage with strip_tags + $size = strlen(strip_tags($this->lastMessage)); + } + // ...let's fill its length with backspaces + $this->write(str_repeat("\x08", $size), false); + + // write the new message $this->write($messages, false); - for ($place = ($size - strlen($messages)); $place > 0; $place--) { - $this->write(' ', false); - } - - // clean up the end line - for ($place = ($size - strlen($messages)); $place > 0; $place--) { - $this->write("\x08", false); + $fill = $size - strlen(strip_tags($messages)); + if ($fill > 0) { + // whitespace whatever has left + $this->write(str_repeat(' ', $fill), false); + // move the cursor back + $this->write(str_repeat("\x08", $fill), false); } if ($newline) { $this->write(''); } + $this->lastMessage = $messages; } /** diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 7bd0f3bfe..db4dc6e5e 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -98,7 +98,7 @@ class RemoteFilesystem $ctx = StreamContextFactory::getContext($options, array('notification' => array($this, 'callbackGet'))); if ($this->progress) { - $this->io->overwrite(" Downloading: connection...", false); + $this->io->write(" Downloading: connection...", false); } if (null !== $fileName) { diff --git a/tests/Composer/Test/IO/ConsoleIOTest.php b/tests/Composer/Test/IO/ConsoleIOTest.php index 78450eb53..73f0faedb 100644 --- a/tests/Composer/Test/IO/ConsoleIOTest.php +++ b/tests/Composer/Test/IO/ConsoleIOTest.php @@ -53,35 +53,35 @@ class ConsoleIOTest extends TestCase { $inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface'); $outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + $outputMock->expects($this->at(0)) ->method('write') - ->with($this->equalTo("\x08"), $this->equalTo(false)); - $outputMock->expects($this->at(19)) + ->with($this->equalTo('something (strlen = 23)')); + $outputMock->expects($this->at(1)) ->method('write') - ->with($this->equalTo("\x08"), $this->equalTo(false)); - $outputMock->expects($this->at(20)) + ->with($this->equalTo(str_repeat("\x08", 23)), $this->equalTo(false)); + $outputMock->expects($this->at(2)) ->method('write') - ->with($this->equalTo('some information'), $this->equalTo(false)); - $outputMock->expects($this->at(21)) + ->with($this->equalTo('shorter (12)'), $this->equalTo(false)); + $outputMock->expects($this->at(3)) ->method('write') - ->with($this->equalTo(' '), $this->equalTo(false)); - $outputMock->expects($this->at(24)) + ->with($this->equalTo(str_repeat(' ', 11)), $this->equalTo(false)); + $outputMock->expects($this->at(4)) ->method('write') - ->with($this->equalTo(' '), $this->equalTo(false)); - $outputMock->expects($this->at(25)) + ->with($this->equalTo(str_repeat("\x08", 11)), $this->equalTo(false)); + $outputMock->expects($this->at(5)) ->method('write') - ->with($this->equalTo("\x08"), $this->equalTo(false)); - $outputMock->expects($this->at(28)) + ->with($this->equalTo(str_repeat("\x08", 12)), $this->equalTo(false)); + $outputMock->expects($this->at(6)) ->method('write') - ->with($this->equalTo("\x08"), $this->equalTo(false)); - $outputMock->expects($this->at(29)) - ->method('write') - ->with($this->equalTo('')); + ->with($this->equalTo('something longer than initial (34)')); $helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet'); $consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock); - $consoleIO->overwrite('some information', true, 20); + $consoleIO->write('something (strlen = 23)'); + $consoleIO->overwrite('shorter (12)', false); + $consoleIO->overwrite('something longer than initial (34)'); } public function testAsk() From 347f8feeecd943fb3e17681909e7ccd510e82f83 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 8 Mar 2012 18:10:58 +0100 Subject: [PATCH 14/57] Update docs --- src/Composer/Command/CreateProjectCommand.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 5677a0553..007252969 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -36,11 +36,11 @@ class CreateProjectCommand extends Command ->setName('create-project') ->setDescription('Create new project from a package into given directory.') ->setDefinition(array( + new InputArgument('package', InputArgument::REQUIRED, 'Package name to be installed'), + new InputArgument('directory', InputArgument::OPTIONAL, 'Directory where the files should be created'), + new InputArgument('version', InputArgument::OPTIONAL, 'Version, will defaults to latest'), new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), new InputOption('repository-url', null, InputOption::VALUE_REQUIRED, 'Pick a different repository url to look for the package.'), - new InputArgument('package', InputArgument::REQUIRED), - new InputArgument('version', InputArgument::OPTIONAL), - new InputArgument('directory', InputArgument::OPTIONAL), )) ->setHelp(<<create-project command creates a new project from a given @@ -48,7 +48,7 @@ package into a new directory. You can use this command to bootstrap new projects or setup a clean version-controlled installation for developers of your project. -php composer.phar create-project vendor/project intodirectory +php composer.phar create-project vendor/project target-directory [version] To setup a developer workable version you should create the project using the source controlled code by appending the '--prefer-source' flag. From ea593fdb108b78f9337e584b84af91f129072c70 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 8 Mar 2012 18:12:21 +0100 Subject: [PATCH 15/57] Allow autodetection of the version --- src/Composer/Command/CreateProjectCommand.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 007252969..d21fafb64 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -102,16 +102,24 @@ EOT throw new \InvalidArgumentException("Invalid repository url given. Has to be a .json file or an http url."); } - $package = $sourceRepo->findPackage($packageName, $version); - if (!$package) { - throw new \InvalidArgumentException("Could not find package $packageName with version $version."); + $candidates = $sourceRepo->findPackages($packageName, $version); + if (!$candidates) { + throw new \InvalidArgumentException("Could not find package $packageName" . ($version ? " with version $version." : '')); } if (null === $directory) { - $parts = explode("/", $packageName); + $parts = explode("/", $packageName, 2); $directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts); } + // select highest version if we have many + $package = $candidates[0]; + foreach ($candidates as $candidate) { + if (version_compare($package->getVersion(), $candidate->getVersion(), '<')) { + $package = $candidate; + } + } + $io->write('Installing ' . $package->getName() . ' as new project.', true); $projectInstaller = new ProjectInstaller($directory, $dm); $projectInstaller->install($package); From 388fcfe61082d263d280f13807adff2261b72425 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 8 Mar 2012 18:16:22 +0100 Subject: [PATCH 16/57] Updated changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5328073a2..3a9d73764 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ * 1.0.0-alpha2 + * Added `create-project` command to install a project from scratch with composer * Git clones from GitHub automatically select between git/https/http protocols * Enhanced `validate` command to give more feedback * Added "file" downloader type to download plain files + * Added support for authentication with svn repositories * Dependency on filter_var is now optional * Various robustness & error handling improvements From 3aabb4784c1d8e6ba5e1fc5306f1ac758316f86b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 8 Mar 2012 18:19:31 +0100 Subject: [PATCH 17/57] Cross platform test fixes --- .../Test/Repository/Vcs/SvnDriverTest.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 7d4578930..e9734eab6 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -25,12 +25,12 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase * * @return array */ - public static function urlProvider() + public function urlProvider() { return array( - array('http://till:test@svn.example.org/', " --no-auth-cache --username 'till' --password 'test' "), + array('http://till:test@svn.example.org/', $this->getCmd(" --no-auth-cache --username 'till' --password 'test' ")), array('http://svn.apache.org/', ''), - array('svn://johndoe@example.org', " --no-auth-cache --username 'johndoe' --password '' "), + array('svn://johndoe@example.org', $this->getCmd(" --no-auth-cache --username 'johndoe' --password '' ")), ); } @@ -44,4 +44,13 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expect, $svn->getSvnCredentialString()); } + + private function getCmd($cmd) + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + return strtr($cmd, "'", '"'); + } + + return $cmd; + } } From d1dea702c9be6e07e07ded85bd4b1e4562bfd39d Mon Sep 17 00:00:00 2001 From: Beau Simensen Date: Fri, 2 Mar 2012 21:35:40 -0800 Subject: [PATCH 18/57] Moving install related code to Composer\Install --- src/Composer/Command/InstallCommand.php | 229 +-------------------- src/Composer/Command/UpdateCommand.php | 12 +- src/Composer/Install.php | 252 ++++++++++++++++++++++++ 3 files changed, 258 insertions(+), 235 deletions(-) create mode 100644 src/Composer/Install.php diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 6b6120bb8..4a0070abd 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -12,30 +12,11 @@ namespace Composer\Command; -use Composer\Script\ScriptEvents; +use Composer\Install; use Composer\Script\EventDispatcher; -use Composer\Autoload\AutoloadGenerator; -use Composer\Composer; -use Composer\DependencyResolver; -use Composer\DependencyResolver\Pool; -use Composer\DependencyResolver\Request; -use Composer\DependencyResolver\Operation; -use Composer\Package\AliasPackage; -use Composer\Package\MemoryPackage; -use Composer\Package\Link; -use Composer\Package\LinkConstraint\VersionConstraint; -use Composer\Package\PackageInterface; -use Composer\Repository\ArrayRepository; -use Composer\Repository\CompositeRepository; -use Composer\Repository\PlatformRepository; -use Composer\Repository\RepositoryInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Composer\DependencyResolver\Operation\InstallOperation; -use Composer\DependencyResolver\Operation\UpdateOperation; -use Composer\DependencyResolver\Solver; -use Composer\IO\IOInterface; /** * @author Jordi Boggiano @@ -72,8 +53,9 @@ EOT $composer = $this->getComposer(); $io = $this->getApplication()->getIO(); $eventDispatcher = new EventDispatcher($composer, $io); + $install = new Install; - return $this->install( + return $install->run( $io, $composer, $eventDispatcher, @@ -84,209 +66,4 @@ EOT (Boolean)$input->getOption('install-suggests') ); } - - public function install(IOInterface $io, Composer $composer, EventDispatcher $eventDispatcher, $preferSource = false, $dryRun = false, $verbose = false, $noInstallRecommends = false, $installSuggests = false, $update = false, RepositoryInterface $additionalInstalledRepository = null) - { - if ($dryRun) { - $verbose = true; - } - - if ($preferSource) { - $composer->getDownloadManager()->setPreferSource(true); - } - - $repoManager = $composer->getRepositoryManager(); - - // create local repo, this contains all packages that are installed in the local project - $localRepo = $repoManager->getLocalRepository(); - // create installed repo, this contains all local packages + platform packages (php & extensions) - $installedRepo = new CompositeRepository(array($localRepo, new PlatformRepository())); - if ($additionalInstalledRepository) { - $installedRepo->addRepository($additionalInstalledRepository); - } - - // prepare aliased packages - if (!$update && $composer->getLocker()->isLocked()) { - $aliases = $composer->getLocker()->getAliases(); - } else { - $aliases = $composer->getPackage()->getAliases(); - } - foreach ($aliases as $alias) { - foreach ($repoManager->findPackages($alias['package'], $alias['version']) as $package) { - $package->getRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); - } - foreach ($repoManager->getLocalRepository()->findPackages($alias['package'], $alias['version']) as $package) { - $repoManager->getLocalRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); - $repoManager->getLocalRepository()->removePackage($package); - } - } - - // creating repository pool - $pool = new Pool; - $pool->addRepository($installedRepo); - foreach ($repoManager->getRepositories() as $repository) { - $pool->addRepository($repository); - } - - // dispatch pre event - if (!$dryRun) { - $eventName = $update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD; - $eventDispatcher->dispatchCommandEvent($eventName); - } - - // creating requirements request - $installFromLock = false; - $request = new Request($pool); - if ($update) { - $io->write('Updating dependencies'); - - $request->updateAll(); - - $links = $this->collectLinks($composer->getPackage(), $noInstallRecommends, $installSuggests); - - foreach ($links as $link) { - $request->install($link->getTarget(), $link->getConstraint()); - } - } elseif ($composer->getLocker()->isLocked()) { - $installFromLock = true; - $io->write('Installing from lock file'); - - if (!$composer->getLocker()->isFresh()) { - $io->write('Your lock file is out of sync with your composer.json, run "composer.phar update" to update dependencies'); - } - - foreach ($composer->getLocker()->getLockedPackages() as $package) { - $version = $package->getVersion(); - foreach ($aliases as $alias) { - if ($alias['package'] === $package->getName() && $alias['version'] === $package->getVersion()) { - $version = $alias['alias']; - break; - } - } - $constraint = new VersionConstraint('=', $version); - $request->install($package->getName(), $constraint); - } - } else { - $io->write('Installing dependencies'); - - $links = $this->collectLinks($composer->getPackage(), $noInstallRecommends, $installSuggests); - - foreach ($links as $link) { - $request->install($link->getTarget(), $link->getConstraint()); - } - } - - // prepare solver - $installationManager = $composer->getInstallationManager(); - $policy = new DependencyResolver\DefaultPolicy(); - $solver = new DependencyResolver\Solver($policy, $pool, $installedRepo); - - // solve dependencies - $operations = $solver->solve($request); - - // force dev packages to be updated to latest reference on update - if ($update) { - foreach ($localRepo->getPackages() as $package) { - if ($package instanceof AliasPackage) { - $package = $package->getAliasOf(); - } - - // skip non-dev packages - if (!$package->isDev()) { - continue; - } - - // skip packages that will be updated/uninstalled - foreach ($operations as $operation) { - if (('update' === $operation->getJobType() && $package === $operation->getInitialPackage()) - || ('uninstall' === $operation->getJobType() && $package === $operation->getPackage()) - ) { - continue 2; - } - } - - // force update - $newPackage = $repoManager->findPackage($package->getName(), $package->getVersion()); - if ($newPackage && $newPackage->getSourceReference() !== $package->getSourceReference()) { - $operations[] = new UpdateOperation($package, $newPackage); - } - } - } - - // anti-alias local repository to allow updates to work fine - foreach ($repoManager->getLocalRepository()->getPackages() as $package) { - if ($package instanceof AliasPackage) { - $repoManager->getLocalRepository()->addPackage(clone $package->getAliasOf()); - $repoManager->getLocalRepository()->removePackage($package); - } - } - - // execute operations - if (!$operations) { - $io->write('Nothing to install/update'); - } - - foreach ($operations as $operation) { - if ($verbose) { - $io->write((string) $operation); - } - if (!$dryRun) { - $eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType())), $operation); - - // if installing from lock, restore dev packages' references to their locked state - if ($installFromLock) { - $package = null; - if ('update' === $operation->getJobType()) { - $package = $operation->getTargetPackage(); - } elseif ('install' === $operation->getJobType()) { - $package = $operation->getPackage(); - } - if ($package && $package->isDev()) { - $lockData = $composer->getLocker()->getLockData(); - foreach ($lockData['packages'] as $lockedPackage) { - if (!empty($lockedPackage['source-reference']) && strtolower($lockedPackage['package']) === $package->getName()) { - $package->setSourceReference($lockedPackage['source-reference']); - break; - } - } - } - } - $installationManager->execute($operation); - - $eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType())), $operation); - } - } - - if (!$dryRun) { - if ($update || !$composer->getLocker()->isLocked()) { - $composer->getLocker()->setLockData($localRepo->getPackages(), $aliases); - $io->write('Writing lock file'); - } - - $localRepo->write(); - - $io->write('Generating autoload files'); - $generator = new AutoloadGenerator; - $generator->dump($localRepo, $composer->getPackage(), $installationManager, $installationManager->getVendorPath().'/.composer'); - - // dispatch post event - $eventName = $update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; - $eventDispatcher->dispatchCommandEvent($eventName); - } - } - - private function collectLinks(PackageInterface $package, $noInstallRecommends, $installSuggests) - { - $links = $package->getRequires(); - - if (!$noInstallRecommends) { - $links = array_merge($links, $package->getRecommends()); - } - - if ($installSuggests) { - $links = array_merge($links, $package->getSuggests()); - } - - return $links; - } } diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 7e1e8e0c1..4ead94d7e 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -12,13 +12,7 @@ namespace Composer\Command; -use Composer\Autoload\AutoloadGenerator; -use Composer\DependencyResolver; -use Composer\DependencyResolver\Pool; -use Composer\DependencyResolver\Request; -use Composer\DependencyResolver\Operation; -use Composer\Package\LinkConstraint\VersionConstraint; -use Composer\Repository\PlatformRepository; +use Composer\Install; use Composer\Script\EventDispatcher; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -54,12 +48,12 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { - $installCommand = $this->getApplication()->find('install'); $composer = $this->getComposer(); $io = $this->getApplication()->getIO(); $eventDispatcher = new EventDispatcher($composer, $io); + $install = new Install; - return $installCommand->install( + return $install->run( $io, $composer, $eventDispatcher, diff --git a/src/Composer/Install.php b/src/Composer/Install.php new file mode 100644 index 000000000..d88231cd8 --- /dev/null +++ b/src/Composer/Install.php @@ -0,0 +1,252 @@ + + * 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\Autoload\AutoloadGenerator; +use Composer\DependencyResolver\DefaultPolicy; +use Composer\DependencyResolver\Operation\UpdateOperation; +use Composer\DependencyResolver\Pool; +use Composer\DependencyResolver\Request; +use Composer\DependencyResolver\Solver; +use Composer\IO\IOInterface; +use Composer\Package\AliasPackage; +use Composer\Package\Link; +use Composer\Package\LinkConstraint\VersionConstraint; +use Composer\Package\PackageInterface; +use Composer\Repository\CompositeRepository; +use Composer\Repository\PlatformRepository; +use Composer\Repository\RepositoryInterface; +use Composer\Script\EventDispatcher; +use Composer\Script\ScriptEvents; + +class Install +{ + /** + * Run installation (or update) + * + * @param IOInterface $io + * @param Composer $composer + * @param EventDispatcher $eventDispatcher + * @param bool $preferSource + * @param bool $dryRun + * @param bool $verbose + * @param bool $noInstallRecommends + * @param bool $installSuggests + * @param bool $update + * @param RepositoryInterface $additionalInstalledRepository + */ + public function run(IOInterface $io, Composer $composer, EventDispatcher $eventDispatcher, $preferSource = false, $dryRun = false, $verbose = false, $noInstallRecommends = false, $installSuggests = false, $update = false, RepositoryInterface $additionalInstalledRepository = null) + { + if ($dryRun) { + $verbose = true; + } + + if ($preferSource) { + $composer->getDownloadManager()->setPreferSource(true); + } + + $repoManager = $composer->getRepositoryManager(); + + // create local repo, this contains all packages that are installed in the local project + $localRepo = $repoManager->getLocalRepository(); + // create installed repo, this contains all local packages + platform packages (php & extensions) + $installedRepo = new CompositeRepository(array($localRepo, new PlatformRepository())); + if ($additionalInstalledRepository) { + $installedRepo->addRepository($additionalInstalledRepository); + } + + // prepare aliased packages + if (!$update && $composer->getLocker()->isLocked()) { + $aliases = $composer->getLocker()->getAliases(); + } else { + $aliases = $composer->getPackage()->getAliases(); + } + foreach ($aliases as $alias) { + foreach ($repoManager->findPackages($alias['package'], $alias['version']) as $package) { + $package->getRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); + } + foreach ($repoManager->getLocalRepository()->findPackages($alias['package'], $alias['version']) as $package) { + $repoManager->getLocalRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); + $repoManager->getLocalRepository()->removePackage($package); + } + } + + // creating repository pool + $pool = new Pool; + $pool->addRepository($installedRepo); + foreach ($repoManager->getRepositories() as $repository) { + $pool->addRepository($repository); + } + + // dispatch pre event + if (!$dryRun) { + $eventName = $update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD; + $eventDispatcher->dispatchCommandEvent($eventName); + } + + // creating requirements request + $installFromLock = false; + $request = new Request($pool); + if ($update) { + $io->write('Updating dependencies'); + + $request->updateAll(); + + $links = $this->collectLinks($composer->getPackage(), $noInstallRecommends, $installSuggests); + + foreach ($links as $link) { + $request->install($link->getTarget(), $link->getConstraint()); + } + } elseif ($composer->getLocker()->isLocked()) { + $installFromLock = true; + $io->write('Installing from lock file'); + + if (!$composer->getLocker()->isFresh()) { + $io->write('Your lock file is out of sync with your composer.json, run "composer.phar update" to update dependencies'); + } + + foreach ($composer->getLocker()->getLockedPackages() as $package) { + $version = $package->getVersion(); + foreach ($aliases as $alias) { + if ($alias['package'] === $package->getName() && $alias['version'] === $package->getVersion()) { + $version = $alias['alias']; + break; + } + } + $constraint = new VersionConstraint('=', $version); + $request->install($package->getName(), $constraint); + } + } else { + $io->write('Installing dependencies'); + + $links = $this->collectLinks($composer->getPackage(), $noInstallRecommends, $installSuggests); + + foreach ($links as $link) { + $request->install($link->getTarget(), $link->getConstraint()); + } + } + + // prepare solver + $installationManager = $composer->getInstallationManager(); + $policy = new DependencyResolver\DefaultPolicy(); + $solver = new DependencyResolver\Solver($policy, $pool, $installedRepo); + + // solve dependencies + $operations = $solver->solve($request); + + // force dev packages to be updated to latest reference on update + if ($update) { + foreach ($localRepo->getPackages() as $package) { + if ($package instanceof AliasPackage) { + $package = $package->getAliasOf(); + } + + // skip non-dev packages + if (!$package->isDev()) { + continue; + } + + // skip packages that will be updated/uninstalled + foreach ($operations as $operation) { + if (('update' === $operation->getJobType() && $package === $operation->getInitialPackage()) + || ('uninstall' === $operation->getJobType() && $package === $operation->getPackage()) + ) { + continue 2; + } + } + + // force update + $newPackage = $repoManager->findPackage($package->getName(), $package->getVersion()); + if ($newPackage && $newPackage->getSourceReference() !== $package->getSourceReference()) { + $operations[] = new UpdateOperation($package, $newPackage); + } + } + } + + // anti-alias local repository to allow updates to work fine + foreach ($repoManager->getLocalRepository()->getPackages() as $package) { + if ($package instanceof AliasPackage) { + $repoManager->getLocalRepository()->addPackage(clone $package->getAliasOf()); + $repoManager->getLocalRepository()->removePackage($package); + } + } + + // execute operations + if (!$operations) { + $io->write('Nothing to install/update'); + } + + foreach ($operations as $operation) { + if ($verbose) { + $io->write((string) $operation); + } + if (!$dryRun) { + $eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType())), $operation); + + // if installing from lock, restore dev packages' references to their locked state + if ($installFromLock) { + $package = null; + if ('update' === $operation->getJobType()) { + $package = $operation->getTargetPackage(); + } elseif ('install' === $operation->getJobType()) { + $package = $operation->getPackage(); + } + if ($package && $package->isDev()) { + $lockData = $composer->getLocker()->getLockData(); + foreach ($lockData['packages'] as $lockedPackage) { + if (!empty($lockedPackage['source-reference']) && strtolower($lockedPackage['package']) === $package->getName()) { + $package->setSourceReference($lockedPackage['source-reference']); + break; + } + } + } + } + $installationManager->execute($operation); + + $eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType())), $operation); + } + } + + if (!$dryRun) { + if ($update || !$composer->getLocker()->isLocked()) { + $composer->getLocker()->setLockData($localRepo->getPackages(), $aliases); + $io->write('Writing lock file'); + } + + $localRepo->write(); + + $io->write('Generating autoload files'); + $generator = new AutoloadGenerator; + $generator->dump($localRepo, $composer->getPackage(), $installationManager, $installationManager->getVendorPath().'/.composer'); + + // dispatch post event + $eventName = $update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; + $eventDispatcher->dispatchCommandEvent($eventName); + } + } + + private function collectLinks(PackageInterface $package, $noInstallRecommends, $installSuggests) + { + $links = $package->getRequires(); + + if (!$noInstallRecommends) { + $links = array_merge($links, $package->getRecommends()); + } + + if ($installSuggests) { + $links = array_merge($links, $package->getSuggests()); + } + + return $links; + } +} From 9404d8d593d3a052d163aea6ca1e1fcc5b3fbaf1 Mon Sep 17 00:00:00 2001 From: Beau Simensen Date: Mon, 5 Mar 2012 15:48:07 -0800 Subject: [PATCH 19/57] Constructor, factory --- src/Composer/Command/InstallCommand.php | 5 +- src/Composer/Command/UpdateCommand.php | 5 +- src/Composer/Install.php | 161 ++++++++++++++++++------ 3 files changed, 125 insertions(+), 46 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 4a0070abd..07b5d480a 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -53,12 +53,9 @@ EOT $composer = $this->getComposer(); $io = $this->getApplication()->getIO(); $eventDispatcher = new EventDispatcher($composer, $io); - $install = new Install; + $install = Install::create($io, $composer, $eventDispatcher); return $install->run( - $io, - $composer, - $eventDispatcher, (Boolean)$input->getOption('prefer-source'), (Boolean)$input->getOption('dry-run'), (Boolean)$input->getOption('verbose'), diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 4ead94d7e..f07f6df20 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -51,12 +51,9 @@ EOT $composer = $this->getComposer(); $io = $this->getApplication()->getIO(); $eventDispatcher = new EventDispatcher($composer, $io); - $install = new Install; + $install = Install::create($io, $composer, $eventDispatcher); return $install->run( - $io, - $composer, - $eventDispatcher, (Boolean)$input->getOption('prefer-source'), (Boolean)$input->getOption('dry-run'), (Boolean)$input->getOption('verbose'), diff --git a/src/Composer/Install.php b/src/Composer/Install.php index d88231cd8..ff1d658b6 100644 --- a/src/Composer/Install.php +++ b/src/Composer/Install.php @@ -18,25 +18,90 @@ use Composer\DependencyResolver\Operation\UpdateOperation; use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; use Composer\DependencyResolver\Solver; +use Composer\Downloader\DownloadManager; +use Composer\Installer\InstallationManager; use Composer\IO\IOInterface; use Composer\Package\AliasPackage; use Composer\Package\Link; use Composer\Package\LinkConstraint\VersionConstraint; +use Composer\Package\Locker; use Composer\Package\PackageInterface; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryInterface; +use Composer\Repository\RepositoryManager; use Composer\Script\EventDispatcher; use Composer\Script\ScriptEvents; class Install { + /** + * + * @var IOInterface + */ + protected $io; + + /** + * + * @var PackageInterface + */ + protected $package; + + /** + * + * @var DownloadManager + */ + protected $downloadManager; + + /** + * + * @var RepositoryManager + */ + protected $repositoryManager; + + /** + * + * @var Locker + */ + protected $locker; + + /** + * + * @var InstallationManager + */ + protected $installationManager; + + /** + * + * @var EventDispatcher + */ + protected $eventDispatcher; + + /** + * Constructor + * + * @param IOInterface $io + * @param PackageInterface $package + * @param DownloadManager $downloadManager + * @param RepositoryManager $repositoryManager + * @param Locker $locker + * @param InstallationManager $installationManager + * @param EventDispatcher $eventDispatcher + */ + public function __construct(IOInterface $io, PackageInterface $package, DownloadManager $downloadManager, RepositoryManager $repositoryManager, Locker $locker, InstallationManager $installationManager, EventDispatcher $eventDispatcher) + { + $this->io = $io; + $this->package = $package; + $this->downloadManager = $downloadManager; + $this->repositoryManager = $repositoryManager; + $this->locker = $locker; + $this->installationManager = $installationManager; + $this->eventDispatcher = $eventDispatcher; + } + /** * Run installation (or update) * - * @param IOInterface $io - * @param Composer $composer - * @param EventDispatcher $eventDispatcher * @param bool $preferSource * @param bool $dryRun * @param bool $verbose @@ -45,17 +110,17 @@ class Install * @param bool $update * @param RepositoryInterface $additionalInstalledRepository */ - public function run(IOInterface $io, Composer $composer, EventDispatcher $eventDispatcher, $preferSource = false, $dryRun = false, $verbose = false, $noInstallRecommends = false, $installSuggests = false, $update = false, RepositoryInterface $additionalInstalledRepository = null) + public function run($preferSource = false, $dryRun = false, $verbose = false, $noInstallRecommends = false, $installSuggests = false, $update = false, RepositoryInterface $additionalInstalledRepository = null) { if ($dryRun) { $verbose = true; } if ($preferSource) { - $composer->getDownloadManager()->setPreferSource(true); + $this->downloadManager->setPreferSource(true); } - $repoManager = $composer->getRepositoryManager(); + $repoManager = $this->repositoryManager; // create local repo, this contains all packages that are installed in the local project $localRepo = $repoManager->getLocalRepository(); @@ -66,10 +131,10 @@ class Install } // prepare aliased packages - if (!$update && $composer->getLocker()->isLocked()) { - $aliases = $composer->getLocker()->getAliases(); + if (!$update && $this->locker->isLocked()) { + $aliases = $this->locker->getAliases(); } else { - $aliases = $composer->getPackage()->getAliases(); + $aliases = $this->package->getAliases(); } foreach ($aliases as $alias) { foreach ($repoManager->findPackages($alias['package'], $alias['version']) as $package) { @@ -91,31 +156,31 @@ class Install // dispatch pre event if (!$dryRun) { $eventName = $update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD; - $eventDispatcher->dispatchCommandEvent($eventName); + $this->eventDispatcher->dispatchCommandEvent($eventName); } // creating requirements request $installFromLock = false; $request = new Request($pool); if ($update) { - $io->write('Updating dependencies'); + $this->io->write('Updating dependencies'); $request->updateAll(); - $links = $this->collectLinks($composer->getPackage(), $noInstallRecommends, $installSuggests); + $links = $this->collectLinks($noInstallRecommends, $installSuggests); foreach ($links as $link) { $request->install($link->getTarget(), $link->getConstraint()); } - } elseif ($composer->getLocker()->isLocked()) { + } elseif ($this->locker->isLocked()) { $installFromLock = true; - $io->write('Installing from lock file'); + $this->io->write('Installing from lock file'); - if (!$composer->getLocker()->isFresh()) { - $io->write('Your lock file is out of sync with your composer.json, run "composer.phar update" to update dependencies'); + if (!$this->locker->isFresh()) { + $this->io->write('Your lock file is out of sync with your composer.json, run "composer.phar update" to update dependencies'); } - foreach ($composer->getLocker()->getLockedPackages() as $package) { + foreach ($this->locker->getLockedPackages() as $package) { $version = $package->getVersion(); foreach ($aliases as $alias) { if ($alias['package'] === $package->getName() && $alias['version'] === $package->getVersion()) { @@ -127,9 +192,9 @@ class Install $request->install($package->getName(), $constraint); } } else { - $io->write('Installing dependencies'); + $this->io->write('Installing dependencies'); - $links = $this->collectLinks($composer->getPackage(), $noInstallRecommends, $installSuggests); + $links = $this->collectLinks($noInstallRecommends, $installSuggests); foreach ($links as $link) { $request->install($link->getTarget(), $link->getConstraint()); @@ -137,9 +202,8 @@ class Install } // prepare solver - $installationManager = $composer->getInstallationManager(); - $policy = new DependencyResolver\DefaultPolicy(); - $solver = new DependencyResolver\Solver($policy, $pool, $installedRepo); + $policy = new DefaultPolicy(); + $solver = new Solver($policy, $pool, $installedRepo); // solve dependencies $operations = $solver->solve($request); @@ -183,15 +247,15 @@ class Install // execute operations if (!$operations) { - $io->write('Nothing to install/update'); + $this->io->write('Nothing to install/update'); } foreach ($operations as $operation) { if ($verbose) { - $io->write((string) $operation); + $this->io->write((string) $operation); } if (!$dryRun) { - $eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType())), $operation); + $this->eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType())), $operation); // if installing from lock, restore dev packages' references to their locked state if ($installFromLock) { @@ -202,7 +266,7 @@ class Install $package = $operation->getPackage(); } if ($package && $package->isDev()) { - $lockData = $composer->getLocker()->getLockData(); + $lockData = $this->locker->getLockData(); foreach ($lockData['packages'] as $lockedPackage) { if (!empty($lockedPackage['source-reference']) && strtolower($lockedPackage['package']) === $package->getName()) { $package->setSourceReference($lockedPackage['source-reference']); @@ -211,42 +275,63 @@ class Install } } } - $installationManager->execute($operation); + $this->installationManager->execute($operation); - $eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType())), $operation); + $this->eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType())), $operation); } } if (!$dryRun) { - if ($update || !$composer->getLocker()->isLocked()) { - $composer->getLocker()->setLockData($localRepo->getPackages(), $aliases); - $io->write('Writing lock file'); + if ($update || !$this->locker->isLocked()) { + $this->locker->setLockData($localRepo->getPackages(), $aliases); + $this->io->write('Writing lock file'); } $localRepo->write(); - $io->write('Generating autoload files'); + $this->io->write('Generating autoload files'); $generator = new AutoloadGenerator; - $generator->dump($localRepo, $composer->getPackage(), $installationManager, $installationManager->getVendorPath().'/.composer'); + $generator->dump($localRepo, $this->package, $this->installationManager, $this->installationManager->getVendorPath().'/.composer'); // dispatch post event $eventName = $update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; - $eventDispatcher->dispatchCommandEvent($eventName); + $this->eventDispatcher->dispatchCommandEvent($eventName); } } - private function collectLinks(PackageInterface $package, $noInstallRecommends, $installSuggests) + private function collectLinks($noInstallRecommends, $installSuggests) { - $links = $package->getRequires(); + $links = $this->package->getRequires(); if (!$noInstallRecommends) { - $links = array_merge($links, $package->getRecommends()); + $links = array_merge($links, $this->package->getRecommends()); } if ($installSuggests) { - $links = array_merge($links, $package->getSuggests()); + $links = array_merge($links, $this->package->getSuggests()); } return $links; } + + /** + * Create Install + * + * @param IOInterface $io + * @param Composer $composer + * @param EventDispatcher $eventDispatcher + * @return Install + */ + static public function create(IOInterface $io, Composer $composer, EventDispatcher $eventDispatcher) + { + return new static( + $io, + $composer->getPackage(), + $composer->getDownloadManager(), + $composer->getRepositoryManager(), + $composer->getLocker(), + $composer->getInstallationManager(), + $eventDispatcher + ); + } } From b4dd86c2477d0611cf203a88a24afb9abdd56ecc Mon Sep 17 00:00:00 2001 From: Beau Simensen Date: Mon, 5 Mar 2012 20:28:37 -0800 Subject: [PATCH 20/57] Remove $repoManager local variable. --- src/Composer/Install.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Composer/Install.php b/src/Composer/Install.php index ff1d658b6..5ca2dec4d 100644 --- a/src/Composer/Install.php +++ b/src/Composer/Install.php @@ -120,10 +120,10 @@ class Install $this->downloadManager->setPreferSource(true); } - $repoManager = $this->repositoryManager; + $this->repositoryManager = $this->repositoryManager; // create local repo, this contains all packages that are installed in the local project - $localRepo = $repoManager->getLocalRepository(); + $localRepo = $this->repositoryManager->getLocalRepository(); // create installed repo, this contains all local packages + platform packages (php & extensions) $installedRepo = new CompositeRepository(array($localRepo, new PlatformRepository())); if ($additionalInstalledRepository) { @@ -137,19 +137,19 @@ class Install $aliases = $this->package->getAliases(); } foreach ($aliases as $alias) { - foreach ($repoManager->findPackages($alias['package'], $alias['version']) as $package) { + foreach ($this->repositoryManager->findPackages($alias['package'], $alias['version']) as $package) { $package->getRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); } - foreach ($repoManager->getLocalRepository()->findPackages($alias['package'], $alias['version']) as $package) { - $repoManager->getLocalRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); - $repoManager->getLocalRepository()->removePackage($package); + foreach ($this->repositoryManager->getLocalRepository()->findPackages($alias['package'], $alias['version']) as $package) { + $this->repositoryManager->getLocalRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); + $this->repositoryManager->getLocalRepository()->removePackage($package); } } // creating repository pool $pool = new Pool; $pool->addRepository($installedRepo); - foreach ($repoManager->getRepositories() as $repository) { + foreach ($this->repositoryManager->getRepositories() as $repository) { $pool->addRepository($repository); } @@ -230,7 +230,7 @@ class Install } // force update - $newPackage = $repoManager->findPackage($package->getName(), $package->getVersion()); + $newPackage = $this->repositoryManager->findPackage($package->getName(), $package->getVersion()); if ($newPackage && $newPackage->getSourceReference() !== $package->getSourceReference()) { $operations[] = new UpdateOperation($package, $newPackage); } @@ -238,10 +238,10 @@ class Install } // anti-alias local repository to allow updates to work fine - foreach ($repoManager->getLocalRepository()->getPackages() as $package) { + foreach ($this->repositoryManager->getLocalRepository()->getPackages() as $package) { if ($package instanceof AliasPackage) { - $repoManager->getLocalRepository()->addPackage(clone $package->getAliasOf()); - $repoManager->getLocalRepository()->removePackage($package); + $this->repositoryManager->getLocalRepository()->addPackage(clone $package->getAliasOf()); + $this->repositoryManager->getLocalRepository()->removePackage($package); } } From 8a7d31706da6626df79ad4f8403de9265f5c8887 Mon Sep 17 00:00:00 2001 From: Beau Simensen Date: Tue, 6 Mar 2012 15:30:18 -0800 Subject: [PATCH 21/57] Renamed Composer\Install to Composer\Installer --- src/Composer/Command/InstallCommand.php | 4 ++-- src/Composer/Command/UpdateCommand.php | 4 ++-- src/Composer/{Install.php => Installer.php} | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) rename src/Composer/{Install.php => Installer.php} (99%) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 07b5d480a..e8744af5a 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -12,7 +12,7 @@ namespace Composer\Command; -use Composer\Install; +use Composer\Installer; use Composer\Script\EventDispatcher; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -53,7 +53,7 @@ EOT $composer = $this->getComposer(); $io = $this->getApplication()->getIO(); $eventDispatcher = new EventDispatcher($composer, $io); - $install = Install::create($io, $composer, $eventDispatcher); + $install = Installer::create($io, $composer, $eventDispatcher); return $install->run( (Boolean)$input->getOption('prefer-source'), diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index f07f6df20..69640bd3d 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -12,7 +12,7 @@ namespace Composer\Command; -use Composer\Install; +use Composer\Installer; use Composer\Script\EventDispatcher; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -51,7 +51,7 @@ EOT $composer = $this->getComposer(); $io = $this->getApplication()->getIO(); $eventDispatcher = new EventDispatcher($composer, $io); - $install = Install::create($io, $composer, $eventDispatcher); + $install = Installer::create($io, $composer, $eventDispatcher); return $install->run( (Boolean)$input->getOption('prefer-source'), diff --git a/src/Composer/Install.php b/src/Composer/Installer.php similarity index 99% rename from src/Composer/Install.php rename to src/Composer/Installer.php index 5ca2dec4d..6bb13eb86 100644 --- a/src/Composer/Install.php +++ b/src/Composer/Installer.php @@ -33,7 +33,7 @@ use Composer\Repository\RepositoryManager; use Composer\Script\EventDispatcher; use Composer\Script\ScriptEvents; -class Install +class Installer { /** * @@ -315,12 +315,12 @@ class Install } /** - * Create Install + * Create Installer * * @param IOInterface $io * @param Composer $composer * @param EventDispatcher $eventDispatcher - * @return Install + * @return Installer */ static public function create(IOInterface $io, Composer $composer, EventDispatcher $eventDispatcher) { From 3352066ecec47b67ae2550a7cb9b75057976e80f Mon Sep 17 00:00:00 2001 From: Beau Simensen Date: Thu, 8 Mar 2012 09:58:19 -0800 Subject: [PATCH 22/57] Use Installer for Create Project Command. --- src/Composer/Command/CreateProjectCommand.php | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index d21fafb64..ea6c4cd42 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -12,16 +12,18 @@ namespace Composer\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Output\OutputInterface; -use Composer\IO\IOInterface; use Composer\Factory; +use Composer\Installer; +use Composer\Installer\ProjectInstaller; +use Composer\IO\IOInterface; use Composer\Repository\ComposerRepository; use Composer\Repository\FilesystemRepository; -use Composer\Installer\ProjectInstaller; +use Composer\Script\EventDispatcher; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; /** * Install a package as new project into new directory. @@ -76,15 +78,6 @@ EOT ); } - protected function getInstallCommand($input, $output) - { - $app = $this->getApplication(); - return function() use ($app, $input, $output) { - $newInput = new ArrayInput(array('command' => 'install')); - $app->doRUn($newInput, $output); - }; - } - public function installProject(IOInterface $io, $installCommand, $packageName, $directory = null, $version = null, $preferSource = false, $repositoryUrl = null) { $dm = $this->createDownloadManager($io); @@ -126,7 +119,12 @@ EOT $io->write('Created project into directory ' . $directory . '', true); chdir($directory); - $installCommand(); + + $composer = Factory::create($io); + $eventDispatcher = new EventDispatcher($composer, $io); + $installer = Installer::create($io, $composer, $eventDispatcher); + + $installer->run($preferSource); } protected function createDownloadManager(IOInterface $io) From b1c93d1f0a5f659ff1099cfa22cdcb7c50f3c7f7 Mon Sep 17 00:00:00 2001 From: Beau Simensen Date: Thu, 8 Mar 2012 11:04:56 -0800 Subject: [PATCH 23/57] Fixed unused `use` per @stof and horrible bug that made this class no longer work. --- src/Composer/Command/CreateProjectCommand.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index ea6c4cd42..04b6266f2 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -19,7 +19,6 @@ use Composer\IO\IOInterface; use Composer\Repository\ComposerRepository; use Composer\Repository\FilesystemRepository; use Composer\Script\EventDispatcher; -use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -69,7 +68,6 @@ EOT return $this->installProject( $io, - $this->getInstallCommand($input, $output), $input->getArgument('package'), $input->getArgument('directory'), $input->getArgument('version'), @@ -78,7 +76,7 @@ EOT ); } - public function installProject(IOInterface $io, $installCommand, $packageName, $directory = null, $version = null, $preferSource = false, $repositoryUrl = null) + public function installProject(IOInterface $io, $packageName, $directory = null, $version = null, $preferSource = false, $repositoryUrl = null) { $dm = $this->createDownloadManager($io); if ($preferSource) { From d5d133e584151b944f09e3e79f9b3031fe8c0f4d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 8 Mar 2012 21:08:55 +0100 Subject: [PATCH 24/57] Move create projects article --- doc/{ => articles}/create-projects.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/{ => articles}/create-projects.md (100%) diff --git a/doc/create-projects.md b/doc/articles/create-projects.md similarity index 100% rename from doc/create-projects.md rename to doc/articles/create-projects.md From 53ab5011f0ac00eff69887275c0d2d9999b6b2c7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 8 Mar 2012 21:12:43 +0100 Subject: [PATCH 25/57] Update create projects docs --- doc/articles/create-projects.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/articles/create-projects.md b/doc/articles/create-projects.md index c1e220139..9dda91117 100644 --- a/doc/articles/create-projects.md +++ b/doc/articles/create-projects.md @@ -7,11 +7,13 @@ There are several applications for this: 2. You can check out any package and start developing on patches for example. 3. Projects with multiple developers can use this feature to bootstrap the initial application for development. -To create a new project using composer you can use the "create-project", -pass it a package name + version and a directory to create the project in. +To create a new project using composer you can use the "create-project" command. +Pass it a package name, and the directory to create the project in. You can also +provide a version as third argument, otherwise the latest version is used. + The directory is not allowed to exist, it will be created during installation. - php composer.phar create-project doctrine/orm 2.2.0 /path/to/new-project + php composer.phar create-project doctrine/orm path 2.2.0 By default the command checks for the packages on packagist.org. To change this behavior you can use the --repository-url parameter and either point it to an HTTP url @@ -19,4 +21,3 @@ for your own packagist repository or to a packages.json file. If you want to get a development version of the code directly checked out from version control you have to add the --prefer-source parameter. - From 3e22084ea4c44f7c8c8c8d13ecdd819f9baf914f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 8 Mar 2012 21:59:02 +0100 Subject: [PATCH 26/57] Overhaul VcsDrivers, introduce TransportException for remote filesystem errors --- .../Downloader/TransportException.php | 20 +++++++++++++ .../Repository/Vcs/GitBitbucketDriver.php | 24 ++++------------ src/Composer/Repository/Vcs/GitDriver.php | 18 ++---------- src/Composer/Repository/Vcs/GitHubDriver.php | 26 ++++------------- .../Repository/Vcs/HgBitbucketDriver.php | 28 +++++-------------- src/Composer/Repository/Vcs/HgDriver.php | 18 ++---------- src/Composer/Repository/Vcs/SvnDriver.php | 18 ++---------- src/Composer/Repository/Vcs/VcsDriver.php | 17 ++++++++++- src/Composer/Repository/VcsRepository.php | 6 +++- src/Composer/Util/RemoteFilesystem.php | 11 ++++---- .../Test/Util/RemoteFilesystemTest.php | 4 +-- 11 files changed, 73 insertions(+), 117 deletions(-) create mode 100644 src/Composer/Downloader/TransportException.php diff --git a/src/Composer/Downloader/TransportException.php b/src/Composer/Downloader/TransportException.php new file mode 100644 index 000000000..61bd67d11 --- /dev/null +++ b/src/Composer/Downloader/TransportException.php @@ -0,0 +1,20 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Downloader; + +/** + * @author Jordi Boggiano + */ +class TransportException extends \Exception +{ +} diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php index 20e060e68..8021a2cb8 100644 --- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -49,7 +49,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository), true); + $repoData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository)); $this->rootIdentifier = !empty($repoData['main_branch']) ? $repoData['main_branch'] : 'master'; } @@ -93,13 +93,13 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface if (!isset($this->infoCache[$identifier])) { $composer = $this->getContents($this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json'); if (!$composer) { - throw new \UnexpectedValueException('Failed to retrieve composer information for identifier '.$identifier.' in '.$this->getUrl()); + return; } $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $changeset = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); + $changeset = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier)); $composer['time'] = $changeset['timestamp']; } $this->infoCache[$identifier] = $composer; @@ -114,7 +114,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags')); $this->tags = array(); foreach ($tagsData as $tag => $data) { $this->tags[$tag] = $data['raw_node']; @@ -130,7 +130,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches')); $this->branches = array(); foreach ($branchData as $branch => $data) { $this->branches[$branch] = $data['raw_node']; @@ -140,20 +140,6 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface return $this->branches; } - /** - * {@inheritDoc} - */ - public function hasComposerFile($identifier) - { - try { - $this->getComposerInformation($identifier); - return true; - } catch (\Exception $e) { - } - - return false; - } - /** * {@inheritDoc} */ diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index d6c49daf2..90f5ff926 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -9,7 +9,7 @@ use Composer\IO\IOInterface; /** * @author Jordi Boggiano */ -class GitDriver extends VcsDriver implements VcsDriverInterface +class GitDriver extends VcsDriver { protected $tags; protected $branches; @@ -117,7 +117,7 @@ class GitDriver extends VcsDriver implements VcsDriverInterface $this->process->execute(sprintf('cd %s && git show %s:composer.json', escapeshellarg($this->repoDir), escapeshellarg($identifier)), $composer); if (!trim($composer)) { - throw new \UnexpectedValueException('Failed to retrieve composer information for identifier '.$identifier.' in '.$this->getUrl()); + return; } $composer = JsonFile::parseJson($composer); @@ -173,20 +173,6 @@ class GitDriver extends VcsDriver implements VcsDriverInterface return $this->branches; } - /** - * {@inheritDoc} - */ - public function hasComposerFile($identifier) - { - try { - $this->getComposerInformation($identifier); - return true; - } catch (\Exception $e) { - } - - return false; - } - /** * {@inheritDoc} */ diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 13071a594..01371c1a2 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -8,7 +8,7 @@ use Composer\IO\IOInterface; /** * @author Jordi Boggiano */ -class GitHubDriver extends VcsDriver implements VcsDriverInterface +class GitHubDriver extends VcsDriver { protected $owner; protected $repository; @@ -39,7 +39,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository), true); + $repoData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository)); $this->rootIdentifier = $repoData['master_branch'] ?: 'master'; } @@ -83,13 +83,13 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface if (!isset($this->infoCache[$identifier])) { $composer = $this->getContents($this->getScheme() . '://raw.github.com/'.$this->owner.'/'.$this->repository.'/'.$identifier.'/composer.json'); if (!$composer) { - throw new \UnexpectedValueException('Failed to retrieve composer information for identifier '.$identifier.' in '.$this->getUrl()); + return; } $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $commit = json_decode($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/commits/'.$identifier), true); + $commit = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/commits/'.$identifier)); $composer['time'] = $commit['commit']['committer']['date']; } $this->infoCache[$identifier] = $composer; @@ -104,7 +104,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/tags')); $this->tags = array(); foreach ($tagsData as $tag) { $this->tags[$tag['name']] = $tag['commit']['sha']; @@ -120,7 +120,7 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/branches')); $this->branches = array(); foreach ($branchData as $branch) { $this->branches[$branch['name']] = $branch['commit']['sha']; @@ -130,20 +130,6 @@ class GitHubDriver extends VcsDriver implements VcsDriverInterface return $this->branches; } - /** - * {@inheritDoc} - */ - public function hasComposerFile($identifier) - { - try { - $this->getComposerInformation($identifier); - return true; - } catch (\Exception $e) { - } - - return false; - } - /** * {@inheritDoc} */ diff --git a/src/Composer/Repository/Vcs/HgBitbucketDriver.php b/src/Composer/Repository/Vcs/HgBitbucketDriver.php index 6d2e8b066..54dfd5d30 100644 --- a/src/Composer/Repository/Vcs/HgBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/HgBitbucketDriver.php @@ -18,7 +18,7 @@ use Composer\IO\IOInterface; /** * @author Per Bernhardt */ -class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface +class HgBitbucketDriver extends VcsDriver { protected $owner; protected $repository; @@ -49,7 +49,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $repoData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags')); $this->rootIdentifier = $repoData['tip']['raw_node']; } @@ -93,13 +93,13 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface if (!isset($this->infoCache[$identifier])) { $composer = $this->getContents($this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json'); if (!$composer) { - throw new \UnexpectedValueException('Failed to retrieve composer information for identifier '.$identifier.' in '.$this->getUrl()); + return; } $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { - $changeset = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier), true); + $changeset = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier)); $composer['time'] = $changeset['timestamp']; } $this->infoCache[$identifier] = $composer; @@ -114,7 +114,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'), true); + $tagsData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags')); $this->tags = array(); foreach ($tagsData as $tag => $data) { $this->tags[$tag] = $data['raw_node']; @@ -130,7 +130,7 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = json_decode($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches'), true); + $branchData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches')); $this->branches = array(); foreach ($branchData as $branch => $data) { $this->branches[$branch] = $data['raw_node']; @@ -140,25 +140,11 @@ class HgBitbucketDriver extends VcsDriver implements VcsDriverInterface return $this->branches; } - /** - * {@inheritDoc} - */ - public function hasComposerFile($identifier) - { - try { - $this->getComposerInformation($identifier); - return true; - } catch (\Exception $e) { - } - - return false; - } - /** * {@inheritDoc} */ public static function supports($url, $deep = false) { - return preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url); + return extension_loaded('openssl') && preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url); } } diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index 1ca21948d..f7390fb47 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -19,7 +19,7 @@ use Composer\IO\IOInterface; /** * @author Per Bernhardt */ -class HgDriver extends VcsDriver implements VcsDriverInterface +class HgDriver extends VcsDriver { protected $tags; protected $branches; @@ -100,7 +100,7 @@ class HgDriver extends VcsDriver implements VcsDriverInterface $this->process->execute(sprintf('cd %s && hg cat -r %s composer.json', escapeshellarg($this->tmpDir), escapeshellarg($identifier)), $composer); if (!trim($composer)) { - throw new \UnexpectedValueException('Failed to retrieve composer information for identifier ' . $identifier . ' in ' . $this->getUrl()); + return; } $composer = JsonFile::parseJson($composer); @@ -159,20 +159,6 @@ class HgDriver extends VcsDriver implements VcsDriverInterface return $this->branches; } - /** - * {@inheritDoc} - */ - public function hasComposerFile($identifier) - { - try { - $this->getComposerInformation($identifier); - return true; - } catch (\Exception $e) { - } - - return false; - } - /** * {@inheritDoc} */ diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 2d428f6d4..f9b986c91 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -9,7 +9,7 @@ use Composer\IO\IOInterface; /** * @author Jordi Boggiano */ -class SvnDriver extends VcsDriver implements VcsDriverInterface +class SvnDriver extends VcsDriver { protected $baseUrl; protected $tags; @@ -108,7 +108,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface ); if (!trim($composer)) { - throw new \UnexpectedValueException('Failed to retrieve composer information for identifier '.$identifier.' in '.$this->getUrl()); + return; } $composer = JsonFile::parseJson($composer); @@ -226,20 +226,6 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface ); } - /** - * {@inheritDoc} - */ - public function hasComposerFile($identifier) - { - try { - $this->getComposerInformation($identifier); - return true; - } catch (\Exception $e) { - } - - return false; - } - /** * {@inheritDoc} */ diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index 75b631a42..a20e959fd 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -12,6 +12,7 @@ namespace Composer\Repository\Vcs; +use Composer\Downloader\TransportException; use Composer\IO\IOInterface; use Composer\Util\ProcessExecutor; use Composer\Util\RemoteFilesystem; @@ -21,7 +22,7 @@ use Composer\Util\RemoteFilesystem; * * @author François Pluchino */ -abstract class VcsDriver +abstract class VcsDriver implements VcsDriverInterface { protected $url; protected $io; @@ -41,6 +42,20 @@ abstract class VcsDriver $this->process = $process ?: new ProcessExecutor; } + /** + * {@inheritDoc} + */ + public function hasComposerFile($identifier) + { + try { + return (Boolean) $this->getComposerInformation($identifier); + } catch (TransportException $e) { + } + + return false; + } + + /** * Get the https or http protocol depending on SSL support. * diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index afdc12bcb..fa37a62aa 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -2,6 +2,7 @@ namespace Composer\Repository; +use Composer\Downloader\TransportException; use Composer\Repository\Vcs\VcsDriverInterface; use Composer\Package\Version\VersionParser; use Composer\Package\PackageInterface; @@ -90,11 +91,14 @@ class VcsRepository extends ArrayRepository if ($parsedTag && $driver->hasComposerFile($identifier)) { try { $data = $driver->getComposerInformation($identifier); - } catch (\Exception $e) { + } catch (TransportException $e) { if ($debug) { $this->io->write('Skipped tag '.$tag.', '.$e->getMessage()); } continue; + } catch (\Exception $e) { + $this->io->write('Skipped tag '.$tag.', '.$e->getMessage()); + continue; } // manually versioned package diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 7bd0f3bfe..bc50b06fb 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -13,6 +13,7 @@ namespace Composer\Util; use Composer\IO\IOInterface; +use Composer\Downloader\TransportException; /** * @author François Pluchino @@ -81,7 +82,7 @@ class RemoteFilesystem * @param boolean $progress Display the progression * @param boolean $firstCall Whether this is the first attempt at fetching this resource * - * @throws \RuntimeException When the file could not be downloaded + * @throws TransportException When the file could not be downloaded */ protected function get($originUrl, $fileUrl, $fileName = null, $progress = true, $firstCall = true) { @@ -117,7 +118,7 @@ class RemoteFilesystem } if (false === $this->result) { - throw new \RuntimeException("The '$fileUrl' file could not be downloaded"); + throw new TransportException("The '$fileUrl' file could not be downloaded"); } } @@ -137,7 +138,7 @@ class RemoteFilesystem case STREAM_NOTIFY_AUTH_REQUIRED: case STREAM_NOTIFY_FAILURE: if (404 === $messageCode && !$this->firstCall) { - throw new \RuntimeException("The '" . $this->fileUrl . "' URL not found"); + throw new TransportException("The '" . $this->fileUrl . "' URL not found", 404); } // for private repository returning 404 error when the authorization is incorrect @@ -149,9 +150,9 @@ class RemoteFilesystem // get authorization informations if (401 === $messageCode || $attemptAuthentication) { if (!$this->io->isInteractive()) { - $mess = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console"; + $message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console"; - throw new \RuntimeException($mess); + throw new TransportException($message, 401); } $this->io->overwrite(' Authentication required ('.parse_url($this->fileUrl, PHP_URL_HOST).'):'); diff --git a/tests/Composer/Test/Util/RemoteFilesystemTest.php b/tests/Composer/Test/Util/RemoteFilesystemTest.php index c7e5c076e..bee389941 100644 --- a/tests/Composer/Test/Util/RemoteFilesystemTest.php +++ b/tests/Composer/Test/Util/RemoteFilesystemTest.php @@ -111,7 +111,7 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase $this->callCallbackGet($fs, STREAM_NOTIFY_FAILURE, 0, '', 404, 0, 0); $this->fail(); } catch (\Exception $e) { - $this->assertInstanceOf('RuntimeException', $e); + $this->assertInstanceOf('Composer\Downloader\TransportException', $e); $this->assertContains('URL not found', $e->getMessage()); } } @@ -137,7 +137,7 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase $this->callCallbackGet($fs, STREAM_NOTIFY_FAILURE, 0, '', 404, 0, 0); $this->fail(); } catch (\Exception $e) { - $this->assertInstanceOf('RuntimeException', $e); + $this->assertInstanceOf('Composer\Downloader\TransportException', $e); $this->assertContains('URL required authentication', $e->getMessage()); $this->assertAttributeEquals(false, 'firstCall', $fs); } From 98d7e31c74d757e5a1ad237b80cbad9000b09c15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Haso=C5=88?= Date: Thu, 8 Mar 2012 13:01:59 +0100 Subject: [PATCH 27/57] Sort versions before print --- src/Composer/Command/ShowCommand.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 299e96fe4..5e8bf262a 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -165,10 +165,12 @@ EOT $versions = array(); foreach ($repos->findPackages($package->getName()) as $version) { - $versions[$version->getPrettyVersion()] = true; + $versions[$version->getPrettyVersion()] = $version->getVersion(); } - $versions = join(', ', array_keys($versions)); + uasort($versions, 'version_compare'); + + $versions = join(', ', array_keys(array_reverse($versions))); // highlight installed version if ($installedRepo->hasPackage($package)) { @@ -193,4 +195,4 @@ EOT } } } -} \ No newline at end of file +} From 947d429c617c00f430f64f4bd824d0ace034b496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Haso=C5=88?= Date: Fri, 9 Mar 2012 09:31:51 +0100 Subject: [PATCH 28/57] Removed definition of global constants reserved for PHP >=5.4 and fixed tests --- src/Composer/Json/JsonFile.php | 22 ++++++++-------------- tests/Composer/Test/Json/JsonFileTest.php | 20 +++++++++++++++----- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index 1b20af274..458265e3e 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -18,16 +18,6 @@ use JsonSchema\Validator; use Seld\JsonLint\JsonParser; use Composer\Util\StreamContextFactory; -if (!defined('JSON_UNESCAPED_SLASHES')) { - define('JSON_UNESCAPED_SLASHES', 64); -} -if (!defined('JSON_PRETTY_PRINT')) { - define('JSON_PRETTY_PRINT', 128); -} -if (!defined('JSON_UNESCAPED_UNICODE')) { - define('JSON_UNESCAPED_UNICODE', 256); -} - /** * Reads/writes json files. * @@ -39,6 +29,10 @@ class JsonFile const LAX_SCHEMA = 1; const STRICT_SCHEMA = 2; + const JSON_UNESCAPED_SLASHES = 64; + const JSON_PRETTY_PRINT = 128; + const JSON_UNESCAPED_UNICODE = 256; + private $path; /** @@ -108,7 +102,7 @@ class JsonFile ); } } - file_put_contents($this->path, static::encode($hash, $options). ($options & JSON_PRETTY_PRINT ? "\n" : '')); + file_put_contents($this->path, static::encode($hash, $options). ($options & self::JSON_PRETTY_PRINT ? "\n" : '')); } /** @@ -170,9 +164,9 @@ class JsonFile $json = json_encode($data); - $prettyPrint = (Boolean) ($options & JSON_PRETTY_PRINT); - $unescapeUnicode = (Boolean) ($options & JSON_UNESCAPED_UNICODE); - $unescapeSlashes = (Boolean) ($options & JSON_UNESCAPED_SLASHES); + $prettyPrint = (Boolean) ($options & self::JSON_PRETTY_PRINT); + $unescapeUnicode = (Boolean) ($options & self::JSON_UNESCAPED_UNICODE); + $unescapeSlashes = (Boolean) ($options & self::JSON_UNESCAPED_SLASHES); if (!$prettyPrint && !$unescapeUnicode && !$unescapeSlashes) { return $json; diff --git a/tests/Composer/Test/Json/JsonFileTest.php b/tests/Composer/Test/Json/JsonFileTest.php index 3090a450f..4e30928cb 100644 --- a/tests/Composer/Test/Json/JsonFileTest.php +++ b/tests/Composer/Test/Json/JsonFileTest.php @@ -140,9 +140,10 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase public function testUnicode() { - if (!function_exists('mb_convert_encoding')) { + if (!function_exists('mb_convert_encoding') && version_compare(PHP_VERSION, '5.4', '<')) { $this->markTestSkipped('Test requires the mbstring extension'); } + $data = array("Žluťoučký \" kůň" => "úpěl ďábelské ódy za €"); $json = '{ "Žluťoučký \" kůň": "úpěl ďábelské ódy za €" @@ -151,14 +152,23 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase $this->assertJsonFormat($json, $data); } - public function testEscapedSlashes() + public function testOnlyUnicode() { - if (!function_exists('mb_convert_encoding')) { + if (!function_exists('mb_convert_encoding') && version_compare(PHP_VERSION, '5.4', '<')) { $this->markTestSkipped('Test requires the mbstring extension'); } - $data = "\\/fooƌ"; - $this->assertJsonFormat('"\\\\\\/fooƌ"', $data, JSON_UNESCAPED_UNICODE); + $data = "\\/ƌ"; + + $this->assertJsonFormat('"\\\\\\/ƌ"', $data, JsonFile::JSON_UNESCAPED_UNICODE); + } + + public function testEscapedSlashes() + { + + $data = "\\/foo"; + + $this->assertJsonFormat('"\\\\\\/foo"', $data, 0); } public function testEscapedUnicode() From b74773053bfb46879b85cbcaf15df3cbced67a30 Mon Sep 17 00:00:00 2001 From: DonMartio Date: Fri, 9 Mar 2012 10:04:52 +0100 Subject: [PATCH 29/57] Missing slash prevents version evaluation. --- src/Composer/Repository/Vcs/SvnDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 2d428f6d4..1abe1174d 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -90,7 +90,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface { $identifier = '/' . trim($identifier, '/') . '/'; if (!isset($this->infoCache[$identifier])) { - preg_match('{^(.+?)(@\d+)?$}', $identifier, $match); + preg_match('{^(.+?)(@\d+)?/$}', $identifier, $match); if (!empty($match[2])) { $identifier = $match[1]; $rev = $match[2]; From 1d544630b65faaa00b1af95885c52ab30bd05383 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 9 Mar 2012 18:30:59 +0100 Subject: [PATCH 30/57] Allow requesting a particular vcs driver to bypass github/bitbucket ones --- src/Composer/Factory.php | 5 ++++- src/Composer/Repository/VcsRepository.php | 21 +++++++++++++------ .../Test/Repository/VcsRepositoryTest.php | 2 +- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 4fefb31af..3f4812927 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -123,8 +123,11 @@ class Factory $rm = new RepositoryManager($io); $rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository'); $rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository'); - $rm->setRepositoryClass('pear', 'Composer\Repository\PearRepository'); $rm->setRepositoryClass('package', 'Composer\Repository\PackageRepository'); + $rm->setRepositoryClass('pear', 'Composer\Repository\PearRepository'); + $rm->setRepositoryClass('git', 'Composer\Repository\VcsRepository'); + $rm->setRepositoryClass('svn', 'Composer\Repository\VcsRepository'); + $rm->setRepositoryClass('hg', 'Composer\Repository\VcsRepository'); return $rm; } diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index fa37a62aa..490f807e0 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -20,20 +20,22 @@ class VcsRepository extends ArrayRepository protected $debug; protected $io; protected $versionParser; + protected $type; public function __construct(array $config, IOInterface $io, array $drivers = null) { $this->drivers = $drivers ?: array( - 'Composer\Repository\Vcs\GitHubDriver', - 'Composer\Repository\Vcs\GitBitbucketDriver', - 'Composer\Repository\Vcs\GitDriver', - 'Composer\Repository\Vcs\SvnDriver', - 'Composer\Repository\Vcs\HgBitbucketDriver', - 'Composer\Repository\Vcs\HgDriver', + 'github' => 'Composer\Repository\Vcs\GitHubDriver', + 'git-bitbucket' => 'Composer\Repository\Vcs\GitBitbucketDriver', + 'git' => 'Composer\Repository\Vcs\GitDriver', + 'svn' => 'Composer\Repository\Vcs\SvnDriver', + 'hg-bitbucket' => 'Composer\Repository\Vcs\HgBitbucketDriver', + 'hg' => 'Composer\Repository\Vcs\HgDriver', ); $this->url = $config['url']; $this->io = $io; + $this->type = $config['type']; } public function setDebug($debug) @@ -43,6 +45,13 @@ class VcsRepository extends ArrayRepository public function getDriver() { + if (isset($this->drivers[$this->type])) { + $class = $this->drivers[$this->type]; + $driver = new $class($this->url, $this->io); + $driver->initialize(); + return $driver; + } + foreach ($this->drivers as $driver) { if ($driver::supports($this->url)) { $driver = new $driver($this->url, $this->io); diff --git a/tests/Composer/Test/Repository/VcsRepositoryTest.php b/tests/Composer/Test/Repository/VcsRepositoryTest.php index 255162354..7c008f24f 100644 --- a/tests/Composer/Test/Repository/VcsRepositoryTest.php +++ b/tests/Composer/Test/Repository/VcsRepositoryTest.php @@ -123,7 +123,7 @@ class VcsRepositoryTest extends \PHPUnit_Framework_TestCase 'dev-master' => true, ); - $repo = new VcsRepository(array('url' => self::$gitRepo), new NullIO); + $repo = new VcsRepository(array('url' => self::$gitRepo, 'type' => 'vcs'), new NullIO); $packages = $repo->getPackages(); $dumper = new ArrayDumper(); From 142d5785f495ba4d76c6ff84df80fda30883b1e3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 9 Mar 2012 18:32:49 +0100 Subject: [PATCH 31/57] Updated docs --- doc/05-repositories.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 7485a3550..cd8b94a89 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -138,7 +138,9 @@ VCS repository provides `dist`s for them that fetch the packages as zips. * **GitHub:** [github.com](https://github.com) (Git) * **BitBucket:** [bitbucket.org](https://bitbucket.org) (Git and Mercurial) -The VCS driver to be used is detected automatically based on the URL. +The VCS driver to be used is detected automatically based on the URL. However, +should you need to specify one for whatever reason, you can use `git`, `svn` or +`hg` as the repository type instead of `vcs`. ### PEAR From 8dad8d8c43dd953a6b74f4647f6b985648c979a1 Mon Sep 17 00:00:00 2001 From: digitalkaoz Date: Fri, 9 Mar 2012 19:30:37 +0100 Subject: [PATCH 32/57] added some trivial tests --- tests/Composer/Test/ComposerTest.php | 62 ++++++++++++++ .../Test/Downloader/ArchiveDownloaderTest.php | 17 ++++ tests/Composer/Test/IO/NullIOTest.php | 84 +++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 tests/Composer/Test/ComposerTest.php create mode 100644 tests/Composer/Test/IO/NullIOTest.php diff --git a/tests/Composer/Test/ComposerTest.php b/tests/Composer/Test/ComposerTest.php new file mode 100644 index 000000000..e1fb8de3c --- /dev/null +++ b/tests/Composer/Test/ComposerTest.php @@ -0,0 +1,62 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test; + +use Composer\Composer; + +class ComposerTest extends TestCase +{ + public function testSetGetPackage() + { + $composer = new Composer(); + $package = $this->getMock('Composer\Package\PackageInterface'); + $composer->setPackage($package); + + $this->assertEquals($package,$composer->getPackage()); + } + + public function testSetGetLocker() + { + $composer = new Composer(); + $locker = $this->getMockBuilder('Composer\Package\Locker')->disableOriginalConstructor()->getMock(); + $composer->setLocker($locker); + + $this->assertEquals($locker,$composer->getLocker()); + } + + public function testSetGetRepositoryManager() + { + $composer = new Composer(); + $manager = $this->getMockBuilder('Composer\Repository\RepositoryManager')->disableOriginalConstructor()->getMock(); + $composer->setRepositoryManager($manager); + + $this->assertEquals($manager,$composer->getRepositoryManager()); + } + + public function testSetGetDownloadManager() + { + $composer = new Composer(); + $manager = $this->getMock('Composer\Downloader\DownloadManager'); + $composer->setDownloadManager($manager); + + $this->assertEquals($manager,$composer->getDownloadManager()); + } + + public function testSetGetInstallationManager() + { + $composer = new Composer(); + $manager = $this->getMock('Composer\Installer\InstallationManager'); + $composer->setInstallationManager($manager); + + $this->assertEquals($manager,$composer->getInstallationManager()); + } +} \ No newline at end of file diff --git a/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php b/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php index fbbf2b269..ce4a2b0fe 100644 --- a/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php +++ b/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php @@ -32,4 +32,21 @@ class ArchiveDownloaderTest extends \PHPUnit_Framework_TestCase $this->assertRegExp('#/path/[a-z0-9]+\.js#', $first); $this->assertSame($first, $method->invoke($downloader, $packageMock, '/path')); } + + public function testProcessUrl() + { + $downloader = $this->getMockForAbstractClass('Composer\Downloader\ArchiveDownloader', array($this->getMock('Composer\IO\IOInterface'))); + $method = new \ReflectionMethod($downloader, 'processUrl'); + $method->setAccessible(true); + + $expected = 'https://github.com/composer/composer/zipball/master'; + $url = $method->invoke($downloader, $expected); + + if(extension_loaded('openssl')) + { + $this->assertEquals($expected, $url); + } else { + $this->assertEquals('http://nodeload.github.com/composer/composer/zipball/master', $url); + } + } } diff --git a/tests/Composer/Test/IO/NullIOTest.php b/tests/Composer/Test/IO/NullIOTest.php new file mode 100644 index 000000000..406db336d --- /dev/null +++ b/tests/Composer/Test/IO/NullIOTest.php @@ -0,0 +1,84 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\IO; + +use Composer\IO\NullIO; +use Composer\Test\TestCase; + +class NullIOTest extends TestCase +{ + public function testIsInteractive() + { + $io = new NullIO(); + + $this->assertFalse($io->isInteractive()); + } + + public function testHasAuthorization() + { + $io = new NullIO(); + + $this->assertFalse($io->hasAuthorization('foo')); + } + + public function testGetLastPassword() + { + $io = new NullIO(); + + $this->assertNull($io->getLastPassword()); + } + + public function testGetLastUsername() + { + $io = new NullIO(); + + $this->assertNull($io->getLastUsername()); + } + + public function testAskAndHideAnswer() + { + $io = new NullIO(); + + $this->assertNull($io->askAndHideAnswer('foo')); + } + + public function testGetAuthorizations() + { + $io = new NullIO(); + + $this->assertInternalType('array',$io->getAuthorizations()); + $this->assertEmpty($io->getAuthorizations()); + $this->assertEquals(array('username' => null, 'password' => null),$io->getAuthorization('foo')); + } + + public function testAsk() + { + $io = new NullIO(); + + $this->assertEquals('foo',$io->ask('bar','foo')); + } + + public function testAskConfirmation() + { + $io = new NullIO(); + + $this->assertEquals('foo',$io->askConfirmation('bar','foo')); + } + + public function testAskAndValidate() + { + $io = new NullIO(); + + $this->assertEquals('foo', $io->askAndValidate('question', 'validator', false, 'foo')); + } +} \ No newline at end of file From 202ca605591e9f326fcfcf490a6ffde1e31110e9 Mon Sep 17 00:00:00 2001 From: digitalkaoz Date: Fri, 9 Mar 2012 20:29:03 +0100 Subject: [PATCH 33/57] modified search listing (grouping packages) --- src/Composer/Command/SearchCommand.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Composer/Command/SearchCommand.php b/src/Composer/Command/SearchCommand.php index d01ebc1b4..a2006eaca 100644 --- a/src/Composer/Command/SearchCommand.php +++ b/src/Composer/Command/SearchCommand.php @@ -56,26 +56,26 @@ EOT } $tokens = array_map('strtolower', $input->getArgument('tokens')); + $packages = array(); + foreach ($repos->getPackages() as $package) { foreach ($tokens as $token) { if (false === ($pos = strpos($package->getName(), $token))) { continue; } - if ($platformRepo->hasPackage($package)) { - $type = 'platform: '; - } elseif ($installedRepo->hasPackage($package)) { - $type = 'installed: '; - } else { - $type = 'available: '; - } - $name = substr($package->getPrettyName(), 0, $pos) . '' . substr($package->getPrettyName(), $pos, strlen($token)) . '' . substr($package->getPrettyName(), $pos + strlen($token)); - $output->writeln($type . ': ' . $name . ' ' . $package->getPrettyVersion() . ''); + $version = $installedRepo->hasPackage($package) ? ''.$package->getPrettyVersion().'' : $package->getPrettyVersion(); + + $packages[$name][$package->getPrettyVersion()] = $version; continue 2; } } + + foreach ($packages as $name => $versions) { + $output->writeln($name .' : '. join(', ', $versions)); + } } } \ No newline at end of file From 38f866f1dd3475db30b4de6e4fc61a06c9a3426f Mon Sep 17 00:00:00 2001 From: digitalkaoz Date: Fri, 9 Mar 2012 22:03:26 +0100 Subject: [PATCH 34/57] fixes #373 aswell --- src/Composer/Command/SearchCommand.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/SearchCommand.php b/src/Composer/Command/SearchCommand.php index a2006eaca..7a25a2294 100644 --- a/src/Composer/Command/SearchCommand.php +++ b/src/Composer/Command/SearchCommand.php @@ -60,13 +60,18 @@ EOT foreach ($repos->getPackages() as $package) { foreach ($tokens as $token) { - if (false === ($pos = strpos($package->getName(), $token))) { + if (false === ($pos = strpos($package->getName(), $token)) && (false === strpos(join(',',$package->getKeywords() ?: array()), $token))) { continue; } - $name = substr($package->getPrettyName(), 0, $pos) - . '' . substr($package->getPrettyName(), $pos, strlen($token)) . '' - . substr($package->getPrettyName(), $pos + strlen($token)); + if (false !== ($pos = strpos($package->getName(), $token))) { + $name = substr($package->getPrettyName(), 0, $pos) + . '' . substr($package->getPrettyName(), $pos, strlen($token)) . '' + . substr($package->getPrettyName(), $pos + strlen($token)); + } else { + $name = $package->getPrettyName(); + } + $version = $installedRepo->hasPackage($package) ? ''.$package->getPrettyVersion().'' : $package->getPrettyVersion(); $packages[$name][$package->getPrettyVersion()] = $version; From a4f9e03d3538ee43262dc6b91dd811afe892716e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 9 Mar 2012 23:44:10 +0100 Subject: [PATCH 35/57] Add workaround for PHP bug #61336 --- src/Composer/Util/RemoteFilesystem.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index bc50b06fb..80538e5c6 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -108,6 +108,11 @@ class RemoteFilesystem $result = @file_get_contents($fileUrl, false, $ctx); } + // fix for 5.4.0 https://bugs.php.net/bug.php?id=61336 + if (!empty($http_response_header[0]) && preg_match('{^HTTP/\S+ 404}i', $http_response_header[0])) { + $result = false; + } + // avoid overriding if content was loaded by a sub-call to get() if (null === $this->result) { $this->result = $result; From 950003bfc3e4e8a6664037266a4419a421a60c21 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 10 Mar 2012 00:10:41 +0100 Subject: [PATCH 36/57] Reflow the VcsRepo and prevent exception leakage from breaking the parsing --- src/Composer/Repository/VcsRepository.php | 150 ++++++++++++---------- 1 file changed, 85 insertions(+), 65 deletions(-) diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index 490f807e0..65e432b18 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -83,9 +83,15 @@ class VcsRepository extends ArrayRepository $this->versionParser = new VersionParser; $loader = new ArrayLoader(); - if ($driver->hasComposerFile($driver->getRootIdentifier())) { - $data = $driver->getComposerInformation($driver->getRootIdentifier()); - $this->packageName = !empty($data['name']) ? $data['name'] : null; + try { + if ($driver->hasComposerFile($driver->getRootIdentifier())) { + $data = $driver->getComposerInformation($driver->getRootIdentifier()); + $this->packageName = !empty($data['name']) ? $data['name'] : null; + } + } catch (\Exception $e) { + if ($debug) { + $this->io->write('Skipped parsing '.$driver->getRootIdentifier().', '.$e->getMessage()); + } } foreach ($driver->getTags() as $tag => $identifier) { @@ -96,49 +102,53 @@ class VcsRepository extends ArrayRepository $this->io->overwrite($msg, false); } - $parsedTag = $this->validateTag($tag); - if ($parsedTag && $driver->hasComposerFile($identifier)) { - try { - $data = $driver->getComposerInformation($identifier); - } catch (TransportException $e) { - if ($debug) { - $this->io->write('Skipped tag '.$tag.', '.$e->getMessage()); - } - continue; - } catch (\Exception $e) { - $this->io->write('Skipped tag '.$tag.', '.$e->getMessage()); - continue; - } - - // manually versioned package - if (isset($data['version'])) { - $data['version_normalized'] = $this->versionParser->normalize($data['version']); - } else { - // auto-versionned package, read value from tag - $data['version'] = $tag; - $data['version_normalized'] = $parsedTag; - } - - // make sure tag packages have no -dev flag - $data['version'] = preg_replace('{[.-]?dev$}i', '', $data['version']); - $data['version_normalized'] = preg_replace('{(^dev-|[.-]?dev$)}i', '', $data['version_normalized']); - - // broken package, version doesn't match tag - if ($data['version_normalized'] !== $parsedTag) { - if ($debug) { - $this->io->write('Skipped tag '.$tag.', tag ('.$parsedTag.') does not match version ('.$data['version_normalized'].') in composer.json'); - } - continue; - } - + if (!$parsedTag = $this->validateTag($tag)) { if ($debug) { - $this->io->write('Importing tag '.$tag.' ('.$data['version_normalized'].')'); + $this->io->write('Skipped tag '.$tag.', invalid tag name'); } - - $this->addPackage($loader->load($this->preProcess($driver, $data, $identifier))); - } elseif ($debug) { - $this->io->write('Skipped tag '.$tag.', '.($parsedTag ? 'no composer file was found' : 'invalid name')); + continue; } + + try { + if (!$data = $driver->getComposerInformation($identifier)) { + if ($debug) { + $this->io->write('Skipped tag '.$tag.', no composer file'); + } + continue; + } + } catch (\Exception $e) { + if ($debug) { + $this->io->write('Skipped tag '.$tag.', '.$e->getMessage()); + } + continue; + } + + // manually versioned package + if (isset($data['version'])) { + $data['version_normalized'] = $this->versionParser->normalize($data['version']); + } else { + // auto-versionned package, read value from tag + $data['version'] = $tag; + $data['version_normalized'] = $parsedTag; + } + + // make sure tag packages have no -dev flag + $data['version'] = preg_replace('{[.-]?dev$}i', '', $data['version']); + $data['version_normalized'] = preg_replace('{(^dev-|[.-]?dev$)}i', '', $data['version_normalized']); + + // broken package, version doesn't match tag + if ($data['version_normalized'] !== $parsedTag) { + if ($debug) { + $this->io->write('Skipped tag '.$tag.', tag ('.$parsedTag.') does not match version ('.$data['version_normalized'].') in composer.json'); + } + continue; + } + + if ($debug) { + $this->io->write('Importing tag '.$tag.' ('.$data['version_normalized'].')'); + } + + $this->addPackage($loader->load($this->preProcess($driver, $data, $identifier))); } $this->io->overwrite('', false); @@ -151,36 +161,46 @@ class VcsRepository extends ArrayRepository $this->io->overwrite($msg, false); } - $parsedBranch = $this->validateBranch($branch); - if ($driver->hasComposerFile($identifier)) { - $data = $driver->getComposerInformation($identifier); + if (!$parsedBranch = $this->validateBranch($branch)) { + if ($debug) { + $this->io->write('Skipped branch '.$branch.', invalid name'); + } + continue; + } - if (!$parsedBranch) { + try { + if (!$data = $driver->getComposerInformation($identifier)) { if ($debug) { - $this->io->write('Skipped branch '.$branch.', invalid name and no composer file was found'); + $this->io->write('Skipped branch '.$branch.', no composer file'); } continue; } - - // branches are always auto-versionned, read value from branch name - $data['version'] = $branch; - $data['version_normalized'] = $parsedBranch; - - // make sure branch packages have a dev flag - if ('dev-' === substr($parsedBranch, 0, 4) || '9999999-dev' === $parsedBranch) { - $data['version'] = 'dev-' . $data['version']; - } else { - $data['version'] = $data['version'] . '-dev'; - } - + } catch (TransportException $e) { if ($debug) { - $this->io->write('Importing branch '.$branch.' ('.$data['version_normalized'].')'); + $this->io->write('Skipped branch '.$branch.', no composer file was found'); } - - $this->addPackage($loader->load($this->preProcess($driver, $data, $identifier))); - } elseif ($debug) { - $this->io->write('Skipped branch '.$branch.', no composer file was found'); + continue; + } catch (\Exception $e) { + $this->io->write('Skipped branch '.$branch.', '.$e->getMessage()); + continue; } + + // branches are always auto-versionned, read value from branch name + $data['version'] = $branch; + $data['version_normalized'] = $parsedBranch; + + // make sure branch packages have a dev flag + if ('dev-' === substr($parsedBranch, 0, 4) || '9999999-dev' === $parsedBranch) { + $data['version'] = 'dev-' . $data['version']; + } else { + $data['version'] = $data['version'] . '-dev'; + } + + if ($debug) { + $this->io->write('Importing branch '.$branch.' ('.$data['version_normalized'].')'); + } + + $this->addPackage($loader->load($this->preProcess($driver, $data, $identifier))); } $this->io->overwrite('', false); From 5f2e42ec462f0b22fd4c714565ba273123a6c068 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 10 Mar 2012 01:16:37 +0100 Subject: [PATCH 37/57] CS fixes, made EventDispatcher optional in factory method --- src/Composer/Command/CreateProjectCommand.php | 4 +-- src/Composer/Command/InstallCommand.php | 14 ++++---- src/Composer/Installer.php | 36 +++++++++---------- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 04b6266f2..5c0039b77 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -18,7 +18,6 @@ use Composer\Installer\ProjectInstaller; use Composer\IO\IOInterface; use Composer\Repository\ComposerRepository; use Composer\Repository\FilesystemRepository; -use Composer\Script\EventDispatcher; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -119,8 +118,7 @@ EOT chdir($directory); $composer = Factory::create($io); - $eventDispatcher = new EventDispatcher($composer, $io); - $installer = Installer::create($io, $composer, $eventDispatcher); + $installer = Installer::create($io, $composer); $installer->run($preferSource); } diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index e8744af5a..d06278e7d 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -13,7 +13,6 @@ namespace Composer\Command; use Composer\Installer; -use Composer\Script\EventDispatcher; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -52,15 +51,14 @@ EOT { $composer = $this->getComposer(); $io = $this->getApplication()->getIO(); - $eventDispatcher = new EventDispatcher($composer, $io); - $install = Installer::create($io, $composer, $eventDispatcher); + $install = Installer::create($io, $composer); return $install->run( - (Boolean)$input->getOption('prefer-source'), - (Boolean)$input->getOption('dry-run'), - (Boolean)$input->getOption('verbose'), - (Boolean)$input->getOption('no-install-recommends'), - (Boolean)$input->getOption('install-suggests') + (Boolean) $input->getOption('prefer-source'), + (Boolean) $input->getOption('dry-run'), + (Boolean) $input->getOption('verbose'), + (Boolean) $input->getOption('no-install-recommends'), + (Boolean) $input->getOption('install-suggests') ); } } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 6bb13eb86..f6fb70c1d 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -33,53 +33,51 @@ use Composer\Repository\RepositoryManager; use Composer\Script\EventDispatcher; use Composer\Script\ScriptEvents; +/** + * @author Jordi Boggiano + * @author Beau Simensen + * @author Konstantin Kudryashov + */ class Installer { /** - * * @var IOInterface */ protected $io; /** - * * @var PackageInterface */ protected $package; /** - * * @var DownloadManager */ protected $downloadManager; /** - * * @var RepositoryManager */ protected $repositoryManager; /** - * * @var Locker */ protected $locker; /** - * * @var InstallationManager */ protected $installationManager; /** - * * @var EventDispatcher */ protected $eventDispatcher; /** * Constructor - * + * * @param IOInterface $io * @param PackageInterface $package * @param DownloadManager $downloadManager @@ -102,12 +100,12 @@ class Installer /** * Run installation (or update) * - * @param bool $preferSource - * @param bool $dryRun - * @param bool $verbose - * @param bool $noInstallRecommends - * @param bool $installSuggests - * @param bool $update + * @param Boolean $preferSource + * @param Boolean $dryRun + * @param Boolean $verbose + * @param Boolean $noInstallRecommends + * @param Boolean $installSuggests + * @param Boolean $update * @param RepositoryInterface $additionalInstalledRepository */ public function run($preferSource = false, $dryRun = false, $verbose = false, $noInstallRecommends = false, $installSuggests = false, $update = false, RepositoryInterface $additionalInstalledRepository = null) @@ -202,8 +200,8 @@ class Installer } // prepare solver - $policy = new DefaultPolicy(); - $solver = new Solver($policy, $pool, $installedRepo); + $policy = new DefaultPolicy(); + $solver = new Solver($policy, $pool, $installedRepo); // solve dependencies $operations = $solver->solve($request); @@ -316,14 +314,16 @@ class Installer /** * Create Installer - * + * * @param IOInterface $io * @param Composer $composer * @param EventDispatcher $eventDispatcher * @return Installer */ - static public function create(IOInterface $io, Composer $composer, EventDispatcher $eventDispatcher) + static public function create(IOInterface $io, Composer $composer, EventDispatcher $eventDispatcher = null) { + $eventDispatcher = $eventDispatcher ?: new EventDispatcher($composer, $io); + return new static( $io, $composer->getPackage(), From f8a09eaa3fe6478b006528fb63678f5a78f2bc7c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 10 Mar 2012 01:59:59 +0100 Subject: [PATCH 38/57] Make classmaps relative instead of absolute for file portability --- src/Composer/Autoload/AutoloadGenerator.php | 93 +++++++++++-------- .../Test/Autoload/AutoloadGeneratorTest.php | 11 ++- .../Test/Autoload/Fixtures/autoload_main.php | 5 +- .../Test/Autoload/Fixtures/autoload_main2.php | 5 +- .../Test/Autoload/Fixtures/autoload_main3.php | 1 + .../Fixtures/autoload_override_vendors.php | 1 + .../Autoload/Fixtures/autoload_vendors.php | 1 + 7 files changed, 68 insertions(+), 49 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 59addd0b4..036b40b95 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -61,12 +61,16 @@ EOF; $relVendorPath = $filesystem->findShortestPath(getcwd(), $vendorPath); $vendorDirCode = $filesystem->findShortestPathCode(realpath($targetDir), $vendorPath, true); + $appBaseDir = $filesystem->findShortestPathCode($vendorPath, getcwd(), true); + $appBaseDir = str_replace('__DIR__', '$vendorDir', $appBaseDir); + $namespacesFile = <<buildPackageMap($installationManager, $mainPackage, $localRepo->getPackages()); $autoloads = $this->parseAutoloads($packageMap); - $appBaseDir = $filesystem->findShortestPathCode($vendorPath, getcwd(), true); - $appBaseDir = str_replace('__DIR__', '$vendorDir', $appBaseDir); - - if (isset($autoloads['psr-0'])) { - foreach ($autoloads['psr-0'] as $namespace => $paths) { - $exportedPaths = array(); - foreach ($paths as $path) { - $path = strtr($path, '\\', '/'); - $baseDir = ''; - if (!$filesystem->isAbsolutePath($path)) { - // vendor dir == working dir - if (preg_match('{^(\./?)?$}', $relVendorPath)) { - $path = '/'.$path; - $baseDir = '$vendorDir . '; - } elseif (strpos($path, $relVendorPath) === 0) { - // path starts with vendor dir - $path = substr($path, strlen($relVendorPath)); - $baseDir = '$vendorDir . '; - } else { - $path = '/'.$path; - $baseDir = $appBaseDir . ' . '; - } - } elseif (strpos($path, $vendorPath) === 0) { - $path = substr($path, strlen($vendorPath)); + foreach ($autoloads['psr-0'] as $namespace => $paths) { + $exportedPaths = array(); + foreach ($paths as $path) { + $path = strtr($path, '\\', '/'); + $baseDir = ''; + if (!$filesystem->isAbsolutePath($path)) { + // vendor dir == working dir + if (preg_match('{^(\./?)?$}', $relVendorPath)) { + $path = '/'.$path; $baseDir = '$vendorDir . '; + } elseif (strpos($path, $relVendorPath) === 0) { + // path starts with vendor dir + $path = substr($path, strlen($relVendorPath)); + $baseDir = '$vendorDir . '; + } else { + $path = '/'.$path; + $baseDir = '$baseDir . '; } - $exportedPaths[] = $baseDir.var_export($path, true); - } - $exportedPrefix = var_export($namespace, true); - $namespacesFile .= " $exportedPrefix => "; - if (count($exportedPaths) > 1) { - $namespacesFile .= "array(".implode(', ',$exportedPaths)."),\n"; - } else { - $namespacesFile .= $exportedPaths[0].",\n"; + } elseif (strpos($path, $vendorPath) === 0) { + $path = substr($path, strlen($vendorPath)); + $baseDir = '$vendorDir . '; } + $exportedPaths[] = $baseDir.var_export($path, true); + } + $exportedPrefix = var_export($namespace, true); + $namespacesFile .= " $exportedPrefix => "; + if (count($exportedPaths) > 1) { + $namespacesFile .= "array(".implode(', ', $exportedPaths)."),\n"; + } else { + $namespacesFile .= $exportedPaths[0].",\n"; } } $namespacesFile .= ");\n"; - if (isset($autoloads['classmap'])) { - // flatten array - $autoloads['classmap'] = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($autoloads['classmap'])); - } else { - $autoloads['classmap'] = array(); - } + $classmapFile = << $path) { + $path = '/'.$filesystem->findShortestPath(getcwd(), $path); + $classmapFile .= ' '.var_export($class, true).' => $baseDir . '.var_export($path, true).",\n"; + } + } + $classmapFile .= ");\n"; file_put_contents($targetDir.'/autoload.php', $autoloadFile); file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile); + file_put_contents($targetDir.'/autoload_classmap.php', $classmapFile); copy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php'); } @@ -154,7 +167,7 @@ EOF; */ public function parseAutoloads(array $packageMap) { - $autoloads = array(); + $autoloads = array('classmap' => array(), 'psr-0' => array()); foreach ($packageMap as $item) { list($package, $installPath) = $item; diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 123faa5c5..9d2d243a9 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -160,11 +160,12 @@ class AutoloadGeneratorTest extends TestCase file_put_contents($this->vendorDir.'/b/b/lib/c.php', 'generator->dump($this->repository, $package, $this->im, $this->vendorDir.'/.composer'); - $this->assertTrue(file_exists($this->vendorDir.'/.composer/autoload_classmap.php'), "ClassMap file needs to be generated, even if empty."); - $this->assertEquals(array( - 'ClassMapFoo' => $this->vendorDir.'/a/a/src/a.php', - 'ClassMapBar' => $this->vendorDir.'/b/b/src/b.php', - 'ClassMapBaz' => $this->vendorDir.'/b/b/lib/c.php', + $this->assertTrue(file_exists($this->vendorDir.'/.composer/autoload_classmap.php'), "ClassMap file needs to be generated."); + $this->assertEquals( + array( + 'ClassMapFoo' => $this->workingDir.'/composer-test-autoload/a/a/src/a.php', + 'ClassMapBar' => $this->workingDir.'/composer-test-autoload/b/b/src/b.php', + 'ClassMapBaz' => $this->workingDir.'/composer-test-autoload/b/b/lib/c.php', ), include ($this->vendorDir.'/.composer/autoload_classmap.php') ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_main.php b/tests/Composer/Test/Autoload/Fixtures/autoload_main.php index 157d888f3..ec243e33e 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_main.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_main.php @@ -3,8 +3,9 @@ // autoload_namespace.php generated by Composer $vendorDir = dirname(__DIR__); +$baseDir = dirname($vendorDir); return array( - 'Main' => dirname($vendorDir) . '/src/', - 'Lala' => dirname($vendorDir) . '/src/', + 'Main' => $baseDir . '/src/', + 'Lala' => $baseDir . '/src/', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_main2.php b/tests/Composer/Test/Autoload/Fixtures/autoload_main2.php index a0fa2c7db..da1c87d88 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_main2.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_main2.php @@ -3,8 +3,9 @@ // autoload_namespace.php generated by Composer $vendorDir = dirname(__DIR__); +$baseDir = dirname(dirname($vendorDir)); return array( - 'Main' => dirname(dirname($vendorDir)) . '/src/', - 'Lala' => dirname(dirname($vendorDir)) . '/src/', + 'Main' => $baseDir . '/src/', + 'Lala' => $baseDir . '/src/', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_main3.php b/tests/Composer/Test/Autoload/Fixtures/autoload_main3.php index 6f12b3d75..3c6d910cc 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_main3.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_main3.php @@ -3,6 +3,7 @@ // autoload_namespace.php generated by Composer $vendorDir = dirname(__DIR__); +$baseDir = dirname($vendorDir); return array( 'Main' => $vendorDir . '/src/', diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_override_vendors.php b/tests/Composer/Test/Autoload/Fixtures/autoload_override_vendors.php index 4b4eacdca..0640aceda 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_override_vendors.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_override_vendors.php @@ -3,6 +3,7 @@ // autoload_namespace.php generated by Composer $vendorDir = dirname(__DIR__); +$baseDir = dirname($vendorDir); return array( 'B\\Sub\\Name' => $vendorDir . '/b/b/src/', diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_vendors.php b/tests/Composer/Test/Autoload/Fixtures/autoload_vendors.php index b149046df..dd076486d 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_vendors.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_vendors.php @@ -3,6 +3,7 @@ // autoload_namespace.php generated by Composer $vendorDir = dirname(__DIR__); +$baseDir = dirname($vendorDir); return array( 'B\\Sub\\Name' => $vendorDir . '/b/b/src/', From 744f4b7c9a1080722f7a07f8d6199f2faa9216ce Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 10 Mar 2012 02:00:29 +0100 Subject: [PATCH 39/57] Backport trait support in ClassMapGenerator from symfony --- src/Composer/Autoload/ClassMapGenerator.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index c9fa687d6..99b2a29ad 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -85,6 +85,7 @@ class ClassMapGenerator { $contents = file_get_contents($path); $tokens = token_get_all($contents); + $T_TRAIT = version_compare(PHP_VERSION, '5.4', '<') ? -1 : T_TRAIT; $classes = array(); @@ -111,6 +112,7 @@ class ClassMapGenerator break; case T_CLASS: case T_INTERFACE: + case $T_TRAIT: // Find the classname while (($t = $tokens[++$i]) && is_array($t)) { if (T_STRING === $t[0]) { @@ -120,11 +122,7 @@ class ClassMapGenerator } } - if (empty($namespace)) { - $classes[] = $class; - } else { - $classes[] = $namespace . $class; - } + $classes[] = ltrim($namespace . $class, '\\'); break; default: break; @@ -134,4 +132,3 @@ class ClassMapGenerator return $classes; } } - From 5d9cb3eb4bb6bd2d50a513e556c62f388dba4f13 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 10 Mar 2012 02:01:26 +0100 Subject: [PATCH 40/57] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a9d73764..a2a726272 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ * 1.0.0-alpha2 * Added `create-project` command to install a project from scratch with composer + * Added automated `classmap` autoloading support for non-PSR-0 compliant projects * Git clones from GitHub automatically select between git/https/http protocols * Enhanced `validate` command to give more feedback * Added "file" downloader type to download plain files From edf93f1fccaebd8764383dc12016d0a1a9672d89 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 10 Mar 2012 02:14:40 +0100 Subject: [PATCH 41/57] Fix test & behavior --- src/Composer/Util/Filesystem.php | 10 ++++++---- .../Composer/Test/Autoload/Fixtures/autoload_main3.php | 6 +++--- tests/Composer/Test/Util/FilesystemTest.php | 2 ++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 201f44a7e..dd0163717 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -66,11 +66,12 @@ class Filesystem throw new \InvalidArgumentException('from and to must be absolute paths'); } + $from = lcfirst(rtrim(strtr($from, '\\', '/'), '/')); + $to = lcfirst(rtrim(strtr($to, '\\', '/'), '/')); + if (dirname($from) === dirname($to)) { return './'.basename($to); } - $from = lcfirst(rtrim(strtr($from, '\\', '/'), '/')); - $to = lcfirst(rtrim(strtr($to, '\\', '/'), '/')); $commonPath = $to; while (strpos($from, $commonPath) !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath) && '.' !== $commonPath) { @@ -101,11 +102,12 @@ class Filesystem throw new \InvalidArgumentException('from and to must be absolute paths'); } + $from = lcfirst(strtr($from, '\\', '/')); + $to = lcfirst(strtr($to, '\\', '/')); + if ($from === $to) { return $directories ? '__DIR__' : '__FILE__'; } - $from = lcfirst(strtr($from, '\\', '/')); - $to = lcfirst(strtr($to, '\\', '/')); $commonPath = $to; while (strpos($from, $commonPath) !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath) && '.' !== $commonPath) { diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_main3.php b/tests/Composer/Test/Autoload/Fixtures/autoload_main3.php index 3c6d910cc..2eb74cf44 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_main3.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_main3.php @@ -3,9 +3,9 @@ // autoload_namespace.php generated by Composer $vendorDir = dirname(__DIR__); -$baseDir = dirname($vendorDir); +$baseDir = $vendorDir; return array( - 'Main' => $vendorDir . '/src/', - 'Lala' => $vendorDir . '/src/', + 'Main' => $baseDir . '/src/', + 'Lala' => $baseDir . '/src/', ); diff --git a/tests/Composer/Test/Util/FilesystemTest.php b/tests/Composer/Test/Util/FilesystemTest.php index 0db5549d2..81ece7d35 100644 --- a/tests/Composer/Test/Util/FilesystemTest.php +++ b/tests/Composer/Test/Util/FilesystemTest.php @@ -41,6 +41,8 @@ class FilesystemTest extends TestCase array('/foo/bar', '/foo/baz', true, "dirname(__DIR__).'/baz'"), array('/foo/bin/run', '/foo/vendor/acme/bin/run', true, "dirname(dirname(__DIR__)).'/vendor/acme/bin/run'"), array('/foo/bin/run', '/bar/bin/run', true, "'/bar/bin/run'"), + array('/bin/run', '/bin/run', true, "__DIR__"), + array('c:/bin/run', 'c:\\bin/run', true, "__DIR__"), array('c:/bin/run', 'c:/vendor/acme/bin/run', true, "dirname(dirname(__DIR__)).'/vendor/acme/bin/run'"), array('c:\\bin\\run', 'c:/vendor/acme/bin/run', true, "dirname(dirname(__DIR__)).'/vendor/acme/bin/run'"), array('c:/bin/run', 'd:/vendor/acme/bin/run', true, "'d:/vendor/acme/bin/run'"), From a284eb5cfe9ecff2fa12b2646641088fa16e6bf4 Mon Sep 17 00:00:00 2001 From: digitalkaoz Date: Sat, 10 Mar 2012 09:53:03 +0100 Subject: [PATCH 42/57] coding style fixes --- tests/Composer/Test/ComposerTest.php | 10 +++++----- .../Composer/Test/Downloader/ArchiveDownloaderTest.php | 3 +-- tests/Composer/Test/IO/NullIOTest.php | 8 ++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/tests/Composer/Test/ComposerTest.php b/tests/Composer/Test/ComposerTest.php index e1fb8de3c..80feb51ba 100644 --- a/tests/Composer/Test/ComposerTest.php +++ b/tests/Composer/Test/ComposerTest.php @@ -21,7 +21,7 @@ class ComposerTest extends TestCase $package = $this->getMock('Composer\Package\PackageInterface'); $composer->setPackage($package); - $this->assertEquals($package,$composer->getPackage()); + $this->assertSame($package, $composer->getPackage()); } public function testSetGetLocker() @@ -30,7 +30,7 @@ class ComposerTest extends TestCase $locker = $this->getMockBuilder('Composer\Package\Locker')->disableOriginalConstructor()->getMock(); $composer->setLocker($locker); - $this->assertEquals($locker,$composer->getLocker()); + $this->assertSame($locker, $composer->getLocker()); } public function testSetGetRepositoryManager() @@ -39,7 +39,7 @@ class ComposerTest extends TestCase $manager = $this->getMockBuilder('Composer\Repository\RepositoryManager')->disableOriginalConstructor()->getMock(); $composer->setRepositoryManager($manager); - $this->assertEquals($manager,$composer->getRepositoryManager()); + $this->assertSame($manager, $composer->getRepositoryManager()); } public function testSetGetDownloadManager() @@ -48,7 +48,7 @@ class ComposerTest extends TestCase $manager = $this->getMock('Composer\Downloader\DownloadManager'); $composer->setDownloadManager($manager); - $this->assertEquals($manager,$composer->getDownloadManager()); + $this->assertSame($manager, $composer->getDownloadManager()); } public function testSetGetInstallationManager() @@ -57,6 +57,6 @@ class ComposerTest extends TestCase $manager = $this->getMock('Composer\Installer\InstallationManager'); $composer->setInstallationManager($manager); - $this->assertEquals($manager,$composer->getInstallationManager()); + $this->assertSame($manager, $composer->getInstallationManager()); } } \ No newline at end of file diff --git a/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php b/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php index ce4a2b0fe..b7f2c0f53 100644 --- a/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php +++ b/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php @@ -42,8 +42,7 @@ class ArchiveDownloaderTest extends \PHPUnit_Framework_TestCase $expected = 'https://github.com/composer/composer/zipball/master'; $url = $method->invoke($downloader, $expected); - if(extension_loaded('openssl')) - { + if (extension_loaded('openssl')) { $this->assertEquals($expected, $url); } else { $this->assertEquals('http://nodeload.github.com/composer/composer/zipball/master', $url); diff --git a/tests/Composer/Test/IO/NullIOTest.php b/tests/Composer/Test/IO/NullIOTest.php index 406db336d..854f27862 100644 --- a/tests/Composer/Test/IO/NullIOTest.php +++ b/tests/Composer/Test/IO/NullIOTest.php @@ -56,23 +56,23 @@ class NullIOTest extends TestCase { $io = new NullIO(); - $this->assertInternalType('array',$io->getAuthorizations()); + $this->assertInternalType('array', $io->getAuthorizations()); $this->assertEmpty($io->getAuthorizations()); - $this->assertEquals(array('username' => null, 'password' => null),$io->getAuthorization('foo')); + $this->assertEquals(array('username' => null, 'password' => null), $io->getAuthorization('foo')); } public function testAsk() { $io = new NullIO(); - $this->assertEquals('foo',$io->ask('bar','foo')); + $this->assertEquals('foo', $io->ask('bar', 'foo')); } public function testAskConfirmation() { $io = new NullIO(); - $this->assertEquals('foo',$io->askConfirmation('bar','foo')); + $this->assertEquals('foo', $io->askConfirmation('bar', 'foo')); } public function testAskAndValidate() From e6e90bb1740d4cbdf6df5f956694c979854543b8 Mon Sep 17 00:00:00 2001 From: digitalkaoz Date: Sat, 10 Mar 2012 09:57:11 +0100 Subject: [PATCH 43/57] added keywords to show command --- src/Composer/Command/ShowCommand.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 5e8bf262a..82438e4db 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -136,6 +136,7 @@ EOT $this->printVersions($input, $output, $package, $installedRepo, $repos); $output->writeln('type : ' . $package->getType()); $output->writeln('names : ' . join(', ', $package->getNames())); + $output->writeln('keywords : ' . join(', ', $package->getKeywords())); $output->writeln('source : ' . sprintf('[%s] %s %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference())); $output->writeln('dist : ' . sprintf('[%s] %s %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference())); $output->writeln('license : ' . join(', ', $package->getLicense())); From fcca58ceb63737529729d2bf80384bc744bcd8ae Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 10 Mar 2012 10:46:30 +0100 Subject: [PATCH 44/57] Fix phar --- src/Composer/Compiler.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Compiler.php b/src/Composer/Compiler.php index 563df4018..ca4875a97 100644 --- a/src/Composer/Compiler.php +++ b/src/Composer/Compiler.php @@ -82,6 +82,7 @@ class Compiler $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/.composer/ClassLoader.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/.composer/autoload.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/.composer/autoload_namespaces.php')); + $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/.composer/autoload_classmap.php')); $this->addComposerBin($phar); // Stubs From 271e9086924bbf1a0aec4be3ec166f2d039bf373 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sat, 10 Mar 2012 17:49:08 +0100 Subject: [PATCH 45/57] Fixed the GitDownloader when changing the repo url --- src/Composer/Downloader/GitDownloader.php | 66 +++++++++++++++-------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 0aa043c31..cceb4cf8b 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -29,24 +29,11 @@ class GitDownloader extends VcsDownloader $command = 'git clone %s %s && cd %2$s && git checkout %3$s && git reset --hard %3$s'; $this->io->write(" Cloning ".$package->getSourceReference()); - // github, autoswitch protocols - if (preg_match('{^(?:https?|git)(://github.com/.*)}', $package->getSourceUrl(), $match)) { - $protocols = array('git', 'https', 'http'); - foreach ($protocols as $protocol) { - $url = escapeshellarg($protocol . $match[1]); - if (0 === $this->process->execute(sprintf($command, $url, escapeshellarg($path), $ref), $ignoredOutput)) { - return; - } - $this->filesystem->removeDirectory($path); - } - throw new \RuntimeException('Failed to checkout ' . $url .' via git, https and http protocols, aborting.' . "\n\n" . $this->process->getErrorOutput()); - } else { - $url = escapeshellarg($package->getSourceUrl()); - $command = sprintf($command, $url, escapeshellarg($path), $ref); - if (0 !== $this->process->execute($command, $ignoredOutput)) { - throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); - } - } + $commandCallable = function($url) use ($ref, $path, $command) { + return sprintf($command, $url, escapeshellarg($path), $ref); + }; + + $this->runCommand($commandCallable, $package->getSourceUrl(), $path); } /** @@ -57,10 +44,13 @@ class GitDownloader extends VcsDownloader $ref = escapeshellarg($target->getSourceReference()); $path = escapeshellarg($path); $this->io->write(" Checking out ".$target->getSourceReference()); - $command = sprintf('cd %s && git fetch && git checkout %2$s && git reset --hard %2$s', $path, $ref); - if (0 !== $this->process->execute($command, $ignoredOutput)) { - throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); - } + $command = 'cd %s && git remote set-url origin %s && git fetch && git checkout %3$s && git reset --hard %3$s'; + + $commandCallable = function($url) use ($ref, $path, $command) { + return sprintf($command, $path, $url, $ref); + }; + + $this->runCommand($commandCallable, $target->getSourceUrl()); } /** @@ -77,4 +67,36 @@ class GitDownloader extends VcsDownloader throw new \RuntimeException('Source directory ' . $path . ' has uncommitted changes'); } } + + /** + * Runs a command doing attempts for each protocol supported by github. + * + * @param callable $commandCallable A callable building the command for the given url + * @param string $url + * @param string $path The directory to remove for each attempt (null if not needed) + * @throws \RuntimeException + */ + protected function runCommand($commandCallable, $url, $path = null) + { + // github, autoswitch protocols + if (preg_match('{^(?:https?|git)(://github.com/.*)}', $url, $match)) { + $protocols = array('git', 'https', 'http'); + foreach ($protocols as $protocol) { + $url = escapeshellarg($protocol . $match[1]); + if (0 === $this->process->execute(call_user_func($commandCallable, $url), $ignoredOutput)) { + return; + } + if (null !== $path) { + $this->filesystem->removeDirectory($path); + } + } + throw new \RuntimeException('Failed to checkout ' . $url .' via git, https and http protocols, aborting.' . "\n\n" . $this->process->getErrorOutput()); + } + + $url = escapeshellarg($url); + $command = call_user_func($commandCallable, $url); + if (0 !== $this->process->execute($command, $ignoredOutput)) { + throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); + } + } } From 673dd6312b3bc8cb2b244d3d7f48bc43e12b1c80 Mon Sep 17 00:00:00 2001 From: digitalkaoz Date: Sat, 10 Mar 2012 18:08:36 +0100 Subject: [PATCH 46/57] fluent api for installer options --- src/Composer/Command/CreateProjectCommand.php | 4 +- src/Composer/Command/InstallCommand.php | 18 ++- src/Composer/Command/UpdateCommand.php | 20 +-- src/Composer/Installer.php | 149 ++++++++++++++---- 4 files changed, 142 insertions(+), 49 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 5c0039b77..a859ac12a 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -120,7 +120,9 @@ EOT $composer = Factory::create($io); $installer = Installer::create($io, $composer); - $installer->run($preferSource); + $installer + ->setPreferSource($preferSource) + ->run(); } protected function createDownloadManager(IOInterface $io) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index d06278e7d..dd18d98f7 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -32,7 +32,7 @@ class InstallCommand extends Command ->setDefinition(array( new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), - new InputOption('no-install-recommends', null, InputOption::VALUE_NONE, 'Do not install recommended packages (ignored when installing from an existing lock file).'), + new InputOption('install-recommends', null, InputOption::VALUE_NONE, 'Also install recommended packages (ignored when installing from an existing lock file).'), new InputOption('install-suggests', null, InputOption::VALUE_NONE, 'Also install suggested packages (ignored when installing from an existing lock file).'), )) ->setHelp(<<getApplication()->getIO(); $install = Installer::create($io, $composer); - return $install->run( - (Boolean) $input->getOption('prefer-source'), - (Boolean) $input->getOption('dry-run'), - (Boolean) $input->getOption('verbose'), - (Boolean) $input->getOption('no-install-recommends'), - (Boolean) $input->getOption('install-suggests') - ); + $install + ->setDryRun($input->getOption('dry-run')) + ->setVerbose($input->getOption('verbose')) + ->setPreferSource($input->getOption('prefer-source')) + ->setInstallRecommends($input->getOption('install-recommends')) + ->setInstallSuggests($input->getOption('install-suggests')) + ; + + return $install->run(); } } diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 69640bd3d..c1b75b5e9 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -31,7 +31,7 @@ class UpdateCommand extends Command ->setDefinition(array( new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), - new InputOption('no-install-recommends', null, InputOption::VALUE_NONE, 'Do not install recommended packages.'), + new InputOption('install-recommends', null, InputOption::VALUE_NONE, 'Also install recommended packages.'), new InputOption('install-suggests', null, InputOption::VALUE_NONE, 'Also install suggested packages.'), )) ->setHelp(<<run( - (Boolean)$input->getOption('prefer-source'), - (Boolean)$input->getOption('dry-run'), - (Boolean)$input->getOption('verbose'), - (Boolean)$input->getOption('no-install-recommends'), - (Boolean)$input->getOption('install-suggests'), - true - ); + $install + ->setDryRun($input->getOption('dry-run')) + ->setVerbose($input->getOption('verbose')) + ->setPreferSource($input->getOption('prefer-source')) + ->setInstallRecommends($input->getOption('install-recommends')) + ->setInstallSuggests($input->getOption('install-suggests')) + ->setUpdate(true) + ; + + return $install->run(); } } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index f6fb70c1d..ece3c5fdb 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -75,6 +75,18 @@ class Installer */ protected $eventDispatcher; + protected $preferSource; + protected $dryRun; + protected $verbose; + protected $installRecommends; + protected $installSuggests; + protected $update; + + /** + * @var RepositoryInterface + */ + protected $additionalInstalledRepository; + /** * Constructor * @@ -99,37 +111,27 @@ class Installer /** * Run installation (or update) - * - * @param Boolean $preferSource - * @param Boolean $dryRun - * @param Boolean $verbose - * @param Boolean $noInstallRecommends - * @param Boolean $installSuggests - * @param Boolean $update - * @param RepositoryInterface $additionalInstalledRepository */ - public function run($preferSource = false, $dryRun = false, $verbose = false, $noInstallRecommends = false, $installSuggests = false, $update = false, RepositoryInterface $additionalInstalledRepository = null) + public function run() { - if ($dryRun) { - $verbose = true; + if ($this->dryRun) { + $this->verbose = true; } - if ($preferSource) { + if ($this->preferSource) { $this->downloadManager->setPreferSource(true); } - $this->repositoryManager = $this->repositoryManager; - // create local repo, this contains all packages that are installed in the local project $localRepo = $this->repositoryManager->getLocalRepository(); // create installed repo, this contains all local packages + platform packages (php & extensions) $installedRepo = new CompositeRepository(array($localRepo, new PlatformRepository())); - if ($additionalInstalledRepository) { - $installedRepo->addRepository($additionalInstalledRepository); + if ($this->additionalInstalledRepository) { + $installedRepo->addRepository($this->additionalInstalledRepository); } // prepare aliased packages - if (!$update && $this->locker->isLocked()) { + if (!$this->update && $this->locker->isLocked()) { $aliases = $this->locker->getAliases(); } else { $aliases = $this->package->getAliases(); @@ -152,20 +154,20 @@ class Installer } // dispatch pre event - if (!$dryRun) { - $eventName = $update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD; + if (!$this->dryRun) { + $eventName = $this->update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD; $this->eventDispatcher->dispatchCommandEvent($eventName); } // creating requirements request $installFromLock = false; $request = new Request($pool); - if ($update) { + if ($this->update) { $this->io->write('Updating dependencies'); $request->updateAll(); - $links = $this->collectLinks($noInstallRecommends, $installSuggests); + $links = $this->collectLinks(); foreach ($links as $link) { $request->install($link->getTarget(), $link->getConstraint()); @@ -192,7 +194,7 @@ class Installer } else { $this->io->write('Installing dependencies'); - $links = $this->collectLinks($noInstallRecommends, $installSuggests); + $links = $this->collectLinks(); foreach ($links as $link) { $request->install($link->getTarget(), $link->getConstraint()); @@ -207,7 +209,7 @@ class Installer $operations = $solver->solve($request); // force dev packages to be updated to latest reference on update - if ($update) { + if ($this->update) { foreach ($localRepo->getPackages() as $package) { if ($package instanceof AliasPackage) { $package = $package->getAliasOf(); @@ -249,10 +251,10 @@ class Installer } foreach ($operations as $operation) { - if ($verbose) { + if ($this->verbose) { $this->io->write((string) $operation); } - if (!$dryRun) { + if (!$this->dryRun) { $this->eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType())), $operation); // if installing from lock, restore dev packages' references to their locked state @@ -279,8 +281,8 @@ class Installer } } - if (!$dryRun) { - if ($update || !$this->locker->isLocked()) { + if (!$this->dryRun) { + if ($this->update || !$this->locker->isLocked()) { $this->locker->setLockData($localRepo->getPackages(), $aliases); $this->io->write('Writing lock file'); } @@ -292,20 +294,20 @@ class Installer $generator->dump($localRepo, $this->package, $this->installationManager, $this->installationManager->getVendorPath().'/.composer'); // dispatch post event - $eventName = $update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; + $eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; $this->eventDispatcher->dispatchCommandEvent($eventName); } } - private function collectLinks($noInstallRecommends, $installSuggests) + private function collectLinks() { $links = $this->package->getRequires(); - if (!$noInstallRecommends) { + if ($this->installRecommends) { $links = array_merge($links, $this->package->getRecommends()); } - if ($installSuggests) { + if ($this->installSuggests) { $links = array_merge($links, $this->package->getSuggests()); } @@ -334,4 +336,89 @@ class Installer $eventDispatcher ); } + + public function setAdditionalInstalledRepository(RepositoryInterface $additionalInstalledRepository) + { + $this->additionalInstalledRepository = $additionalInstalledRepository; + + return $this; + } + + /** + * wether to run in drymode or not + * + * @param boolean $dryRun + * @return Installer + */ + public function setDryRun($dryRun=true) + { + $this->dryRun = (boolean)$dryRun; + + return $this; + } + + /** + * also install recommend packages + * + * @param boolean $installRecommends + * @return Installer + */ + public function setInstallRecommends($installRecommends=true) + { + $this->installRecommends = (boolean)$installRecommends; + + return $this; + } + + /** + * also install suggested packages + * + * @param boolean $installSuggests + * @return Installer + */ + public function setInstallSuggests($installSuggests=true) + { + $this->installSuggests = (boolean)$installSuggests; + + return $this; + } + + /** + * prefer source installation + * + * @param boolean $preferSource + * @return Installer + */ + public function setPreferSource($preferSource=true) + { + $this->preferSource = (boolean)$preferSource; + + return $this; + } + + /** + * update packages + * + * @param boolean $update + * @return Installer + */ + public function setUpdate($update=true) + { + $this->update = (boolean)$update; + + return $this; + } + + /** + * run in verbose mode + * + * @param boolean $verbose + * @return Installer + */ + public function setVerbose($verbose=true) + { + $this->verbose = (boolean)$verbose; + + return $this; + } } From 8fde0379ed08ee47012abb42b5fcb98a1d4ec679 Mon Sep 17 00:00:00 2001 From: Beau Simensen Date: Sat, 10 Mar 2012 09:47:03 -0800 Subject: [PATCH 47/57] Cleanup, not needed here. --- src/Composer/Command/UpdateCommand.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 69640bd3d..cc0960504 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -13,7 +13,6 @@ namespace Composer\Command; use Composer\Installer; -use Composer\Script\EventDispatcher; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -50,8 +49,7 @@ EOT { $composer = $this->getComposer(); $io = $this->getApplication()->getIO(); - $eventDispatcher = new EventDispatcher($composer, $io); - $install = Installer::create($io, $composer, $eventDispatcher); + $install = Installer::create($io, $composer); return $install->run( (Boolean)$input->getOption('prefer-source'), From a4f6314daa437d6f240afabeb0fd31ec6c8bc0ce Mon Sep 17 00:00:00 2001 From: digitalkaoz Date: Sat, 10 Mar 2012 19:56:15 +0100 Subject: [PATCH 48/57] reverted command interfaces for installer refactoring --- src/Composer/Command/InstallCommand.php | 4 ++-- src/Composer/Command/UpdateCommand.php | 4 ++-- src/Composer/Installer.php | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index dd18d98f7..7bf90709e 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -32,7 +32,7 @@ class InstallCommand extends Command ->setDefinition(array( new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), - new InputOption('install-recommends', null, InputOption::VALUE_NONE, 'Also install recommended packages (ignored when installing from an existing lock file).'), + new InputOption('no-install-recommends', null, InputOption::VALUE_NONE, 'Do not install recommended packages (ignored when installing from an existing lock file).'), new InputOption('install-suggests', null, InputOption::VALUE_NONE, 'Also install suggested packages (ignored when installing from an existing lock file).'), )) ->setHelp(<<setDryRun($input->getOption('dry-run')) ->setVerbose($input->getOption('verbose')) ->setPreferSource($input->getOption('prefer-source')) - ->setInstallRecommends($input->getOption('install-recommends')) + ->setInstallRecommends(!$input->getOption('no-install-recommends')) ->setInstallSuggests($input->getOption('install-suggests')) ; diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index c1b75b5e9..e7c581174 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -31,7 +31,7 @@ class UpdateCommand extends Command ->setDefinition(array( new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), - new InputOption('install-recommends', null, InputOption::VALUE_NONE, 'Also install recommended packages.'), + new InputOption('no-install-recommends', null, InputOption::VALUE_NONE, 'Do not install recommended packages.'), new InputOption('install-suggests', null, InputOption::VALUE_NONE, 'Also install suggested packages.'), )) ->setHelp(<<setDryRun($input->getOption('dry-run')) ->setVerbose($input->getOption('verbose')) ->setPreferSource($input->getOption('prefer-source')) - ->setInstallRecommends($input->getOption('install-recommends')) + ->setInstallRecommends(!$input->getOption('no-install-recommends')) ->setInstallSuggests($input->getOption('install-suggests')) ->setUpdate(true) ; diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index ece3c5fdb..4af678d6d 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -75,12 +75,12 @@ class Installer */ protected $eventDispatcher; - protected $preferSource; - protected $dryRun; - protected $verbose; - protected $installRecommends; - protected $installSuggests; - protected $update; + protected $preferSource = false; + protected $dryRun = false; + protected $verbose = false; + protected $installRecommends = true; + protected $installSuggests = false; + protected $update = false; /** * @var RepositoryInterface @@ -358,9 +358,9 @@ class Installer } /** - * also install recommend packages + * install recommend packages * - * @param boolean $installRecommends + * @param boolean $noInstallRecommends * @return Installer */ public function setInstallRecommends($installRecommends=true) From d502065cb7aa4bc6be9e0485a0e1dbedbf62ba23 Mon Sep 17 00:00:00 2001 From: digitalkaoz Date: Sat, 10 Mar 2012 20:14:54 +0100 Subject: [PATCH 49/57] fixed cs --- src/Composer/Installer.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 4af678d6d..a0f85576a 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -352,7 +352,7 @@ class Installer */ public function setDryRun($dryRun=true) { - $this->dryRun = (boolean)$dryRun; + $this->dryRun = (boolean) $dryRun; return $this; } @@ -365,7 +365,7 @@ class Installer */ public function setInstallRecommends($installRecommends=true) { - $this->installRecommends = (boolean)$installRecommends; + $this->installRecommends = (boolean) $installRecommends; return $this; } @@ -378,7 +378,7 @@ class Installer */ public function setInstallSuggests($installSuggests=true) { - $this->installSuggests = (boolean)$installSuggests; + $this->installSuggests = (boolean) $installSuggests; return $this; } @@ -391,7 +391,7 @@ class Installer */ public function setPreferSource($preferSource=true) { - $this->preferSource = (boolean)$preferSource; + $this->preferSource = (boolean) $preferSource; return $this; } @@ -404,7 +404,7 @@ class Installer */ public function setUpdate($update=true) { - $this->update = (boolean)$update; + $this->update = (boolean) $update; return $this; } @@ -417,7 +417,7 @@ class Installer */ public function setVerbose($verbose=true) { - $this->verbose = (boolean)$verbose; + $this->verbose = (boolean) $verbose; return $this; } From 741a66e504545dbb5bc76da259739f5683f707ca Mon Sep 17 00:00:00 2001 From: digitalkaoz Date: Sat, 10 Mar 2012 21:55:23 +0100 Subject: [PATCH 50/57] added description to show command, searching in description aswell --- src/Composer/Command/SearchCommand.php | 20 ++++++++++++++++++-- src/Composer/Command/ShowCommand.php | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/SearchCommand.php b/src/Composer/Command/SearchCommand.php index 7a25a2294..c5d692cf1 100644 --- a/src/Composer/Command/SearchCommand.php +++ b/src/Composer/Command/SearchCommand.php @@ -18,6 +18,7 @@ use Symfony\Component\Console\Output\OutputInterface; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\ComposerRepository; +use Composer\Package\PackageInterface; /** * @author Robert Schönthal @@ -60,8 +61,8 @@ EOT foreach ($repos->getPackages() as $package) { foreach ($tokens as $token) { - if (false === ($pos = strpos($package->getName(), $token)) && (false === strpos(join(',',$package->getKeywords() ?: array()), $token))) { - continue; + if ($this->isUnmatchedPackage($package, $token)) { + continue; } if (false !== ($pos = strpos($package->getName(), $token))) { @@ -83,4 +84,19 @@ EOT $output->writeln($name .' : '. join(', ', $versions)); } } + + /** + * tries to find a token within the name/keywords/description + * + * @param PackageInterface $package + * @param string $token + * @return boolean + */ + private function isUnmatchedPackage(PackageInterface $package, $token) + { + return (false === strpos($package->getName(), $token)) && + (false === strpos(join(',',$package->getKeywords() ?: array()), $token)) && + (false === strpos($package->getDescription(), $token)) + ; + } } \ No newline at end of file diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 5e8bf262a..57bdbbbeb 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -132,7 +132,7 @@ EOT */ protected function printMeta(InputInterface $input, OutputInterface $output, PackageInterface $package, RepositoryInterface $installedRepo, RepositoryInterface $repos) { - $output->writeln('name : ' . $package->getPrettyName()); + $output->writeln('name : ' . $package->getPrettyName(). ' '.$package->getDescription()); $this->printVersions($input, $output, $package, $installedRepo, $repos); $output->writeln('type : ' . $package->getType()); $output->writeln('names : ' . join(', ', $package->getNames())); From 3d56a5645e1a1109030c4a328c82b2e386bd2ec3 Mon Sep 17 00:00:00 2001 From: digitalkaoz Date: Sat, 10 Mar 2012 22:32:06 +0100 Subject: [PATCH 51/57] fixed cs --- src/Composer/Command/SearchCommand.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/SearchCommand.php b/src/Composer/Command/SearchCommand.php index c5d692cf1..e09602ec8 100644 --- a/src/Composer/Command/SearchCommand.php +++ b/src/Composer/Command/SearchCommand.php @@ -94,9 +94,9 @@ EOT */ private function isUnmatchedPackage(PackageInterface $package, $token) { - return (false === strpos($package->getName(), $token)) && - (false === strpos(join(',',$package->getKeywords() ?: array()), $token)) && - (false === strpos($package->getDescription(), $token)) + return (false === strpos($package->getName(), $token)) + && (false === strpos(join(',',$package->getKeywords() ?: array()), $token)) + && (false === strpos($package->getDescription(), $token)) ; } } \ No newline at end of file From 9f7155a7530358ad2ab466b52cd072713f9da4ba Mon Sep 17 00:00:00 2001 From: Per Bernhardt Date: Sun, 11 Mar 2012 13:31:25 +0100 Subject: [PATCH 52/57] Fixed the HgDownloader when changing the repo url --- src/Composer/Downloader/HgDownloader.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Composer/Downloader/HgDownloader.php b/src/Composer/Downloader/HgDownloader.php index 629883dc8..f537de5ae 100644 --- a/src/Composer/Downloader/HgDownloader.php +++ b/src/Composer/Downloader/HgDownloader.php @@ -37,10 +37,11 @@ class HgDownloader extends VcsDownloader */ public function doUpdate(PackageInterface $initial, PackageInterface $target, $path) { + $url = escapeshellarg($target->getSourceUrl()); $ref = escapeshellarg($target->getSourceReference()); $path = escapeshellarg($path); $this->io->write(" Updating to ".$target->getSourceReference()); - $this->process->execute(sprintf('cd %s && hg pull && hg up %s', $path, $ref), $ignoredOutput); + $this->process->execute(sprintf('cd %s && hg pull %s && hg up %s', $path, $url, $ref), $ignoredOutput); } /** From 5696b44a4f36e54346db33af60a32057eb4d240d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 11 Mar 2012 15:56:47 +0100 Subject: [PATCH 53/57] Fix tests --- .../Test/Downloader/GitDownloaderTest.php | 24 +++++++++---------- .../Test/Downloader/HgDownloaderTest.php | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/Composer/Test/Downloader/GitDownloaderTest.php b/tests/Composer/Test/Downloader/GitDownloaderTest.php index 389f4ddb7..b6eed80a1 100644 --- a/tests/Composer/Test/Downloader/GitDownloaderTest.php +++ b/tests/Composer/Test/Downloader/GitDownloaderTest.php @@ -41,7 +41,7 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase public function testDownload() { - $expectedGitCommand = $this->getCmd('git clone \'https://example.com/composer/composer\' \'composerPath\' && cd \'composerPath\' && git checkout \'ref\' && git reset --hard \'ref\''); + $expectedGitCommand = $this->getCmd("git clone 'https://example.com/composer/composer' 'composerPath' && cd 'composerPath' && git checkout 'ref' && git reset --hard 'ref'"); $packageMock = $this->getMock('Composer\Package\PackageInterface'); $packageMock->expects($this->any()) ->method('getSourceReference') @@ -70,19 +70,19 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('https://github.com/composer/composer')); $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); - $expectedGitCommand = $this->getCmd('git clone \'git://github.com/composer/composer\' \'composerPath\' && cd \'composerPath\' && git checkout \'ref\' && git reset --hard \'ref\''); + $expectedGitCommand = $this->getCmd("git clone 'git://github.com/composer/composer' 'composerPath' && cd 'composerPath' && git checkout 'ref' && git reset --hard 'ref'"); $processExecutor->expects($this->at(0)) ->method('execute') ->with($this->equalTo($expectedGitCommand)) ->will($this->returnValue(1)); - $expectedGitCommand = $this->getCmd('git clone \'https://github.com/composer/composer\' \'composerPath\' && cd \'composerPath\' && git checkout \'ref\' && git reset --hard \'ref\''); + $expectedGitCommand = $this->getCmd("git clone 'https://github.com/composer/composer' 'composerPath' && cd 'composerPath' && git checkout 'ref' && git reset --hard 'ref'"); $processExecutor->expects($this->at(1)) ->method('execute') ->with($this->equalTo($expectedGitCommand)) ->will($this->returnValue(1)); - $expectedGitCommand = $this->getCmd('git clone \'http://github.com/composer/composer\' \'composerPath\' && cd \'composerPath\' && git checkout \'ref\' && git reset --hard \'ref\''); + $expectedGitCommand = $this->getCmd("git clone 'http://github.com/composer/composer' 'composerPath' && cd 'composerPath' && git checkout 'ref' && git reset --hard 'ref'"); $processExecutor->expects($this->at(2)) ->method('execute') ->with($this->equalTo($expectedGitCommand)) @@ -97,7 +97,7 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase */ public function testDownloadThrowsRuntimeExceptionIfGitCommandFails() { - $expectedGitCommand = $this->getCmd('git clone \'https://example.com/composer/composer\' \'composerPath\' && cd \'composerPath\' && git checkout \'ref\' && git reset --hard \'ref\''); + $expectedGitCommand = $this->getCmd("git clone 'https://example.com/composer/composer' 'composerPath' && cd 'composerPath' && git checkout 'ref' && git reset --hard 'ref'"); $packageMock = $this->getMock('Composer\Package\PackageInterface'); $packageMock->expects($this->any()) ->method('getSourceReference') @@ -132,8 +132,8 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase public function testUpdate() { - $expectedGitUpdateCommand = $this->getCmd('cd \'composerPath\' && git fetch && git checkout \'ref\' && git reset --hard \'ref\''); - $expectedGitResetCommand = $this->getCmd('cd \'composerPath\' && git status --porcelain'); + $expectedGitUpdateCommand = $this->getCmd("cd 'composerPath' && git remote set-url origin 'git://github.com/composer/composer' && git fetch && git checkout 'ref' && git reset --hard 'ref'"); + $expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain"); $packageMock = $this->getMock('Composer\Package\PackageInterface'); $packageMock->expects($this->any()) @@ -141,7 +141,7 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('ref')); $packageMock->expects($this->any()) ->method('getSourceUrl') - ->will($this->returnValue('https://github.com/l3l0/composer')); + ->will($this->returnValue('https://github.com/composer/composer')); $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); $processExecutor->expects($this->at(0)) ->method('execute') @@ -161,8 +161,8 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase */ public function testUpdateThrowsRuntimeExceptionIfGitCommandFails() { - $expectedGitUpdateCommand = $this->getCmd('cd \'composerPath\' && git fetch && git checkout \'ref\' && git reset --hard \'ref\''); - $expectedGitResetCommand = $this->getCmd('cd \'composerPath\' && git status --porcelain'); + $expectedGitUpdateCommand = $this->getCmd("cd 'composerPath' && git remote set-url origin 'git://github.com/composer/composer' && git fetch && git checkout 'ref' && git reset --hard 'ref'"); + $expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain"); $packageMock = $this->getMock('Composer\Package\PackageInterface'); $packageMock->expects($this->any()) @@ -170,7 +170,7 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('ref')); $packageMock->expects($this->any()) ->method('getSourceUrl') - ->will($this->returnValue('https://github.com/l3l0/composer')); + ->will($this->returnValue('https://github.com/composer/composer')); $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); $processExecutor->expects($this->at(0)) ->method('execute') @@ -187,7 +187,7 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase public function testRemove() { - $expectedGitResetCommand = $this->getCmd('cd \'composerPath\' && git status --porcelain'); + $expectedGitResetCommand = $this->getCmd("cd 'composerPath' && git status --porcelain"); $packageMock = $this->getMock('Composer\Package\PackageInterface'); $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); diff --git a/tests/Composer/Test/Downloader/HgDownloaderTest.php b/tests/Composer/Test/Downloader/HgDownloaderTest.php index 39e1efb17..8ca89f667 100644 --- a/tests/Composer/Test/Downloader/HgDownloaderTest.php +++ b/tests/Composer/Test/Downloader/HgDownloaderTest.php @@ -75,8 +75,8 @@ class HgDownloaderTest extends \PHPUnit_Framework_TestCase public function testUpdate() { - $expectedUpdateCommand = $this->getCmd('cd \'composerPath\' && hg pull && hg up \'ref\''); - $expectedResetCommand = $this->getCmd('cd \'composerPath\' && hg st'); + $expectedUpdateCommand = $this->getCmd("cd 'composerPath' && hg pull 'https://github.com/l3l0/composer' && hg up 'ref'"); + $expectedResetCommand = $this->getCmd("cd 'composerPath' && hg st"); $packageMock = $this->getMock('Composer\Package\PackageInterface'); $packageMock->expects($this->any()) From 33c926c303bb089a85521fff4dfae009e3032b1c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 11 Mar 2012 16:09:29 +0100 Subject: [PATCH 54/57] Show/Search command cleanups --- src/Composer/Command/SearchCommand.php | 14 +++++++------- src/Composer/Command/ShowCommand.php | 17 +++++++++++------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Composer/Command/SearchCommand.php b/src/Composer/Command/SearchCommand.php index e09602ec8..59c9231bf 100644 --- a/src/Composer/Command/SearchCommand.php +++ b/src/Composer/Command/SearchCommand.php @@ -61,11 +61,11 @@ EOT foreach ($repos->getPackages() as $package) { foreach ($tokens as $token) { - if ($this->isUnmatchedPackage($package, $token)) { - continue; + if (!$this->matchPackage($package, $token)) { + continue; } - if (false !== ($pos = strpos($package->getName(), $token))) { + if (false !== ($pos = stripos($package->getName(), $token))) { $name = substr($package->getPrettyName(), 0, $pos) . '' . substr($package->getPrettyName(), $pos, strlen($token)) . '' . substr($package->getPrettyName(), $pos + strlen($token)); @@ -92,11 +92,11 @@ EOT * @param string $token * @return boolean */ - private function isUnmatchedPackage(PackageInterface $package, $token) + private function matchPackage(PackageInterface $package, $token) { - return (false === strpos($package->getName(), $token)) - && (false === strpos(join(',',$package->getKeywords() ?: array()), $token)) - && (false === strpos($package->getDescription(), $token)) + return (false !== stripos($package->getName(), $token)) + || (false !== stripos(join(',', $package->getKeywords() ?: array()), $token)) + || (false !== stripos($package->getDescription(), $token)) ; } } \ No newline at end of file diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 57bdbbbeb..7591ebfe2 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -132,21 +132,26 @@ EOT */ protected function printMeta(InputInterface $input, OutputInterface $output, PackageInterface $package, RepositoryInterface $installedRepo, RepositoryInterface $repos) { - $output->writeln('name : ' . $package->getPrettyName(). ' '.$package->getDescription()); + $output->writeln('name : ' . $package->getPrettyName()); + $output->writeln('descrip. : ' . $package->getDescription()); $this->printVersions($input, $output, $package, $installedRepo, $repos); $output->writeln('type : ' . $package->getType()); - $output->writeln('names : ' . join(', ', $package->getNames())); + $output->writeln('license : ' . implode(', ', $package->getLicense())); $output->writeln('source : ' . sprintf('[%s] %s %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference())); $output->writeln('dist : ' . sprintf('[%s] %s %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference())); - $output->writeln('license : ' . join(', ', $package->getLicense())); + $output->writeln('names : ' . implode(', ', $package->getNames())); if ($package->getAutoload()) { $output->writeln("\nautoload"); foreach ($package->getAutoload() as $type => $autoloads) { $output->writeln('' . $type . ''); - foreach ($autoloads as $name => $path) { - $output->writeln($name . ' : ' . ($path ?: '.')); + if ($type === 'psr-0') { + foreach ($autoloads as $name => $path) { + $output->writeln(($name ?: '*') . ' => ' . ($path ?: '.')); + } + } elseif ($type === 'classmap') { + $output->writeln(implode(', ', $autoloads)); } } } @@ -170,7 +175,7 @@ EOT uasort($versions, 'version_compare'); - $versions = join(', ', array_keys(array_reverse($versions))); + $versions = implode(', ', array_keys(array_reverse($versions))); // highlight installed version if ($installedRepo->hasPackage($package)) { From 5dba49af54b150235762ec3d65bd23eeb85e1daa Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 11 Mar 2012 16:44:18 +0100 Subject: [PATCH 55/57] Show package descriptions in show/search commands, merge similar packages in show, fixes #366 --- src/Composer/Command/SearchCommand.php | 18 ++++++++----- src/Composer/Command/ShowCommand.php | 25 ++++++++++++++++--- .../Repository/PlatformRepository.php | 10 +++++--- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/Composer/Command/SearchCommand.php b/src/Composer/Command/SearchCommand.php index 59c9231bf..0a4779ea5 100644 --- a/src/Composer/Command/SearchCommand.php +++ b/src/Composer/Command/SearchCommand.php @@ -19,6 +19,7 @@ use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\ComposerRepository; use Composer\Package\PackageInterface; +use Composer\Package\AliasPackage; /** * @author Robert Schönthal @@ -56,10 +57,14 @@ EOT $repos = new CompositeRepository(array($installedRepo, new ComposerRepository(array('url' => 'http://packagist.org')))); } - $tokens = array_map('strtolower', $input->getArgument('tokens')); + $tokens = $input->getArgument('tokens'); $packages = array(); foreach ($repos->getPackages() as $package) { + if ($package instanceof AliasPackage || isset($packages[$package->getName()])) { + continue; + } + foreach ($tokens as $token) { if (!$this->matchPackage($package, $token)) { continue; @@ -73,15 +78,16 @@ EOT $name = $package->getPrettyName(); } - $version = $installedRepo->hasPackage($package) ? ''.$package->getPrettyVersion().'' : $package->getPrettyVersion(); - - $packages[$name][$package->getPrettyVersion()] = $version; + $packages[$package->getName()] = array( + 'name' => $name, + 'description' => strtok($package->getDescription(), "\r\n") + ); continue 2; } } - foreach ($packages as $name => $versions) { - $output->writeln($name .' : '. join(', ', $versions)); + foreach ($packages as $details) { + $output->writeln($details['name'] .' : '. $details['description']); } } diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 7591ebfe2..c66972ca6 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -83,15 +83,32 @@ EOT } // list packages + $packages = array(); foreach ($repos->getPackages() as $package) { if ($platformRepo->hasPackage($package)) { - $type = 'platform: '; + $type = 'platform:'; } elseif ($installedRepo->hasPackage($package)) { - $type = 'installed: '; + $type = 'installed:'; } else { - $type = 'available: '; + $type = 'available:'; + } + if (isset($packages[$type][$package->getName()]) + && version_compare($packages[$type][$package->getName()]->getVersion(), $package->getVersion(), '>=') + ) { + continue; + } + $packages[$type][$package->getName()] = $package; + } + + foreach (array('platform:', 'available:', 'installed:') as $type) { + if (isset($packages[$type])) { + $output->writeln($type); + ksort($packages[$type]); + foreach ($packages[$type] as $package) { + $output->writeln(' '.$package->getPrettyName() .' : '. strtok($package->getDescription(), "\r\n")); + } + $output->writeln(''); } - $output->writeln($type . ' ' . $package->getPrettyName() . ' ' . $package->getPrettyVersion() . ' (' . $package->getVersion() . ')'); } } diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index 12d5a6aa8..495155276 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -36,14 +36,15 @@ class PlatformRepository extends ArrayRepository } $php = new MemoryPackage('php', $version, $prettyVersion); + $php->setDescription('The PHP interpreter'); parent::addPackage($php); - foreach (get_loaded_extensions() as $ext) { - if (in_array($ext, array('standard', 'Core'))) { + foreach (get_loaded_extensions() as $name) { + if (in_array($name, array('standard', 'Core'))) { continue; } - $reflExt = new \ReflectionExtension($ext); + $reflExt = new \ReflectionExtension($name); try { $prettyVersion = $reflExt->getVersion(); $version = $versionParser->normalize($prettyVersion); @@ -52,7 +53,8 @@ class PlatformRepository extends ArrayRepository $version = $versionParser->normalize($prettyVersion); } - $ext = new MemoryPackage('ext-'.strtolower($ext), $version, $prettyVersion); + $ext = new MemoryPackage('ext-'.$name, $version, $prettyVersion); + $ext->setDescription('The '.$name.' PHP extension'); parent::addPackage($ext); } } From cd6eb49427040f1d73bb15a51724c654032fc97f Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 11 Mar 2012 23:42:27 +0100 Subject: [PATCH 56/57] fix notice when running show command on package without keywords --- src/Composer/Command/ShowCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 4d313e132..042ff420b 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -151,7 +151,7 @@ EOT { $output->writeln('name : ' . $package->getPrettyName()); $output->writeln('descrip. : ' . $package->getDescription()); - $output->writeln('keywords : ' . join(', ', $package->getKeywords())); + $output->writeln('keywords : ' . join(', ', $package->getKeywords() ?: array())); $this->printVersions($input, $output, $package, $installedRepo, $repos); $output->writeln('type : ' . $package->getType()); $output->writeln('license : ' . implode(', ', $package->getLicense())); From 01c1878aeafac3d271ce5f901eb01282b4836653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Haso=C5=88?= Date: Mon, 12 Mar 2012 12:24:11 +0100 Subject: [PATCH 57/57] Save local repo after each success operation --- src/Composer/Installer.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index a0f85576a..dad3dc08e 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -278,6 +278,8 @@ class Installer $this->installationManager->execute($operation); $this->eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType())), $operation); + + $localRepo->write(); } }