1
0
Fork 0

Merge branch 'master'

Conflicts:
	src/Composer/Console/Application.php
pull/198/head
François Pluchino 2012-01-17 17:56:06 +01:00
commit 8053878b6a
12 changed files with 348 additions and 123 deletions

View File

@ -53,7 +53,7 @@ Global installation of composer (via homebrew)
Installing via this homebrew formula will always get you the latest composer version. Installing via this homebrew formula will always get you the latest composer version.
1. run `brew uninstall composer ; brew install https://raw.github.com/gist/1574469/composer.rb` 1. run `brew uninstall composer ; brew install --HEAD https://raw.github.com/gist/1574469/composer.rb`
2. Change into a project directory `cd /path/to/my/project` 2. Change into a project directory `cd /path/to/my/project`
3. Use composer as you normally would `composer.phar install` 3. Use composer as you normally would `composer.phar install`

View File

@ -20,6 +20,9 @@
<filter> <filter>
<whitelist> <whitelist>
<directory>./src/Composer/</directory> <directory>./src/Composer/</directory>
<exclude>
<file>./src/Composer/Autoload/ClassLoader.php</file>
</exclude>
</whitelist> </whitelist>
</filter> </filter>
</phpunit> </phpunit>

View File

@ -16,10 +16,12 @@ use Composer\Composer;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
/** /**
* @author Justin Rainbow <justin.rainbow@gmail.com> * @author Justin Rainbow <justin.rainbow@gmail.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/ */
class DependsCommand extends Command class DependsCommand extends Command
{ {
@ -27,13 +29,14 @@ class DependsCommand extends Command
{ {
$this $this
->setName('depends') ->setName('depends')
->setDescription('Where is a package used?') ->setDescription('Shows which packages depend on the given package')
->setDefinition(array( ->setDefinition(array(
new InputArgument('package', InputArgument::REQUIRED, 'the package to inspect') new InputArgument('package', InputArgument::REQUIRED, 'Package to inspect'),
new InputOption('link-type', '', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Link types to show', array('requires', 'recommends', 'suggests'))
)) ))
->setHelp(<<<EOT ->setHelp(<<<EOT
The depends command displays detailed information about where a Displays detailed information about where a package is referenced.
package is referenced.
<info>php composer.phar depends composer/composer</info> <info>php composer.phar depends composer/composer</info>
EOT EOT
@ -46,7 +49,11 @@ EOT
$composer = $this->getComposer(); $composer = $this->getComposer();
$references = $this->getReferences($input, $output, $composer); $references = $this->getReferences($input, $output, $composer);
$this->printReferences($input, $output, $references); if ($input->getOption('verbose')) {
$this->printReferences($input, $output, $references);
} else {
$this->printPackages($input, $output, $references);
}
} }
/** /**
@ -63,18 +70,20 @@ EOT
$needle = $input->getArgument('package'); $needle = $input->getArgument('package');
$references = array(); $references = array();
$verbose = (Boolean) $input->getOption('verbose');
// check if we have a local installation so we can grab the right package/version $repos = $composer->getRepositoryManager()->getRepositories();
$repos = array_merge( $types = $input->getOption('link-type');
array($composer->getRepositoryManager()->getLocalRepository()),
$composer->getRepositoryManager()->getRepositories()
);
foreach ($repos as $repository) { foreach ($repos as $repository) {
foreach ($repository->getPackages() as $package) { foreach ($repository->getPackages() as $package) {
foreach (array('requires', 'recommends', 'suggests') as $type) { foreach ($types as $type) {
foreach ($package->{'get'.$type}() as $link) { foreach ($package->{'get'.$type}() as $link) {
if ($link->getTarget() === $needle) { if ($link->getTarget() === $needle) {
$references[] = array($type, $package, $link); if ($verbose) {
$references[] = array($type, $package, $link);
} else {
$references[$package->getName()] = $package->getPrettyName();
}
} }
} }
} }
@ -90,4 +99,12 @@ EOT
$output->writeln($ref[1]->getPrettyName() . ' ' . $ref[1]->getPrettyVersion() . ' <info>' . $ref[0] . '</info> ' . $ref[2]->getPrettyConstraint()); $output->writeln($ref[1]->getPrettyName() . ' ' . $ref[1]->getPrettyVersion() . ' <info>' . $ref[0] . '</info> ' . $ref[2]->getPrettyConstraint());
} }
} }
private function printPackages(InputInterface $input, OutputInterface $output, array $packages)
{
ksort($packages);
foreach ($packages as $package) {
$output->writeln($package);
}
}
} }

View File

@ -85,11 +85,9 @@ EOT
$composer->getRepositoryManager()->getRepositories() $composer->getRepositoryManager()->getRepositories()
); );
foreach ($repos as $repository) { foreach ($repos as $repository) {
foreach ($repository->getPackages() as $package) { foreach ($repository->findPackagesByName($input->getArgument('package')) as $package) {
if ($package->getName() === $input->getArgument('package')) { if (null === $highestVersion || version_compare($package->getVersion(), $highestVersion->getVersion(), '>=')) {
if (null === $highestVersion || version_compare($package->getVersion(), $highestVersion->getVersion(), '>=')) { $highestVersion = $package;
$highestVersion = $package;
}
} }
} }
} }
@ -135,10 +133,8 @@ EOT
$versions = array(); $versions = array();
foreach ($composer->getRepositoryManager()->getRepositories() as $repository) { foreach ($composer->getRepositoryManager()->getRepositories() as $repository) {
foreach ($repository->getPackages() as $version) { foreach ($repository->findPackagesByName($package->getName()) as $version) {
if ($version->getName() === $package->getName()) { $versions[] = $version->getPrettyVersion();
$versions[] = $version->getPrettyVersion();
}
} }
} }

View File

@ -21,12 +21,8 @@ use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\Finder;
use Composer\Command; use Composer\Command;
use Composer\Composer; use Composer\Composer;
use Composer\Installer; use Composer\Factory;
use Composer\Downloader; use Composer\IO\IOInterface;
use Composer\Repository;
use Composer\Package;
use Composer\Json\JsonFile;
use Composer\IO\IOInterface;
use Composer\IO\ConsoleIO; use Composer\IO\ConsoleIO;
/** /**
@ -78,7 +74,12 @@ class Application extends BaseApplication
public function getComposer() public function getComposer()
{ {
if (null === $this->composer) { if (null === $this->composer) {
$this->composer = self::bootstrapComposer($this->io); try {
$this->composer = Factory::create($this->io);
} catch (\InvalidArgumentException $e) {
$this->io->writeln($e->getMessage());
exit(1);
}
} }
return $this->composer; return $this->composer;
@ -92,93 +93,6 @@ class Application extends BaseApplication
return $this->io; return $this->io;
} }
/**
* Bootstraps a Composer instance
*
* @return Composer
*/
public static function bootstrapComposer(IOInterface $io, $composerFile = null)
{
// load Composer configuration
if (null === $composerFile) {
$composerFile = getenv('COMPOSER') ?: 'composer.json';
}
$file = new JsonFile($composerFile);
if (!$file->exists()) {
if ($composerFile === 'composer.json') {
$this->io->writeln('Composer could not find a composer.json file in '.getcwd());
} else {
$this->io->writeln('Composer could not find the config file: '.$composerFile);
}
$this->io->writeln('To initialize a project, please create a composer.json file as described on the http://packagist.org/ "Getting Started" section');
exit(1);
}
// Configuration defaults
$composerConfig = array(
'vendor-dir' => 'vendor',
);
$packageConfig = $file->read();
if (isset($packageConfig['config']) && is_array($packageConfig['config'])) {
$packageConfig['config'] = array_merge($composerConfig, $packageConfig['config']);
} else {
$packageConfig['config'] = $composerConfig;
}
$vendorDir = getenv('COMPOSER_VENDOR_DIR') ?: $packageConfig['config']['vendor-dir'];
if (!isset($packageConfig['config']['bin-dir'])) {
$packageConfig['config']['bin-dir'] = $vendorDir.'/bin';
}
$binDir = getenv('COMPOSER_BIN_DIR') ?: $packageConfig['config']['bin-dir'];
// initialize repository manager
$rm = new Repository\RepositoryManager($io);
$rm->setLocalRepository(new Repository\FilesystemRepository(new JsonFile($vendorDir.'/.composer/installed.json')));
$rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository');
$rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository');
$rm->setRepositoryClass('pear', 'Composer\Repository\PearRepository');
$rm->setRepositoryClass('package', 'Composer\Repository\PackageRepository');
// initialize download manager
$dm = new Downloader\DownloadManager();
$dm->setDownloader('git', new Downloader\GitDownloader());
$dm->setDownloader('svn', new Downloader\SvnDownloader());
$dm->setDownloader('hg', new Downloader\HgDownloader());
$dm->setDownloader('pear', new Downloader\PearDownloader($io));
$dm->setDownloader('zip', new Downloader\ZipDownloader($io));
// initialize installation manager
$im = new Installer\InstallationManager($vendorDir);
$im->addInstaller(new Installer\LibraryInstaller($vendorDir, $binDir, $dm, $rm->getLocalRepository(), $io, null));
$im->addInstaller(new Installer\InstallerInstaller($vendorDir, $binDir, $dm, $rm->getLocalRepository(), $io, $im));
// load package
$loader = new Package\Loader\RootPackageLoader($rm);
$package = $loader->load($packageConfig);
// load default repository unless it's explicitly disabled
if (!isset($packageConfig['repositories']['packagist']) || $packageConfig['repositories']['packagist'] !== false) {
$rm->addRepository(new Repository\ComposerRepository(array('url' => 'http://packagist.org')));
}
// init locker
$lockFile = substr($composerFile, -5) === '.json' ? substr($composerFile, 0, -4).'lock' : $composerFile . '.lock';
$locker = new Package\Locker(new JsonFile($lockFile), $rm, md5_file($composerFile));
// initialize composer
$composer = new Composer();
$composer->setPackage($package);
$composer->setLocker($locker);
$composer->setRepositoryManager($rm);
$composer->setDownloadManager($dm);
$composer->setInstallationManager($im);
return $composer;
}
/** /**
* Initializes all the composer commands * Initializes all the composer commands
*/ */

141
src/Composer/Factory.php Normal file
View File

@ -0,0 +1,141 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Json\JsonFile;
use Composer\IO\IOInterface;
/**
* Creates an configured instance of composer.
*
* @author Ryan Weaver <ryan@knplabs.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Igor Wiedler <igor@wiedler.ch>
*/
class Factory
{
/**
* Creates a Composer instance
*
* @return Composer
*/
public function createComposer(IOInterface $io, $composerFile = null)
{
// load Composer configuration
if (null === $composerFile) {
$composerFile = getenv('COMPOSER') ?: 'composer.json';
}
$file = new JsonFile($composerFile);
if (!$file->exists()) {
if ($composerFile === 'composer.json') {
$message = 'Composer could not find a composer.json file in '.getcwd();
} else {
$message = 'Composer could not find the config file: '.$composerFile;
}
$instructions = 'To initialize a project, please create a composer.json file as described on the http://packagist.org/ "Getting Started" section';
throw new \InvalidArgumentException($message.PHP_EOL.$instructions);
}
// Configuration defaults
$composerConfig = array(
'vendor-dir' => 'vendor',
);
$packageConfig = $file->read();
if (isset($packageConfig['config']) && is_array($packageConfig['config'])) {
$packageConfig['config'] = array_merge($composerConfig, $packageConfig['config']);
} else {
$packageConfig['config'] = $composerConfig;
}
$vendorDir = getenv('COMPOSER_VENDOR_DIR') ?: $packageConfig['config']['vendor-dir'];
if (!isset($packageConfig['config']['bin-dir'])) {
$packageConfig['config']['bin-dir'] = $vendorDir.'/bin';
}
$binDir = getenv('COMPOSER_BIN_DIR') ?: $packageConfig['config']['bin-dir'];
// initialize repository manager
$rm = $this->createRepositoryManager($io, $vendorDir);
// initialize download manager
$dm = $this->createDownloadManager($io);
// initialize installation manager
$im = $this->createInstallationManager($rm, $dm, $vendorDir, $binDir, $io);
// load package
$loader = new Package\Loader\RootPackageLoader($rm);
$package = $loader->load($packageConfig);
// load default repository unless it's explicitly disabled
if (!isset($packageConfig['repositories']['packagist']) || $packageConfig['repositories']['packagist'] !== false) {
$rm->addRepository(new Repository\ComposerRepository(array('url' => 'http://packagist.org')));
}
// init locker
$lockFile = substr($composerFile, -5) === '.json' ? substr($composerFile, 0, -4).'lock' : $composerFile . '.lock';
$locker = new Package\Locker(new JsonFile($lockFile), $rm, md5_file($composerFile));
// initialize composer
$composer = new Composer();
$composer->setPackage($package);
$composer->setLocker($locker);
$composer->setRepositoryManager($rm);
$composer->setDownloadManager($dm);
$composer->setInstallationManager($im);
return $composer;
}
protected function createRepositoryManager(IOInterface $io, $vendorDir)
{
$rm = new Repository\RepositoryManager($io);
$rm->setLocalRepository(new Repository\FilesystemRepository(new JsonFile($vendorDir.'/.composer/installed.json')));
$rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository');
$rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository');
$rm->setRepositoryClass('pear', 'Composer\Repository\PearRepository');
$rm->setRepositoryClass('package', 'Composer\Repository\PackageRepository');
return $rm;
}
protected function createDownloadManager(IOInterface $io)
{
$dm = new Downloader\DownloadManager();
$dm->setDownloader('git', new Downloader\GitDownloader());
$dm->setDownloader('svn', new Downloader\SvnDownloader());
$dm->setDownloader('hg', new Downloader\HgDownloader());
$dm->setDownloader('pear', new Downloader\PearDownloader($io));
$dm->setDownloader('zip', new Downloader\ZipDownloader($io));
return $dm;
}
protected function createInstallationManager(Repository\RepositoryManager $rm, Downloader\DownloadManager $dm, $vendorDir, $binDir, IOInterface $io)
{
$im = new Installer\InstallationManager($vendorDir);
$im->addInstaller(new Installer\LibraryInstaller($vendorDir, $binDir, $dm, $rm->getLocalRepository(), $io, null));
$im->addInstaller(new Installer\InstallerInstaller($vendorDir, $binDir, $dm, $rm->getLocalRepository(), $io, $im));
return $im;
}
static public function create(IOInterface $io, $composerFile = null)
{
$factory = new static();
return $factory->createComposer($io, $composerFile);
}
}

View File

@ -15,11 +15,6 @@ namespace Composer\Json;
use Composer\Repository\RepositoryManager; use Composer\Repository\RepositoryManager;
use Composer\Composer; use Composer\Composer;
// defined as of PHP 5.4
if (!defined('JSON_PRETTY_PRINT')) {
define('JSON_PRETTY_PRINT', 128);
}
/** /**
* Reads/writes json files. * Reads/writes json files.
* *
@ -80,8 +75,9 @@ class JsonFile
* Writes json file. * Writes json file.
* *
* @param array $hash writes hash into json file * @param array $hash writes hash into json file
* @param Boolean $prettyPrint If true, output is pretty-printed
*/ */
public function write(array $hash) public function write(array $hash, $prettyPrint = true)
{ {
$dir = dirname($this->path); $dir = dirname($this->path);
if (!is_dir($dir)) { if (!is_dir($dir)) {
@ -96,7 +92,86 @@ class JsonFile
); );
} }
} }
file_put_contents($this->path, json_encode($hash, JSON_PRETTY_PRINT)); file_put_contents($this->path, $this->encode($hash, $prettyPrint));
}
/**
* Encodes an array into (optionally pretty-printed) JSON
*
* Original code for this function can be found at:
* http://recursive-design.com/blog/2008/03/11/format-json-with-php/
*
* @param array $hash Data to encode into a formatted JSON string
* @param Boolean $prettyPrint If true, output is pretty-printed
* @return string Indented version of the original JSON string
*/
public function encode(array $hash, $prettyPrint = true)
{
if ($prettyPrint && defined('JSON_PRETTY_PRINT')) {
return json_encode($hash, JSON_PRETTY_PRINT);
}
$json = json_encode($hash);
if (!$prettyPrint) {
return $json;
}
$result = '';
$pos = 0;
$strLen = strlen($json);
$indentStr = ' ';
$newLine = "\n";
$prevChar = '';
$outOfQuotes = true;
for ($i = 0; $i <= $strLen; $i++) {
// Grab the next character in the string
$char = substr($json, $i, 1);
// Are we inside a quoted string?
if ('"' === $char && '\\' !== $prevChar) {
$outOfQuotes = !$outOfQuotes;
} elseif (':' === $char && $outOfQuotes) {
// Add a space after the : character
$char .= ' ';
} elseif (('}' === $char || ']' === $char) && $outOfQuotes) {
$pos--;
if ('{' !== $prevChar && '[' !== $prevChar) {
// If this character is the end of an element,
// output a new line and indent the next line
$result .= $newLine;
for ($j = 0; $j < $pos; $j++) {
$result .= $indentStr;
}
} else {
// Collapse empty {} and []
$result = rtrim($result);
}
}
// Add the character to the result string
$result .= $char;
// If the last character was the beginning of an element,
// output a new line and indent the next line
if ((',' === $char || '{' === $char || '[' === $char) && $outOfQuotes) {
$result .= $newLine;
if ('{' === $char || '[' === $char) {
$pos++;
}
for ($j = 0; $j < $pos; $j++) {
$result .= $indentStr;
}
}
$prevChar = $char;
}
return $result;
} }
/** /**

View File

@ -41,6 +41,24 @@ class ArrayRepository implements RepositoryInterface
} }
} }
/**
* {@inheritDoc}
*/
public function findPackagesByName($name)
{
// normalize name
$name = strtolower($name);
$packages = array();
foreach ($this->getPackages() as $package) {
if ($package->getName() === $name) {
$packages[] = $package;
}
}
return $packages;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View File

@ -41,6 +41,15 @@ interface RepositoryInterface extends \Countable
*/ */
function findPackage($name, $version); function findPackage($name, $version);
/**
* Searches for packages by it's name.
*
* @param string $name package name
*
* @return array
*/
function findPackagesByName($name);
/** /**
* Returns list of registered packages. * Returns list of registered packages.
* *

View File

@ -115,6 +115,25 @@ class SolverTest extends TestCase
$this->checkSolverResult(array()); $this->checkSolverResult(array());
} }
public function testSolverUpdateDoesOnlyUpdate()
{
$this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
$this->repoInstalled->addPackage($packageB = $this->getPackage('B', '1.0'));
$this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1'));
$this->reposComplete();
$packageA->setRequires(array(new Link('A', 'B', new VersionConstraint('>=', '1.0.0.0'), 'requires')));
$this->request->install('A', new VersionConstraint('=', '1.0.0.0'));
$this->request->install('B', new VersionConstraint('=', '1.1.0.0'));
$this->request->update('A', new VersionConstraint('=', '1.0.0.0'));
$this->request->update('B', new VersionConstraint('=', '1.0.0.0'));
$this->checkSolverResult(array(
array('job' => 'update', 'from' => $packageB, 'to' => $newPackageB),
));
}
public function testSolverUpdateSingle() public function testSolverUpdateSingle()
{ {
$this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));

View File

@ -84,6 +84,15 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase
$this->expectParseException('missing comma on line 2, char 21', $json); $this->expectParseException('missing comma on line 2, char 21', $json);
} }
public function testSimpleJsonString()
{
$data = array('name' => 'composer/composer');
$json = '{
"name": "composer\/composer"
}';
$this->assertJsonFormat($json, $data);
}
private function expectParseException($text, $json) private function expectParseException($text, $json)
{ {
try { try {
@ -93,4 +102,12 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase
$this->assertContains($text, $e->getMessage()); $this->assertContains($text, $e->getMessage());
} }
} }
private function assertJsonFormat($json, $data)
{
$file = new JsonFile('composer.json');
$this->assertEquals($json, $file->encode($data));
}
} }

View File

@ -50,4 +50,20 @@ class ArrayRepositoryTest extends TestCase
$this->assertTrue($repo->hasPackage($this->getPackage('foo', '1'))); $this->assertTrue($repo->hasPackage($this->getPackage('foo', '1')));
$this->assertFalse($repo->hasPackage($this->getPackage('bar', '1'))); $this->assertFalse($repo->hasPackage($this->getPackage('bar', '1')));
} }
public function testFindPackagesByName()
{
$repo = new ArrayRepository();
$repo->addPackage($this->getPackage('foo', '1'));
$repo->addPackage($this->getPackage('bar', '2'));
$repo->addPackage($this->getPackage('bar', '3'));
$foo = $repo->findPackagesByName('foo');
$this->assertCount(1, $foo);
$this->assertEquals('foo', $foo[0]->getName());
$bar = $repo->findPackagesByName('bar');
$this->assertCount(2, $bar);
$this->assertEquals('bar', $bar[0]->getName());
}
} }