Forráskód Böngészése

Merge branch 'master'

Conflicts:
	src/Composer/Console/Application.php
François Pluchino 13 éve
szülő
commit
8053878b6a

+ 1 - 1
README.md

@@ -53,7 +53,7 @@ Global installation of composer (via homebrew)
 
 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`
 3. Use composer as you normally would `composer.phar install`
 

+ 3 - 0
phpunit.xml.dist

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

+ 29 - 12
src/Composer/Command/DependsCommand.php

@@ -16,10 +16,12 @@ use Composer\Composer;
 use Composer\Package\PackageInterface;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Output\OutputInterface;
 
 /**
  * @author Justin Rainbow <justin.rainbow@gmail.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
  */
 class DependsCommand extends Command
 {
@@ -27,13 +29,14 @@ class DependsCommand extends Command
     {
         $this
             ->setName('depends')
-            ->setDescription('Where is a package used?')
+            ->setDescription('Shows which packages depend on the given package')
             ->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
-The depends command displays detailed information about where a
-package is referenced.
+Displays detailed information about where a package is referenced.
+
 <info>php composer.phar depends composer/composer</info>
 
 EOT
@@ -46,7 +49,11 @@ EOT
         $composer = $this->getComposer();
         $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');
 
         $references = array();
+        $verbose = (Boolean) $input->getOption('verbose');
 
-        // check if we have a local installation so we can grab the right package/version
-        $repos = array_merge(
-            array($composer->getRepositoryManager()->getLocalRepository()),
-            $composer->getRepositoryManager()->getRepositories()
-        );
+        $repos = $composer->getRepositoryManager()->getRepositories();
+        $types = $input->getOption('link-type');
         foreach ($repos as $repository) {
             foreach ($repository->getPackages() as $package) {
-                foreach (array('requires', 'recommends', 'suggests') as $type) {
+                foreach ($types as $type) {
                     foreach ($package->{'get'.$type}() as $link) {
                         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());
         }
     }
+
+    private function printPackages(InputInterface $input, OutputInterface $output, array $packages)
+    {
+        ksort($packages);
+        foreach ($packages as $package) {
+            $output->writeln($package);
+        }
+    }
 }

+ 5 - 9
src/Composer/Command/ShowCommand.php

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

+ 8 - 94
src/Composer/Console/Application.php

@@ -21,12 +21,8 @@ use Symfony\Component\Console\Formatter\OutputFormatterStyle;
 use Symfony\Component\Finder\Finder;
 use Composer\Command;
 use Composer\Composer;
-use Composer\Installer;
-use Composer\Downloader;
-use Composer\Repository;
-use Composer\Package;
-use Composer\Json\JsonFile;
-use Composer\IO\IOInterface;
+use Composer\Factory;
+use Composer\IO\IOInterface;
 use Composer\IO\ConsoleIO;
 
 /**
@@ -78,7 +74,12 @@ class Application extends BaseApplication
     public function getComposer()
     {
         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;
@@ -92,93 +93,6 @@ class Application extends BaseApplication
         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
      */

+ 141 - 0
src/Composer/Factory.php

@@ -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);
+    }
+}

+ 82 - 7
src/Composer/Json/JsonFile.php

@@ -15,11 +15,6 @@ namespace Composer\Json;
 use Composer\Repository\RepositoryManager;
 use Composer\Composer;
 
-// defined as of PHP 5.4
-if (!defined('JSON_PRETTY_PRINT')) {
-    define('JSON_PRETTY_PRINT', 128);
-}
-
 /**
  * Reads/writes json files.
  *
@@ -80,8 +75,9 @@ class JsonFile
      * Writes 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);
         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;
     }
 
     /**

+ 18 - 0
src/Composer/Repository/ArrayRepository.php

@@ -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}
      */

+ 9 - 0
src/Composer/Repository/RepositoryInterface.php

@@ -41,6 +41,15 @@ interface RepositoryInterface extends \Countable
      */
     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.
      *

+ 19 - 0
tests/Composer/Test/DependencyResolver/SolverTest.php

@@ -115,6 +115,25 @@ class SolverTest extends TestCase
         $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()
     {
         $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));

+ 17 - 0
tests/Composer/Test/Json/JsonFileTest.php

@@ -84,6 +84,15 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase
         $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)
     {
         try {
@@ -93,4 +102,12 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase
             $this->assertContains($text, $e->getMessage());
         }
     }
+
+    private function assertJsonFormat($json, $data)
+    {
+        $file = new JsonFile('composer.json');
+
+        $this->assertEquals($json, $file->encode($data));
+    }
+
 }

+ 16 - 0
tests/Composer/Test/Repository/ArrayRepositoryTest.php

@@ -50,4 +50,20 @@ class ArrayRepositoryTest extends TestCase
         $this->assertTrue($repo->hasPackage($this->getPackage('foo', '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());
+    }
 }