From c8a685be6b0fc0f8c1badd933c9fd67c009a490f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Aug 2012 15:44:45 +0200 Subject: [PATCH 01/20] Reduce memory usage by only loading packages that are actually needed, fixes #456 --- src/Composer/DependencyResolver/Pool.php | 176 +++++++++-- src/Composer/Package/BasePackage.php | 35 -- src/Composer/Package/CorePackageInterface.php | 298 ++++++++++++++++++ src/Composer/Package/PackageInterface.php | 294 +---------------- src/Composer/Package/RootPackageInterface.php | 49 +++ .../Repository/ComposerRepository.php | 72 ++++- .../StreamableRepositoryInterface.php | 30 ++ 7 files changed, 596 insertions(+), 358 deletions(-) create mode 100644 src/Composer/Package/CorePackageInterface.php create mode 100644 src/Composer/Package/RootPackageInterface.php create mode 100644 src/Composer/Repository/StreamableRepositoryInterface.php diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 0e3030738..b88666dda 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -13,10 +13,15 @@ namespace Composer\DependencyResolver; use Composer\Package\BasePackage; +use Composer\Package\Version\VersionParser; +use Composer\Package\Loader\ArrayLoader; +use Composer\Package\Link; use Composer\Package\LinkConstraint\LinkConstraintInterface; +use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Repository\RepositoryInterface; use Composer\Repository\CompositeRepository; use Composer\Repository\InstalledRepositoryInterface; +use Composer\Repository\StreamableRepositoryInterface; use Composer\Repository\PlatformRepository; /** @@ -27,15 +32,25 @@ use Composer\Repository\PlatformRepository; */ class Pool { + const MATCH_NAME = -1; + const MATCH_NONE = 0; + const MATCH = 1; + const MATCH_PROVIDE = 2; + const MATCH_REPLACE = 3; + protected $repositories = array(); protected $packages = array(); protected $packageByName = array(); protected $acceptableStabilities; protected $stabilityFlags; + protected $loader; + protected $versionParser; public function __construct($minimumStability = 'stable', array $stabilityFlags = array()) { $stabilities = BasePackage::$stabilities; + $this->loader = new ArrayLoader; + $this->versionParser = new VersionParser; $this->acceptableStabilities = array(); foreach (BasePackage::$stabilities as $stability => $value) { if ($value <= BasePackage::$stabilities[$minimumStability]) { @@ -63,25 +78,65 @@ class Pool $this->repositories[] = $repo; $exempt = $repo instanceof PlatformRepository || $repo instanceof InstalledRepositoryInterface; - foreach ($repo->getPackages() as $package) { - $name = $package->getName(); - $stability = $package->getStability(); - if ( - // always allow exempt repos - $exempt - // allow if package matches the global stability requirement and has no exception - || (!isset($this->stabilityFlags[$name]) - && isset($this->acceptableStabilities[$stability])) - // allow if package matches the package-specific stability flag - || (isset($this->stabilityFlags[$name]) - && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$name] - ) - ) { - $package->setId($id++); - $this->packages[] = $package; + if ($repo instanceof StreamableRepositoryInterface) { + foreach ($repo->getMinimalPackages() as $package) { + $name = $package['name']; + $stability = VersionParser::parseStability($package['version']); + if ( + // always allow exempt repos + $exempt + // allow if package matches the global stability requirement and has no exception + || (!isset($this->stabilityFlags[$name]) + && isset($this->acceptableStabilities[$stability])) + // allow if package matches the package-specific stability flag + || (isset($this->stabilityFlags[$name]) + && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$name] + ) + ) { + $package['id'] = $id++; + $this->packages[] = $package; - foreach ($package->getNames() as $name) { - $this->packageByName[$name][] = $package; + // collect names + $names = array( + $name => true, + ); + if (isset($package['provide'])) { + foreach ($package['provide'] as $target => $constraint) { + $names[$target] = true; + } + } + if (isset($package['replace'])) { + foreach ($package['replace'] as $target => $constraint) { + $names[$target] = true; + } + } + + foreach (array_keys($names) as $name) { + $this->packageByName[$name][] =& $this->packages[$id-2]; + } + } + } + } else { + foreach ($repo->getPackages() as $package) { + $name = $package->getName(); + $stability = $package->getStability(); + if ( + // always allow exempt repos + $exempt + // allow if package matches the global stability requirement and has no exception + || (!isset($this->stabilityFlags[$name]) + && isset($this->acceptableStabilities[$stability])) + // allow if package matches the package-specific stability flag + || (isset($this->stabilityFlags[$name]) + && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$name] + ) + ) { + $package->setId($id++); + $this->packages[] = $package; + + foreach ($package->getNames() as $name) { + $this->packageByName[$name][] = $package; + } } } } @@ -107,6 +162,8 @@ class Pool */ public function packageById($id) { + $this->ensurePackageIsLoaded($this->packages[$id - 1]); + return $this->packages[$id - 1]; } @@ -137,6 +194,10 @@ class Pool $candidates = $this->packageByName[$name]; if (null === $constraint) { + foreach ($candidates as $key => $candidate) { + $candidates[$key] = $this->ensurePackageIsLoaded($candidate); + } + return $candidates; } @@ -144,25 +205,25 @@ class Pool $nameMatch = false; foreach ($candidates as $candidate) { - switch ($candidate->matches($name, $constraint)) { - case BasePackage::MATCH_NONE: + switch ($this->match($candidate, $name, $constraint)) { + case self::MATCH_NONE: break; - case BasePackage::MATCH_NAME: + case self::MATCH_NAME: $nameMatch = true; break; - case BasePackage::MATCH: + case self::MATCH: $nameMatch = true; - $matches[] = $candidate; + $matches[] = $this->ensurePackageIsLoaded($candidate); break; - case BasePackage::MATCH_PROVIDE: - $provideMatches[] = $candidate; + case self::MATCH_PROVIDE: + $provideMatches[] = $this->ensurePackageIsLoaded($candidate); break; - case BasePackage::MATCH_REPLACE: - $matches[] = $candidate; + case self::MATCH_REPLACE: + $matches[] = $this->ensurePackageIsLoaded($candidate); break; default: @@ -202,4 +263,65 @@ class Pool return $prefix.' '.$package->getPrettyString(); } + + private function ensurePackageIsLoaded($data) + { + if (is_array($data)) { + $data = $this->packages[$data['id'] - 1] = $data['repo']->loadPackage($data, $data['id']); + } + + return $data; + } + + /** + * Checks if the package matches the given constraint directly or through + * provided or replaced packages + * + * @param array|PackageInterface $candidate + * @param string $name Name of the package to be matched + * @param LinkConstraintInterface $constraint The constraint to verify + * @return int One of the MATCH* constants of this class or 0 if there is no match + */ + private function match($candidate, $name, LinkConstraintInterface $constraint) + { + if (is_array($candidate)) { + $candidateName = $candidate['name']; + $candidateVersion = $candidate['version']; + foreach (array('provides', 'replaces') as $linkType) { + $$linkType = isset($candidate[rtrim($linkType, 's')]) ? $candidate[rtrim($linkType, 's')] : array(); + foreach ($$linkType as $target => $constraintDef) { + if ('self.version' === $constraintDef) { + $parsedConstraint = $this->versionParser->parseConstraints($candidateVersion); + } else { + $parsedConstraint = $this->versionParser->parseConstraints($constraintDef); + } + ${$linkType}[$target] = new Link($candidateName, $target, $parsedConstraint, $linkType, $constraintDef); + } + } + } else { + $candidateName = $candidate->getName(); + $candidateVersion = $candidate->getVersion(); + $provides = $candidate->getProvides(); + $replaces = $candidate->getReplaces(); + } + + if ($candidateName === $name) { + return $constraint->matches(new VersionConstraint('==', $candidateVersion)) ? self::MATCH : self::MATCH_NAME; + } + + foreach ($provides as $link) { + if ($link->getTarget() === $name && $constraint->matches($link->getConstraint())) { + return self::MATCH_PROVIDE; + } + } + + foreach ($replaces as $link) { + if ($link->getTarget() === $name && $constraint->matches($link->getConstraint())) { + return self::MATCH_REPLACE; + } + } + + return self::MATCH_NONE; + } + } diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index 13c5c1fe5..365ab9bb5 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -38,12 +38,6 @@ abstract class BasePackage implements PackageInterface const STABILITY_ALPHA = 15; const STABILITY_DEV = 20; - const MATCH_NAME = -1; - const MATCH_NONE = 0; - const MATCH = 1; - const MATCH_PROVIDE = 2; - const MATCH_REPLACE = 3; - public static $stabilities = array( 'stable' => self::STABILITY_STABLE, 'RC' => self::STABILITY_RC, @@ -122,35 +116,6 @@ abstract class BasePackage implements PackageInterface return $this->id; } - /** - * Checks if the package matches the given constraint directly or through - * provided or replaced packages - * - * @param string $name Name of the package to be matched - * @param LinkConstraintInterface $constraint The constraint to verify - * @return int One of the MATCH* constants of this class or 0 if there is no match - */ - public function matches($name, LinkConstraintInterface $constraint) - { - if ($this->name === $name) { - return $constraint->matches(new VersionConstraint('==', $this->getVersion())) ? self::MATCH : self::MATCH_NAME; - } - - foreach ($this->getProvides() as $link) { - if ($link->getTarget() === $name && $constraint->matches($link->getConstraint())) { - return self::MATCH_PROVIDE; - } - } - - foreach ($this->getReplaces() as $link) { - if ($link->getTarget() === $name && $constraint->matches($link->getConstraint())) { - return self::MATCH_REPLACE; - } - } - - return self::MATCH_NONE; - } - public function getRepository() { return $this->repository; diff --git a/src/Composer/Package/CorePackageInterface.php b/src/Composer/Package/CorePackageInterface.php new file mode 100644 index 000000000..3b03cfa2f --- /dev/null +++ b/src/Composer/Package/CorePackageInterface.php @@ -0,0 +1,298 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package; + +use Composer\Package\LinkConstraint\LinkConstraintInterface; +use Composer\Repository\RepositoryInterface; + +/** + * Defines the essential information a package has that is used during solving/installation + * + * @author Jordi Boggiano + */ +interface CorePackageInterface +{ + /** + * Returns the package's name without version info, thus not a unique identifier + * + * @return string package name + */ + public function getName(); + + /** + * Returns the package's pretty (i.e. with proper case) name + * + * @return string package name + */ + public function getPrettyName(); + + /** + * Returns a set of names that could refer to this package + * + * No version or release type information should be included in any of the + * names. Provided or replaced package names need to be returned as well. + * + * @return array An array of strings referring to this package + */ + public function getNames(); + + /** + * Allows the solver to set an id for this package to refer to it. + * + * @param int $id + */ + public function setId($id); + + /** + * Retrieves the package's id set through setId + * + * @return int The previously set package id + */ + public function getId(); + + /** + * Returns whether the package is a development virtual package or a concrete one + * + * @return bool + */ + public function isDev(); + + /** + * Returns the package type, e.g. library + * + * @return string The package type + */ + public function getType(); + + /** + * Returns the package targetDir property + * + * @return string The package targetDir + */ + public function getTargetDir(); + + /** + * Returns the package extra data + * + * @return array The package extra data + */ + public function getExtra(); + + /** + * Sets source from which this package was installed (source/dist). + * + * @param string $type source/dist + */ + public function setInstallationSource($type); + + /** + * Returns source from which this package was installed (source/dist). + * + * @param string $type source/dist + */ + public function getInstallationSource(); + + /** + * Returns the repository type of this package, e.g. git, svn + * + * @return string The repository type + */ + public function getSourceType(); + + /** + * Returns the repository url of this package, e.g. git://github.com/naderman/composer.git + * + * @return string The repository url + */ + public function getSourceUrl(); + + /** + * Returns the repository reference of this package, e.g. master, 1.0.0 or a commit hash for git + * + * @return string The repository reference + */ + public function getSourceReference(); + + /** + * Returns the type of the distribution archive of this version, e.g. zip, tarball + * + * @return string The repository type + */ + public function getDistType(); + + /** + * Returns the url of the distribution archive of this version + * + * @return string + */ + public function getDistUrl(); + + /** + * Returns the reference of the distribution archive of this version, e.g. master, 1.0.0 or a commit hash for git + * + * @return string + */ + public function getDistReference(); + + /** + * Returns the sha1 checksum for the distribution archive of this version + * + * @return string + */ + public function getDistSha1Checksum(); + + /** + * Returns the version of this package + * + * @return string version + */ + public function getVersion(); + + /** + * Returns the pretty (i.e. non-normalized) version string of this package + * + * @return string version + */ + public function getPrettyVersion(); + + /** + * Returns the stability of this package: one of (dev, alpha, beta, RC, stable) + * + * @return string + */ + public function getStability(); + + /** + * Returns a set of links to packages which need to be installed before + * this package can be installed + * + * @return array An array of package links defining required packages + */ + public function getRequires(); + + /** + * Returns a set of links to packages which must not be installed at the + * same time as this package + * + * @return array An array of package links defining conflicting packages + */ + public function getConflicts(); + + /** + * Returns a set of links to virtual packages that are provided through + * this package + * + * @return array An array of package links defining provided packages + */ + public function getProvides(); + + /** + * Returns a set of links to packages which can alternatively be + * satisfied by installing this package + * + * @return array An array of package links defining replaced packages + */ + public function getReplaces(); + + /** + * Returns a set of links to packages which are required to develop + * this package. These are installed if in dev mode. + * + * @return array An array of package links defining packages required for development + */ + public function getDevRequires(); + + /** + * Returns a set of package names and reasons why they are useful in + * combination with this package. + * + * @return array An array of package suggestions with descriptions + */ + public function getSuggests(); + + /** + * Returns an associative array of autoloading rules + * + * {"": {""}} + * + * Type is either "psr-0" or "pear". Namespaces are mapped to directories + * for autoloading using the type specified. + * + * @return array Mapping of autoloading rules + */ + public function getAutoload(); + + /** + * Returns a list of directories which should get added to PHP's + * include path. + * + * @return array + */ + public function getIncludePaths(); + + /** + * Stores a reference to the repository that owns the package + * + * @param RepositoryInterface $repository + */ + public function setRepository(RepositoryInterface $repository); + + /** + * Returns a reference to the repository that owns the package + * + * @return RepositoryInterface + */ + public function getRepository(); + + /** + * Returns the package binaries + * + * @return array + */ + public function getBinaries(); + + /** + * Returns a version this package should be aliased to + * + * @return string + */ + public function getAlias(); + + /** + * Returns a non-normalized version this package should be aliased to + * + * @return string + */ + public function getPrettyAlias(); + + /** + * Returns package unique name, constructed from name and version. + * + * @return string + */ + public function getUniqueName(); + + /** + * Converts the package into a readable and unique string + * + * @return string + */ + public function __toString(); + + /** + * Converts the package into a pretty readable string + * + * @return string + */ + public function getPrettyString(); +} diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index de7a14fd0..19a7fb9cd 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -16,149 +16,12 @@ use Composer\Package\LinkConstraint\LinkConstraintInterface; use Composer\Repository\RepositoryInterface; /** + * Defines package metadata that is not necessarily needed for solving and installing packages + * * @author Nils Adermann */ -interface PackageInterface +interface PackageInterface extends CorePackageInterface { - /** - * Returns the package's name without version info, thus not a unique identifier - * - * @return string package name - */ - public function getName(); - - /** - * Returns the package's pretty (i.e. with proper case) name - * - * @return string package name - */ - public function getPrettyName(); - - /** - * Returns a set of names that could refer to this package - * - * No version or release type information should be included in any of the - * names. Provided or replaced package names need to be returned as well. - * - * @return array An array of strings referring to this package - */ - public function getNames(); - - /** - * Allows the solver to set an id for this package to refer to it. - * - * @param int $id - */ - public function setId($id); - - /** - * Retrieves the package's id set through setId - * - * @return int The previously set package id - */ - public function getId(); - - /** - * Checks if the package matches the given constraint directly or through - * provided or replaced packages - * - * @param string $name Name of the package to be matched - * @param LinkConstraintInterface $constraint The constraint to verify - * @return bool Whether this package matches the name and constraint - */ - public function matches($name, LinkConstraintInterface $constraint); - - /** - * Returns whether the package is a development virtual package or a concrete one - * - * @return bool - */ - public function isDev(); - - /** - * Returns the package type, e.g. library - * - * @return string The package type - */ - public function getType(); - - /** - * Returns the package targetDir property - * - * @return string The package targetDir - */ - public function getTargetDir(); - - /** - * Returns the package extra data - * - * @return array The package extra data - */ - public function getExtra(); - - /** - * Sets source from which this package was installed (source/dist). - * - * @param string $type source/dist - */ - public function setInstallationSource($type); - - /** - * Returns source from which this package was installed (source/dist). - * - * @param string $type source/dist - */ - public function getInstallationSource(); - - /** - * Returns the repository type of this package, e.g. git, svn - * - * @return string The repository type - */ - public function getSourceType(); - - /** - * Returns the repository url of this package, e.g. git://github.com/naderman/composer.git - * - * @return string The repository url - */ - public function getSourceUrl(); - - /** - * Returns the repository reference of this package, e.g. master, 1.0.0 or a commit hash for git - * - * @return string The repository reference - */ - public function getSourceReference(); - - /** - * Returns the type of the distribution archive of this version, e.g. zip, tarball - * - * @return string The repository type - */ - public function getDistType(); - - /** - * Returns the url of the distribution archive of this version - * - * @return string - */ - public function getDistUrl(); - - /** - * Returns the reference of the distribution archive of this version, e.g. master, 1.0.0 or a commit hash for git - * - * @return string - */ - public function getDistReference(); - - /** - * Returns the sha1 checksum for the distribution archive of this version - * - * @return string - */ - public function getDistSha1Checksum(); - /** * Returns the scripts of this package * @@ -166,102 +29,6 @@ interface PackageInterface */ public function getScripts(); - /** - * Returns the version of this package - * - * @return string version - */ - public function getVersion(); - - /** - * Returns the pretty (i.e. non-normalized) version string of this package - * - * @return string version - */ - public function getPrettyVersion(); - - /** - * Returns the stability of this package: one of (dev, alpha, beta, RC, stable) - * - * @return string - */ - public function getStability(); - - /** - * Returns the package license, e.g. MIT, BSD, GPL - * - * @return array The package licenses - */ - public function getLicense(); - - /** - * Returns a set of links to packages which need to be installed before - * this package can be installed - * - * @return array An array of package links defining required packages - */ - public function getRequires(); - - /** - * Returns a set of links to packages which must not be installed at the - * same time as this package - * - * @return array An array of package links defining conflicting packages - */ - public function getConflicts(); - - /** - * Returns a set of links to virtual packages that are provided through - * this package - * - * @return array An array of package links defining provided packages - */ - public function getProvides(); - - /** - * Returns a set of links to packages which can alternatively be - * satisfied by installing this package - * - * @return array An array of package links defining replaced packages - */ - public function getReplaces(); - - /** - * Returns a set of links to packages which are required to develop - * this package. These are installed if in dev mode. - * - * @return array An array of package links defining packages required for development - */ - public function getDevRequires(); - - /** - * Returns a set of package names and reasons why they are useful in - * combination with this package. - * - * @return array An array of package suggestions with descriptions - */ - public function getSuggests(); - - /** - * Returns an associative array of autoloading rules - * - * {"": {""}} - * - * Type is either "psr-0" or "pear". Namespaces are mapped to directories - * for autoloading using the type specified. - * - * @return array Mapping of autoloading rules - */ - public function getAutoload(); - - /** - * Returns a list of directories which should get added to PHP's - * include path. - * - * @return array - */ - public function getIncludePaths(); - /** * Returns an array of repositories * @@ -272,18 +39,11 @@ interface PackageInterface public function getRepositories(); /** - * Stores a reference to the repository that owns the package + * Returns the package license, e.g. MIT, BSD, GPL * - * @param RepositoryInterface $repository + * @return array The package licenses */ - public function setRepository(RepositoryInterface $repository); - - /** - * Returns a reference to the repository that owns the package - * - * @return RepositoryInterface - */ - public function getRepository(); + public function getLicense(); /** * Returns the release date of the package @@ -306,13 +66,6 @@ interface PackageInterface */ public function getDescription(); - /** - * Returns the package binaries - * - * @return array - */ - public function getBinaries(); - /** * Returns the package homepage * @@ -329,41 +82,6 @@ interface PackageInterface */ public function getAuthors(); - /** - * Returns a version this package should be aliased to - * - * @return string - */ - public function getAlias(); - - /** - * Returns a non-normalized version this package should be aliased to - * - * @return string - */ - public function getPrettyAlias(); - - /** - * Returns package unique name, constructed from name and version. - * - * @return string - */ - public function getUniqueName(); - - /** - * Converts the package into a readable and unique string - * - * @return string - */ - public function __toString(); - - /** - * Converts the package into a pretty readable string - * - * @return string - */ - public function getPrettyString(); - /** * Returns the support information * diff --git a/src/Composer/Package/RootPackageInterface.php b/src/Composer/Package/RootPackageInterface.php new file mode 100644 index 000000000..0cee7e7f6 --- /dev/null +++ b/src/Composer/Package/RootPackageInterface.php @@ -0,0 +1,49 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package; + +use Composer\Package\LinkConstraint\LinkConstraintInterface; +use Composer\Repository\RepositoryInterface; + +/** + * Defines additional fields that are only needed for the root package + * + * @author Jordi Boggiano + */ +interface RootPackageInterface extends PackageInterface +{ + /** + * Returns the minimum stability of the package + * + * @return string + */ + public function getMinimumStability(); + + /** + * Returns the stability flags to apply to dependencies + * + * array('foo/bar' => 'dev') + * + * @return array + */ + public function getStabilityFlags(); + + /** + * Returns a set of package names and source references that must be enforced on them + * + * array('foo/bar' => 'abcd1234') + * + * @return array + */ + public function getReferences(); +} diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 2c4849243..5b74bc8af 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -14,6 +14,7 @@ namespace Composer\Repository; use Composer\Package\Loader\ArrayLoader; use Composer\Package\PackageInterface; +use Composer\Package\Version\VersionParser; use Composer\Json\JsonFile; use Composer\Cache; use Composer\Config; @@ -23,14 +24,15 @@ use Composer\Util\RemoteFilesystem; /** * @author Jordi Boggiano */ -class ComposerRepository extends ArrayRepository implements NotifiableRepositoryInterface +class ComposerRepository extends ArrayRepository implements NotifiableRepositoryInterface, StreamableRepositoryInterface { protected $config; protected $url; protected $io; - protected $packages; protected $cache; protected $notifyUrl; + protected $minimalPackages; + protected $loader; public function __construct(array $repoConfig, IOInterface $io, Config $config) { @@ -47,6 +49,7 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository $this->url = $repoConfig['url']; $this->io = $io; $this->cache = new Cache($io, $config->get('home').'/cache/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url)); + $this->loader = new ArrayLoader(); } /** @@ -78,10 +81,60 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository @file_get_contents($url, false, $context); } + public function getMinimalPackages() + { + if (isset($this->minimalPackages)) { + return $this->minimalPackages; + } + + $repoData = $this->loadDataFromServer(); + + $this->minimalPackages = array(); + $versionParser = new VersionParser; + + foreach ($repoData as $package) { + $version = !empty($package['version_normalized']) ? $package['version_normalized'] : $versionParser->normalize($package['version']); + $data = array( + 'name' => strtolower($package['name']), + 'repo' => $this, + 'version' => $version, + 'raw' => $package, + ); + if (!empty($package['replace'])) { + $data['replace'] = $package['replace']; + } + if (!empty($package['provide'])) { + $data['provide'] = $package['provide']; + } + + $this->minimalPackages[] = $data; + } + + return $this->minimalPackages; + } + + public function loadPackage(array $data, $id) + { + $package = $this->loader->load($data); + $package->setId($id); + $package->setRepository($data['repo']); + + return $package; + } + protected function initialize() { parent::initialize(); + $repoData = $this->loadDataFromServer(); + + foreach ($repoData as $package) { + $this->addPackage($this->loader->load($package)); + } + } + + protected function loadDataFromServer() + { if (!extension_loaded('openssl') && 'https' === substr($this->url, 0, 5)) { throw new \RuntimeException('You must enable the openssl extension in your php.ini to load information from '.$this->url); } @@ -109,17 +162,18 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository } } - $loader = new ArrayLoader(); - $this->loadRepository($loader, $data); + return $this->loadIncludes($data); } - protected function loadRepository(ArrayLoader $loader, $data) + protected function loadIncludes($data) { + $packages = array(); + // legacy repo handling if (!isset($data['packages']) && !isset($data['includes'])) { foreach ($data as $pkg) { foreach ($pkg['versions'] as $metadata) { - $this->addPackage($loader->load($metadata)); + $packages[] = $metadata; } } @@ -129,7 +183,7 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository if (isset($data['packages'])) { foreach ($data['packages'] as $package => $versions) { foreach ($versions as $version => $metadata) { - $this->addPackage($loader->load($metadata)); + $packages[] = $metadata; } } } @@ -143,8 +197,10 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository $includedData = $json->read(); $this->cache->write($include, json_encode($includedData)); } - $this->loadRepository($loader, $includedData); + $packages = array_merge($packages, $this->loadIncludes($includedData)); } } + + return $packages; } } diff --git a/src/Composer/Repository/StreamableRepositoryInterface.php b/src/Composer/Repository/StreamableRepositoryInterface.php new file mode 100644 index 000000000..e2b95cd78 --- /dev/null +++ b/src/Composer/Repository/StreamableRepositoryInterface.php @@ -0,0 +1,30 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\Package\PackageInterface; + +/** + * @author Jordi Boggiano + */ +interface StreamableRepositoryInterface extends RepositoryInterface +{ + /** + * Return partial package data without loading them all to save on memory + * + * @return array + */ + public function getMinimalPackages(); + + public function loadPackage(array $data, $id); +} From 2d4076e9b29887329743a6bfec16bc5841c02d8e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Aug 2012 14:20:43 +0200 Subject: [PATCH 02/20] Add support for aliases in streamed repos --- src/Composer/DependencyResolver/Pool.php | 67 ++++++++++++++++++- src/Composer/Installer.php | 21 +++--- src/Composer/Package/Locker.php | 13 +++- .../Repository/ComposerRepository.php | 30 ++++++++- 4 files changed, 113 insertions(+), 18 deletions(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index b88666dda..c6f328c5e 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -13,6 +13,7 @@ namespace Composer\DependencyResolver; use Composer\Package\BasePackage; +use Composer\Package\AliasPackage; use Composer\Package\Version\VersionParser; use Composer\Package\Loader\ArrayLoader; use Composer\Package\Link; @@ -64,8 +65,9 @@ class Pool * Adds a repository and its packages to this package pool * * @param RepositoryInterface $repo A package repository + * @param array $aliases */ - public function addRepository(RepositoryInterface $repo) + public function addRepository(RepositoryInterface $repo, $aliases = array()) { if ($repo instanceof CompositeRepository) { $repos = $repo->getRepositories(); @@ -81,7 +83,8 @@ class Pool if ($repo instanceof StreamableRepositoryInterface) { foreach ($repo->getMinimalPackages() as $package) { $name = $package['name']; - $stability = VersionParser::parseStability($package['version']); + $version = $package['version']; + $stability = VersionParser::parseStability($version); if ( // always allow exempt repos $exempt @@ -114,6 +117,35 @@ class Pool foreach (array_keys($names) as $name) { $this->packageByName[$name][] =& $this->packages[$id-2]; } + + // handle root package aliases + if (isset($aliases[$name][$version])) { + $alias = $package; + $alias['version'] = $aliases[$name][$version]['alias_normalized']; + $alias['alias'] = $aliases[$name][$version]['alias']; + $alias['alias_of'] = $package['id']; + $alias['id'] = $id++; + $alias['root_alias'] = true; + $this->packages[] = $alias; + + foreach (array_keys($names) as $name) { + $this->packageByName[$name][] =& $this->packages[$id-2]; + } + } + + // handle normal package aliases + if (isset($package['alias'])) { + $alias = $package; + $alias['version'] = $package['alias_normalized']; + $alias['alias'] = $package['alias']; + $alias['alias_of'] = $package['id']; + $alias['id'] = $id++; + $this->packages[] = $alias; + + foreach (array_keys($names) as $name) { + $this->packageByName[$name][] =& $this->packages[$id-2]; + } + } } } } else { @@ -137,6 +169,22 @@ class Pool foreach ($package->getNames() as $name) { $this->packageByName[$name][] = $package; } + + // handle root package aliases + if (isset($aliases[$name][$package->getVersion()])) { + $alias = $aliases[$name][$package->getVersion()]; + $package->setAlias($alias['alias_normalized']); + $package->setPrettyAlias($alias['alias']); + $package->getRepository()->addPackage($aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); + $aliasPackage->setRootPackageAlias(true); + $aliasPackage->setId($id++); + + $this->packages[] = $aliasPackage; + + foreach ($aliasPackage->getNames() as $name) { + $this->packageByName[$name][] = $aliasPackage; + } + } } } } @@ -267,7 +315,20 @@ class Pool private function ensurePackageIsLoaded($data) { if (is_array($data)) { - $data = $this->packages[$data['id'] - 1] = $data['repo']->loadPackage($data, $data['id']); + if (isset($data['alias_of'])) { + // TODO move to $repo->loadAliasPackage? + $aliasOf = $this->packageById($data['alias_of']); + $rootAlias = !empty($data['root_alias']); + $package = $this->packages[$data['id'] - 1] = new AliasPackage($aliasOf, $data['version'], $data['alias']); + $package->setId($data['id']); + $package->setRootPackageAlias($rootAlias); + + return $package; + } + + $package = $this->packages[$data['id'] - 1] = $data['repo']->loadPackage($data, $data['id']); + + return $package; } return $data; diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 6d70126fb..09f8a77f4 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -244,9 +244,9 @@ class Installer // creating repository pool $pool = new Pool($minimumStability, $stabilityFlags); - $pool->addRepository($installedRepo); + $pool->addRepository($installedRepo, $aliases); foreach ($this->repositoryManager->getRepositories() as $repository) { - $pool->addRepository($repository); + $pool->addRepository($repository, $aliases); } // creating requirements request @@ -276,11 +276,8 @@ class Installer foreach ($lockedPackages as $package) { $version = $package->getVersion(); - foreach ($aliases as $alias) { - if ($alias['package'] === $package->getName() && $alias['version'] === $package->getVersion()) { - $version = $alias['alias_normalized']; - break; - } + if (isset($aliases[$package->getName()][$version])) { + $version = $aliases[$package->getName()][$version]['alias_normalized']; } $constraint = new VersionConstraint('=', $version); $request->install($package->getName(), $constraint); @@ -495,11 +492,11 @@ class Installer $aliases = $this->package->getAliases(); } + $normalizedAliases = array(); + foreach ($aliases as $alias) { - $packages = array_merge( - $platformRepo->findPackages($alias['package'], $alias['version']), - $this->repositoryManager->findPackages($alias['package'], $alias['version']) - ); + $normalizedAliases[$alias['package']][$alias['version']] = array('alias' => $alias['alias'], 'alias_normalized' => $alias['alias_normalized']); + $packages = $platformRepo->findPackages($alias['package'], $alias['version']); foreach ($packages as $package) { $package->setAlias($alias['alias_normalized']); $package->setPrettyAlias($alias['alias']); @@ -508,7 +505,7 @@ class Installer } } - return $aliases; + return $normalizedAliases; } private function isUpdateable(PackageInterface $package) diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index ee4558e23..9b3d2cd25 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -177,11 +177,22 @@ class Locker 'hash' => $this->hash, 'packages' => null, 'packages-dev' => null, - 'aliases' => $aliases, + 'aliases' => array(), 'minimum-stability' => $minimumStability, 'stability-flags' => $stabilityFlags, ); + foreach ($aliases as $package => $versions) { + foreach ($versions as $version => $alias) { + $lock['aliases'][] = array( + 'alias' => $alias['alias'], + 'alias_normalized' => $alias['alias_normalized'], + 'version' => $version, + 'package' => $package, + ); + } + } + $lock['packages'] = $this->lockPackages($packages); if (null !== $devPackages) { $lock['packages-dev'] = $this->lockPackages($devPackages); diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 5b74bc8af..816ed4463 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -107,6 +107,32 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository $data['provide'] = $package['provide']; } + // add branch aliases + if ('dev-' === substr($package['version'], 0, 4) && isset($package['extra']['branch-alias']) && is_array($package['extra']['branch-alias'])) { + foreach ($package['extra']['branch-alias'] as $sourceBranch => $targetBranch) { + // ensure it is an alias to a -dev package + if ('-dev' !== substr($targetBranch, -4)) { + continue; + } + // normalize without -dev and ensure it's a numeric branch that is parseable + $validatedTargetBranch = $versionParser->normalizeBranch(substr($targetBranch, 0, -4)); + if ('-dev' !== substr($validatedTargetBranch, -4)) { + continue; + } + + // ensure that it is the current branch aliasing itself + if (strtolower($package['version']) !== strtolower($sourceBranch)) { + continue; + } + + $alias = preg_replace('{(\.9{7})+}', '.x', $validatedTargetBranch); + $aliasNormalized = $validatedTargetBranch; + $data['alias'] = $alias; + $data['alias_normalized'] = $aliasNormalized; + break; + } + } + $this->minimalPackages[] = $data; } @@ -115,9 +141,9 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository public function loadPackage(array $data, $id) { - $package = $this->loader->load($data); + $package = $this->loader->load($data['raw']); $package->setId($id); - $package->setRepository($data['repo']); + $package->setRepository($this); return $package; } From 25142c5351d8d6bf11c7e7f2270e1dd8fffc12e7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Aug 2012 15:38:46 +0200 Subject: [PATCH 03/20] Clarify variable variable usage --- src/Composer/DependencyResolver/Pool.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index c6f328c5e..e6452458d 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -349,8 +349,8 @@ class Pool $candidateName = $candidate['name']; $candidateVersion = $candidate['version']; foreach (array('provides', 'replaces') as $linkType) { - $$linkType = isset($candidate[rtrim($linkType, 's')]) ? $candidate[rtrim($linkType, 's')] : array(); - foreach ($$linkType as $target => $constraintDef) { + ${$linkType} = isset($candidate[rtrim($linkType, 's')]) ? $candidate[rtrim($linkType, 's')] : array(); + foreach (${$linkType} as $target => $constraintDef) { if ('self.version' === $constraintDef) { $parsedConstraint = $this->versionParser->parseConstraints($candidateVersion); } else { From 774021bf9971dcf7a92ddac2a8996dabc8ef62a4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Aug 2012 15:39:16 +0200 Subject: [PATCH 04/20] Clean up Installer::aliasPackages() --- src/Composer/Installer.php | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 09f8a77f4..e2c6f8113 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -166,7 +166,8 @@ class Installer $installedRepo->addRepository($this->additionalInstalledRepository); } - $aliases = $this->aliasPackages($platformRepo); + $aliases = $this->getRootAliases(); + $this->aliasPlatformPackages($platformRepo, $aliases); if ($this->runScripts) { // dispatch pre event @@ -484,7 +485,7 @@ class Installer return true; } - private function aliasPackages(PlatformRepository $platformRepo) + private function getRootAliases() { if (!$this->update && $this->locker->isLocked()) { $aliases = $this->locker->getAliases(); @@ -495,19 +496,31 @@ class Installer $normalizedAliases = array(); foreach ($aliases as $alias) { - $normalizedAliases[$alias['package']][$alias['version']] = array('alias' => $alias['alias'], 'alias_normalized' => $alias['alias_normalized']); - $packages = $platformRepo->findPackages($alias['package'], $alias['version']); - foreach ($packages as $package) { - $package->setAlias($alias['alias_normalized']); - $package->setPrettyAlias($alias['alias']); - $package->getRepository()->addPackage($aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); - $aliasPackage->setRootPackageAlias(true); - } + $normalizedAliases[$alias['package']][$alias['version']] = array( + 'alias' => $alias['alias'], + 'alias_normalized' => $alias['alias_normalized'] + ); } return $normalizedAliases; } + private function aliasPlatformPackages(PlatformRepository $platformRepo, $aliases) + { + foreach ($aliases as $package => $versions) { + foreach ($versions as $version => $alias) { + $packages = $platformRepo->findPackages($package, $version); + foreach ($packages as $package) { + $package->setAlias($alias['alias_normalized']); + $package->setPrettyAlias($alias['alias']); + $aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']); + $aliasPackage->setRootPackageAlias(true); + $platformRepo->addPackage($aliasPackage); + } + } + } + } + private function isUpdateable(PackageInterface $package) { if (!$this->updateWhitelist) { From 26e8217db760e31b172ace6e2a4a4f6b13c5e1c6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Aug 2012 16:35:22 +0200 Subject: [PATCH 05/20] Remove duplication of branch alias parsing code --- src/Composer/Package/Loader/ArrayLoader.php | 61 ++++++++++++------- .../Repository/ComposerRepository.php | 26 +------- 2 files changed, 42 insertions(+), 45 deletions(-) diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 10ba2b3ed..46effd05c 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -134,28 +134,9 @@ class ArrayLoader implements LoaderInterface $package->setDistSha1Checksum(isset($config['dist']['shasum']) ? $config['dist']['shasum'] : null); } - // check for a branch alias (dev-master => 1.0.x-dev for example) if this is a named branch - if ('dev-' === substr($package->getPrettyVersion(), 0, 4) && isset($config['extra']['branch-alias']) && is_array($config['extra']['branch-alias'])) { - foreach ($config['extra']['branch-alias'] as $sourceBranch => $targetBranch) { - // ensure it is an alias to a -dev package - if ('-dev' !== substr($targetBranch, -4)) { - continue; - } - // normalize without -dev and ensure it's a numeric branch that is parseable - $validatedTargetBranch = $this->versionParser->normalizeBranch(substr($targetBranch, 0, -4)); - if ('-dev' !== substr($validatedTargetBranch, -4)) { - continue; - } - - // ensure that it is the current branch aliasing itself - if (strtolower($package->getPrettyVersion()) !== strtolower($sourceBranch)) { - continue; - } - - $package->setAlias($validatedTargetBranch); - $package->setPrettyAlias(preg_replace('{(\.9{7})+}', '.x', $validatedTargetBranch)); - break; - } + if ($aliasNormalized = $this->getBranchAlias($config)) { + $package->setAlias($aliasNormalized); + $package->setPrettyAlias(preg_replace('{(\.9{7})+}', '.x', $aliasNormalized)); } foreach (Package\BasePackage::$supportedLinkTypes as $type => $opts) { @@ -191,6 +172,42 @@ class ArrayLoader implements LoaderInterface return $package; } + /** + * Retrieves a branch alias (dev-master => 1.0.x-dev for example) if it exists + * + * @param array $config the entire package config + * @return string|null normalized version of the branch alias or null if there is none + */ + public function getBranchAlias(array $config) + { + if ('dev-' !== substr($config['version'], 0, 4) + || !isset($config['extra']['branch-alias']) + || !is_array($config['extra']['branch-alias']) + ) { + return; + } + + foreach ($config['extra']['branch-alias'] as $sourceBranch => $targetBranch) { + // ensure it is an alias to a -dev package + if ('-dev' !== substr($targetBranch, -4)) { + continue; + } + + // normalize without -dev and ensure it's a numeric branch that is parseable + $validatedTargetBranch = $this->versionParser->normalizeBranch(substr($targetBranch, 0, -4)); + if ('-dev' !== substr($validatedTargetBranch, -4)) { + continue; + } + + // ensure that it is the current branch aliasing itself + if (strtolower($config['version']) !== strtolower($sourceBranch)) { + continue; + } + + return $validatedTargetBranch; + } + } + private function loadLinksFromConfig($package, $description, array $linksSpecs) { $links = array(); diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 816ed4463..3a3b05aeb 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -108,29 +108,9 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository } // add branch aliases - if ('dev-' === substr($package['version'], 0, 4) && isset($package['extra']['branch-alias']) && is_array($package['extra']['branch-alias'])) { - foreach ($package['extra']['branch-alias'] as $sourceBranch => $targetBranch) { - // ensure it is an alias to a -dev package - if ('-dev' !== substr($targetBranch, -4)) { - continue; - } - // normalize without -dev and ensure it's a numeric branch that is parseable - $validatedTargetBranch = $versionParser->normalizeBranch(substr($targetBranch, 0, -4)); - if ('-dev' !== substr($validatedTargetBranch, -4)) { - continue; - } - - // ensure that it is the current branch aliasing itself - if (strtolower($package['version']) !== strtolower($sourceBranch)) { - continue; - } - - $alias = preg_replace('{(\.9{7})+}', '.x', $validatedTargetBranch); - $aliasNormalized = $validatedTargetBranch; - $data['alias'] = $alias; - $data['alias_normalized'] = $aliasNormalized; - break; - } + if ($aliasNormalized = $this->loader->getBranchAlias($package)) { + $data['alias'] = preg_replace('{(\.9{7})+}', '.x', $aliasNormalized); + $data['alias_normalized'] = $aliasNormalized; } $this->minimalPackages[] = $data; From 62ead24b61245595d946b4be6cd07e859d686f72 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Aug 2012 17:45:16 +0200 Subject: [PATCH 06/20] Rename variable for clarity --- src/Composer/DependencyResolver/Pool.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index e6452458d..2ec7ea6de 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -65,9 +65,9 @@ class Pool * Adds a repository and its packages to this package pool * * @param RepositoryInterface $repo A package repository - * @param array $aliases + * @param array $rootAliases */ - public function addRepository(RepositoryInterface $repo, $aliases = array()) + public function addRepository(RepositoryInterface $repo, $rootAliases = array()) { if ($repo instanceof CompositeRepository) { $repos = $repo->getRepositories(); @@ -119,10 +119,11 @@ class Pool } // handle root package aliases - if (isset($aliases[$name][$version])) { + if (isset($rootAliases[$name][$version])) { $alias = $package; - $alias['version'] = $aliases[$name][$version]['alias_normalized']; - $alias['alias'] = $aliases[$name][$version]['alias']; + unset($alias['raw']); + $alias['version'] = $rootAliases[$name][$version]['alias_normalized']; + $alias['alias'] = $rootAliases[$name][$version]['alias']; $alias['alias_of'] = $package['id']; $alias['id'] = $id++; $alias['root_alias'] = true; @@ -136,6 +137,7 @@ class Pool // handle normal package aliases if (isset($package['alias'])) { $alias = $package; + unset($alias['raw']); $alias['version'] = $package['alias_normalized']; $alias['alias'] = $package['alias']; $alias['alias_of'] = $package['id']; @@ -171,8 +173,8 @@ class Pool } // handle root package aliases - if (isset($aliases[$name][$package->getVersion()])) { - $alias = $aliases[$name][$package->getVersion()]; + if (isset($rootAliases[$name][$package->getVersion()])) { + $alias = $rootAliases[$name][$package->getVersion()]; $package->setAlias($alias['alias_normalized']); $package->setPrettyAlias($alias['alias']); $package->getRepository()->addPackage($aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); From 9a1fd450b0a3acc7f56c10752e82ba3ee832358a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Aug 2012 17:46:04 +0200 Subject: [PATCH 07/20] Remove code duplication --- src/Composer/DependencyResolver/Pool.php | 39 +++++++++++------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 2ec7ea6de..11e7184a7 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -85,17 +85,7 @@ class Pool $name = $package['name']; $version = $package['version']; $stability = VersionParser::parseStability($version); - if ( - // always allow exempt repos - $exempt - // allow if package matches the global stability requirement and has no exception - || (!isset($this->stabilityFlags[$name]) - && isset($this->acceptableStabilities[$stability])) - // allow if package matches the package-specific stability flag - || (isset($this->stabilityFlags[$name]) - && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$name] - ) - ) { + if ($exempt || $this->isPackageAcceptable($name, $stability)) { $package['id'] = $id++; $this->packages[] = $package; @@ -154,17 +144,7 @@ class Pool foreach ($repo->getPackages() as $package) { $name = $package->getName(); $stability = $package->getStability(); - if ( - // always allow exempt repos - $exempt - // allow if package matches the global stability requirement and has no exception - || (!isset($this->stabilityFlags[$name]) - && isset($this->acceptableStabilities[$stability])) - // allow if package matches the package-specific stability flag - || (isset($this->stabilityFlags[$name]) - && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$name] - ) - ) { + if ($exempt || $this->isPackageAcceptable($name, $stability)) { $package->setId($id++); $this->packages[] = $package; @@ -314,6 +294,21 @@ class Pool return $prefix.' '.$package->getPrettyString(); } + private function isPackageAcceptable($name, $stability) + { + // allow if package matches the global stability requirement and has no exception + if (!isset($this->stabilityFlags[$name]) && isset($this->acceptableStabilities[$stability])) { + return true; + } + + // allow if package matches the package-specific stability flag + if (isset($this->stabilityFlags[$name]) && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$name]) { + return true; + } + + return false; + } + private function ensurePackageIsLoaded($data) { if (is_array($data)) { From e46d26cb9b6f77d4a6d7e0ae8ed33a529e753616 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Aug 2012 17:47:05 +0200 Subject: [PATCH 08/20] Add loadAliasPackage to the StreamableRepositoryInterface and clear up responsibilities between Pool and Repositories --- src/Composer/DependencyResolver/Pool.php | 21 ++++++--------- .../Repository/ComposerRepository.php | 23 ++++++++++++++-- .../StreamableRepositoryInterface.php | 27 ++++++++++++++++++- 3 files changed, 55 insertions(+), 16 deletions(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 11e7184a7..f14607452 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -44,13 +44,11 @@ class Pool protected $packageByName = array(); protected $acceptableStabilities; protected $stabilityFlags; - protected $loader; protected $versionParser; public function __construct($minimumStability = 'stable', array $stabilityFlags = array()) { $stabilities = BasePackage::$stabilities; - $this->loader = new ArrayLoader; $this->versionParser = new VersionParser; $this->acceptableStabilities = array(); foreach (BasePackage::$stabilities as $stability => $value) { @@ -313,19 +311,15 @@ class Pool { if (is_array($data)) { if (isset($data['alias_of'])) { - // TODO move to $repo->loadAliasPackage? $aliasOf = $this->packageById($data['alias_of']); - $rootAlias = !empty($data['root_alias']); - $package = $this->packages[$data['id'] - 1] = new AliasPackage($aliasOf, $data['version'], $data['alias']); - $package->setId($data['id']); - $package->setRootPackageAlias($rootAlias); - - return $package; + $package = $this->packages[$data['id'] - 1] = $data['repo']->loadAliasPackage($data, $aliasOf); + $package->setRootPackageAlias(!empty($data['root_alias'])); + } else { + $package = $this->packages[$data['id'] - 1] = $data['repo']->loadPackage($data); } - $package = $this->packages[$data['id'] - 1] = $data['repo']->loadPackage($data, $data['id']); - - return $package; + $package->setId($data['id']); + $data = $package; } return $data; @@ -342,6 +336,7 @@ class Pool */ private function match($candidate, $name, LinkConstraintInterface $constraint) { + // handle array packages if (is_array($candidate)) { $candidateName = $candidate['name']; $candidateVersion = $candidate['version']; @@ -357,6 +352,7 @@ class Pool } } } else { + // handle object packages $candidateName = $candidate->getName(); $candidateVersion = $candidate->getVersion(); $provides = $candidate->getProvides(); @@ -381,5 +377,4 @@ class Pool return self::MATCH_NONE; } - } diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 3a3b05aeb..b8937bb33 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -81,6 +81,9 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository @file_get_contents($url, false, $context); } + /** + * {@inheritDoc} + */ public function getMinimalPackages() { if (isset($this->minimalPackages)) { @@ -119,15 +122,31 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository return $this->minimalPackages; } - public function loadPackage(array $data, $id) + /** + * {@inheritDoc} + */ + public function loadPackage(array $data) { $package = $this->loader->load($data['raw']); - $package->setId($id); $package->setRepository($this); return $package; } + /** + * {@inheritDoc} + */ + public function loadAliasPackage(array $data, PackageInterface $aliasOf) + { + $aliasPackage = $this->createAliasPackage($aliasOf, $data['version'], $data['alias']); + $aliasPackage->setRepository($this); + + return $aliasPackage; + } + + /** + * {@inheritDoc} + */ protected function initialize() { parent::initialize(); diff --git a/src/Composer/Repository/StreamableRepositoryInterface.php b/src/Composer/Repository/StreamableRepositoryInterface.php index e2b95cd78..7a1d2c6ce 100644 --- a/src/Composer/Repository/StreamableRepositoryInterface.php +++ b/src/Composer/Repository/StreamableRepositoryInterface.php @@ -12,6 +12,7 @@ namespace Composer\Repository; +use Composer\Package\AliasPackage; use Composer\Package\PackageInterface; /** @@ -22,9 +23,33 @@ interface StreamableRepositoryInterface extends RepositoryInterface /** * Return partial package data without loading them all to save on memory * + * The package array must contain the following fields: + * - name: package name (normalized/lowercased) + * - repo: reference to the repository instance + * - version: normalized version + * - replace: array of package name => version constraint, optional + * - provide: array of package name => version constraint, optional + * - alias: pretty alias that this package should be aliased to, optional + * - alias_normalized: normalized alias that this package should be aliased to, optional + * * @return array */ public function getMinimalPackages(); - public function loadPackage(array $data, $id); + /** + * Loads a package from minimal info of the package + * + * @param array $data the minimal info as was returned by getMinimalPackage + * @return PackageInterface + */ + public function loadPackage(array $data); + + /** + * Loads an alias package from minimal info of the package + * + * @param array $data the minimal info as was returned by getMinimalPackage + * @param PackageInterface $aliasOf the package which this alias is an alias of + * @return AliasPackage + */ + public function loadAliasPackage(array $data, PackageInterface $aliasOf); } From 8a275336a1e65ee3dc9c381518e109ba40c8d15d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Aug 2012 17:50:01 +0200 Subject: [PATCH 09/20] CS cleanups --- src/Composer/DependencyResolver/Pool.php | 3 +-- src/Composer/Package/BasePackage.php | 2 -- src/Composer/Package/CorePackageInterface.php | 1 - src/Composer/Package/Loader/ArrayLoader.php | 2 +- src/Composer/Package/PackageInterface.php | 3 --- src/Composer/Package/RootPackageInterface.php | 3 --- src/Composer/Repository/StreamableRepositoryInterface.php | 6 +++--- 7 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index f14607452..940178ed3 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -15,7 +15,6 @@ namespace Composer\DependencyResolver; use Composer\Package\BasePackage; use Composer\Package\AliasPackage; use Composer\Package\Version\VersionParser; -use Composer\Package\Loader\ArrayLoader; use Composer\Package\Link; use Composer\Package\LinkConstraint\LinkConstraintInterface; use Composer\Package\LinkConstraint\VersionConstraint; @@ -62,7 +61,7 @@ class Pool /** * Adds a repository and its packages to this package pool * - * @param RepositoryInterface $repo A package repository + * @param RepositoryInterface $repo A package repository * @param array $rootAliases */ public function addRepository(RepositoryInterface $repo, $rootAliases = array()) diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index 365ab9bb5..f78a2b122 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -12,8 +12,6 @@ namespace Composer\Package; -use Composer\Package\LinkConstraint\LinkConstraintInterface; -use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Repository\RepositoryInterface; use Composer\Repository\PlatformRepository; diff --git a/src/Composer/Package/CorePackageInterface.php b/src/Composer/Package/CorePackageInterface.php index 3b03cfa2f..b0d98d40f 100644 --- a/src/Composer/Package/CorePackageInterface.php +++ b/src/Composer/Package/CorePackageInterface.php @@ -12,7 +12,6 @@ namespace Composer\Package; -use Composer\Package\LinkConstraint\LinkConstraintInterface; use Composer\Repository\RepositoryInterface; /** diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 46effd05c..07ef88a59 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -175,7 +175,7 @@ class ArrayLoader implements LoaderInterface /** * Retrieves a branch alias (dev-master => 1.0.x-dev for example) if it exists * - * @param array $config the entire package config + * @param array $config the entire package config * @return string|null normalized version of the branch alias or null if there is none */ public function getBranchAlias(array $config) diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index 19a7fb9cd..5d4958c61 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -12,9 +12,6 @@ namespace Composer\Package; -use Composer\Package\LinkConstraint\LinkConstraintInterface; -use Composer\Repository\RepositoryInterface; - /** * Defines package metadata that is not necessarily needed for solving and installing packages * diff --git a/src/Composer/Package/RootPackageInterface.php b/src/Composer/Package/RootPackageInterface.php index 0cee7e7f6..0b8a30f4e 100644 --- a/src/Composer/Package/RootPackageInterface.php +++ b/src/Composer/Package/RootPackageInterface.php @@ -12,9 +12,6 @@ namespace Composer\Package; -use Composer\Package\LinkConstraint\LinkConstraintInterface; -use Composer\Repository\RepositoryInterface; - /** * Defines additional fields that are only needed for the root package * diff --git a/src/Composer/Repository/StreamableRepositoryInterface.php b/src/Composer/Repository/StreamableRepositoryInterface.php index 7a1d2c6ce..36439932c 100644 --- a/src/Composer/Repository/StreamableRepositoryInterface.php +++ b/src/Composer/Repository/StreamableRepositoryInterface.php @@ -39,7 +39,7 @@ interface StreamableRepositoryInterface extends RepositoryInterface /** * Loads a package from minimal info of the package * - * @param array $data the minimal info as was returned by getMinimalPackage + * @param array $data the minimal info as was returned by getMinimalPackage * @return PackageInterface */ public function loadPackage(array $data); @@ -47,8 +47,8 @@ interface StreamableRepositoryInterface extends RepositoryInterface /** * Loads an alias package from minimal info of the package * - * @param array $data the minimal info as was returned by getMinimalPackage - * @param PackageInterface $aliasOf the package which this alias is an alias of + * @param array $data the minimal info as was returned by getMinimalPackage + * @param PackageInterface $aliasOf the package which this alias is an alias of * @return AliasPackage */ public function loadAliasPackage(array $data, PackageInterface $aliasOf); From d6de4a00368b9cd302c39bb7db49beaaad8b001d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Aug 2012 15:52:40 +0200 Subject: [PATCH 10/20] Rename Package interfaces to reduce BC issues --- src/Composer/Command/SearchCommand.php | 6 +- src/Composer/Command/ShowCommand.php | 10 +- src/Composer/Composer.php | 10 +- src/Composer/Installer.php | 7 +- src/Composer/Package/AliasPackage.php | 2 +- src/Composer/Package/CompletePackage.php | 174 ++++++++++ .../Package/CompletePackageInterface.php | 81 +++++ src/Composer/Package/CorePackageInterface.php | 297 ------------------ src/Composer/Package/Dumper/ArrayDumper.php | 39 ++- src/Composer/Package/Loader/ArrayLoader.php | 82 ++--- .../Package/Loader/LoaderInterface.php | 3 +- .../Package/Loader/RootPackageLoader.php | 4 +- .../Package/Loader/ValidatingArrayLoader.php | 4 +- .../{MemoryPackage.php => Package.php} | 225 +------------ src/Composer/Package/PackageInterface.php | 282 +++++++++++++++-- src/Composer/Package/RootPackage.php | 81 +++++ src/Composer/Package/RootPackageInterface.php | 2 +- .../Repository/ComposerRepository.php | 4 +- .../Repository/Pear/ChannelReader.php | 2 +- src/Composer/Repository/PearRepository.php | 8 +- .../Repository/PlatformRepository.php | 8 +- .../Test/Autoload/AutoloadGeneratorTest.php | 58 ++-- tests/Composer/Test/ComposerTest.php | 2 +- .../Test/Installer/LibraryInstallerTest.php | 2 +- .../Installer/MetapackageInstallerTest.php | 2 +- tests/Composer/Test/InstallerTest.php | 8 +- ...ackageTest.php => CompletePackageTest.php} | 16 +- .../Test/Package/Dumper/ArrayDumperTest.php | 4 +- .../Repository/Pear/ChannelReaderTest.php | 4 +- tests/Composer/Test/TestCase.php | 6 +- 30 files changed, 750 insertions(+), 683 deletions(-) create mode 100644 src/Composer/Package/CompletePackage.php create mode 100644 src/Composer/Package/CompletePackageInterface.php delete mode 100644 src/Composer/Package/CorePackageInterface.php rename src/Composer/Package/{MemoryPackage.php => Package.php} (70%) create mode 100644 src/Composer/Package/RootPackage.php rename tests/Composer/Test/Package/{MemoryPackageTest.php => CompletePackageTest.php} (78%) diff --git a/src/Composer/Command/SearchCommand.php b/src/Composer/Command/SearchCommand.php index c09b5d4f1..f212f0621 100644 --- a/src/Composer/Command/SearchCommand.php +++ b/src/Composer/Command/SearchCommand.php @@ -17,7 +17,7 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Output\OutputInterface; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; -use Composer\Package\PackageInterface; +use Composer\Package\CompletePackageInterface; use Composer\Package\AliasPackage; use Composer\Factory; @@ -117,11 +117,11 @@ EOT /** * tries to find a token within the name/keywords/description * - * @param PackageInterface $package + * @param CompletePackageInterface $package * @param string $token * @return boolean */ - private function matchPackage(PackageInterface $package, $token) + private function matchPackage(CompletePackageInterface $package, $token) { $score = 0; diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 7d8632588..a850da552 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -14,7 +14,7 @@ namespace Composer\Command; use Composer\Composer; use Composer\Factory; -use Composer\Package\PackageInterface; +use Composer\Package\CompletePackageInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; @@ -133,7 +133,7 @@ EOT * finds a package by name and version if provided * * @param InputInterface $input - * @return PackageInterface + * @return CompletePackageInterface * @throws \InvalidArgumentException */ protected function getPackage(InputInterface $input, OutputInterface $output, RepositoryInterface $installedRepo, RepositoryInterface $repos) @@ -164,7 +164,7 @@ EOT /** * prints package meta data */ - protected function printMeta(InputInterface $input, OutputInterface $output, PackageInterface $package, RepositoryInterface $installedRepo, RepositoryInterface $repos) + protected function printMeta(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, RepositoryInterface $installedRepo, RepositoryInterface $repos) { $output->writeln('name : ' . $package->getPrettyName()); $output->writeln('descrip. : ' . $package->getDescription()); @@ -206,7 +206,7 @@ EOT /** * prints all available versions of this package and highlights the installed one if any */ - protected function printVersions(InputInterface $input, OutputInterface $output, PackageInterface $package, RepositoryInterface $installedRepo, RepositoryInterface $repos) + protected function printVersions(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, RepositoryInterface $installedRepo, RepositoryInterface $repos) { if ($input->getArgument('version')) { $output->writeln('version : ' . $package->getPrettyVersion()); @@ -237,7 +237,7 @@ EOT * * @param string $linkType */ - protected function printLinks(InputInterface $input, OutputInterface $output, PackageInterface $package, $linkType, $title = null) + protected function printLinks(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, $linkType, $title = null) { $title = $title ?: $linkType; if ($links = $package->{'get'.ucfirst($linkType)}()) { diff --git a/src/Composer/Composer.php b/src/Composer/Composer.php index 5b85e172a..2222f85f9 100644 --- a/src/Composer/Composer.php +++ b/src/Composer/Composer.php @@ -12,7 +12,7 @@ namespace Composer; -use Composer\Package\PackageInterface; +use Composer\Package\RootPackageInterface; use Composer\Package\Locker; use Composer\Repository\RepositoryManager; use Composer\Installer\InstallationManager; @@ -27,7 +27,7 @@ class Composer const VERSION = '@package_version@'; /** - * @var Package\PackageInterface + * @var Package\RootPackageInterface */ private $package; @@ -57,16 +57,16 @@ class Composer private $config; /** - * @param Package\PackageInterface $package + * @param Package\RootPackageInterface $package * @return void */ - public function setPackage(PackageInterface $package) + public function setPackage(RootPackageInterface $package) { $this->package = $package; } /** - * @return Package\PackageInterface + * @return Package\RootPackageInterface */ public function getPackage() { diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index e2c6f8113..282fafb78 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -29,6 +29,7 @@ use Composer\Package\Link; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\Locker; use Composer\Package\PackageInterface; +use Composer\Package\RootPackageInterface; use Composer\Repository\CompositeRepository; use Composer\Repository\InstalledArrayRepository; use Composer\Repository\PlatformRepository; @@ -55,7 +56,7 @@ class Installer protected $config; /** - * @var PackageInterface + * @var RootPackageInterface */ protected $package; @@ -112,7 +113,7 @@ class Installer * * @param IOInterface $io * @param Config $config - * @param PackageInterface $package + * @param RootPackageInterface $package * @param DownloadManager $downloadManager * @param RepositoryManager $repositoryManager * @param Locker $locker @@ -120,7 +121,7 @@ class Installer * @param EventDispatcher $eventDispatcher * @param AutoloadGenerator $autoloadGenerator */ - public function __construct(IOInterface $io, Config $config, PackageInterface $package, DownloadManager $downloadManager, RepositoryManager $repositoryManager, Locker $locker, InstallationManager $installationManager, EventDispatcher $eventDispatcher, AutoloadGenerator $autoloadGenerator) + public function __construct(IOInterface $io, Config $config, RootPackageInterface $package, DownloadManager $downloadManager, RepositoryManager $repositoryManager, Locker $locker, InstallationManager $installationManager, EventDispatcher $eventDispatcher, AutoloadGenerator $autoloadGenerator) { $this->io = $io; $this->config = $config; diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index 14c0d65b0..1fe7ff2f8 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -18,7 +18,7 @@ use Composer\Package\Version\VersionParser; /** * @author Jordi Boggiano */ -class AliasPackage extends BasePackage +class AliasPackage extends BasePackage implements CompletePackageInterface { protected $version; protected $prettyVersion; diff --git a/src/Composer/Package/CompletePackage.php b/src/Composer/Package/CompletePackage.php new file mode 100644 index 000000000..a2177214a --- /dev/null +++ b/src/Composer/Package/CompletePackage.php @@ -0,0 +1,174 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package; + +use Composer\Package\Version\VersionParser; + +/** + * Package containing additional metadata that is not used by the solver + * + * @author Nils Adermann + */ +class CompletePackage extends Package implements CompletePackageInterface +{ + protected $repositories; + protected $license = array(); + protected $keywords; + protected $authors; + protected $description; + protected $homepage; + protected $scripts = array(); + protected $support = array(); + + /** + * @param array $scripts + */ + public function setScripts(array $scripts) + { + $this->scripts = $scripts; + } + + /** + * {@inheritDoc} + */ + public function getScripts() + { + return $this->scripts; + } + + /** + * Set the repositories + * + * @param string $repositories + */ + public function setRepositories($repositories) + { + $this->repositories = $repositories; + } + + /** + * {@inheritDoc} + */ + public function getRepositories() + { + return $this->repositories; + } + + /** + * Set the license + * + * @param array $license + */ + public function setLicense(array $license) + { + $this->license = $license; + } + + /** + * {@inheritDoc} + */ + public function getLicense() + { + return $this->license; + } + + /** + * Set the keywords + * + * @param array $keywords + */ + public function setKeywords(array $keywords) + { + $this->keywords = $keywords; + } + + /** + * {@inheritDoc} + */ + public function getKeywords() + { + return $this->keywords; + } + + /** + * Set the authors + * + * @param array $authors + */ + public function setAuthors(array $authors) + { + $this->authors = $authors; + } + + /** + * {@inheritDoc} + */ + public function getAuthors() + { + return $this->authors; + } + + /** + * Set the description + * + * @param string $description + */ + public function setDescription($description) + { + $this->description = $description; + } + + /** + * {@inheritDoc} + */ + public function getDescription() + { + return $this->description; + } + + /** + * Set the homepage + * + * @param string $homepage + */ + public function setHomepage($homepage) + { + $this->homepage = $homepage; + } + + /** + * {@inheritDoc} + */ + public function getHomepage() + { + return $this->homepage; + } + + /** + * Set the support information + * + * @param array $support + */ + public function setSupport(array $support) + { + $this->support = $support; + } + + /** + * {@inheritDoc} + */ + public function getSupport() + { + return $this->support; + } +} diff --git a/src/Composer/Package/CompletePackageInterface.php b/src/Composer/Package/CompletePackageInterface.php new file mode 100644 index 000000000..a341766d3 --- /dev/null +++ b/src/Composer/Package/CompletePackageInterface.php @@ -0,0 +1,81 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package; + +/** + * Defines package metadata that is not necessarily needed for solving and installing packages + * + * @author Nils Adermann + */ +interface CompletePackageInterface extends PackageInterface +{ + /** + * Returns the scripts of this package + * + * @return array array('script name' => array('listeners')) + */ + public function getScripts(); + + /** + * Returns an array of repositories + * + * {"": {}} + * + * @return array Repositories + */ + public function getRepositories(); + + /** + * Returns the package license, e.g. MIT, BSD, GPL + * + * @return array The package licenses + */ + public function getLicense(); + + /** + * Returns an array of keywords relating to the package + * + * @return array + */ + public function getKeywords(); + + /** + * Returns the package description + * + * @return string + */ + public function getDescription(); + + /** + * Returns the package homepage + * + * @return string + */ + public function getHomepage(); + + /** + * Returns an array of authors of the package + * + * Each item can contain name/homepage/email keys + * + * @return array + */ + public function getAuthors(); + + /** + * Returns the support information + * + * @return array + */ + public function getSupport(); +} diff --git a/src/Composer/Package/CorePackageInterface.php b/src/Composer/Package/CorePackageInterface.php deleted file mode 100644 index b0d98d40f..000000000 --- a/src/Composer/Package/CorePackageInterface.php +++ /dev/null @@ -1,297 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Package; - -use Composer\Repository\RepositoryInterface; - -/** - * Defines the essential information a package has that is used during solving/installation - * - * @author Jordi Boggiano - */ -interface CorePackageInterface -{ - /** - * Returns the package's name without version info, thus not a unique identifier - * - * @return string package name - */ - public function getName(); - - /** - * Returns the package's pretty (i.e. with proper case) name - * - * @return string package name - */ - public function getPrettyName(); - - /** - * Returns a set of names that could refer to this package - * - * No version or release type information should be included in any of the - * names. Provided or replaced package names need to be returned as well. - * - * @return array An array of strings referring to this package - */ - public function getNames(); - - /** - * Allows the solver to set an id for this package to refer to it. - * - * @param int $id - */ - public function setId($id); - - /** - * Retrieves the package's id set through setId - * - * @return int The previously set package id - */ - public function getId(); - - /** - * Returns whether the package is a development virtual package or a concrete one - * - * @return bool - */ - public function isDev(); - - /** - * Returns the package type, e.g. library - * - * @return string The package type - */ - public function getType(); - - /** - * Returns the package targetDir property - * - * @return string The package targetDir - */ - public function getTargetDir(); - - /** - * Returns the package extra data - * - * @return array The package extra data - */ - public function getExtra(); - - /** - * Sets source from which this package was installed (source/dist). - * - * @param string $type source/dist - */ - public function setInstallationSource($type); - - /** - * Returns source from which this package was installed (source/dist). - * - * @param string $type source/dist - */ - public function getInstallationSource(); - - /** - * Returns the repository type of this package, e.g. git, svn - * - * @return string The repository type - */ - public function getSourceType(); - - /** - * Returns the repository url of this package, e.g. git://github.com/naderman/composer.git - * - * @return string The repository url - */ - public function getSourceUrl(); - - /** - * Returns the repository reference of this package, e.g. master, 1.0.0 or a commit hash for git - * - * @return string The repository reference - */ - public function getSourceReference(); - - /** - * Returns the type of the distribution archive of this version, e.g. zip, tarball - * - * @return string The repository type - */ - public function getDistType(); - - /** - * Returns the url of the distribution archive of this version - * - * @return string - */ - public function getDistUrl(); - - /** - * Returns the reference of the distribution archive of this version, e.g. master, 1.0.0 or a commit hash for git - * - * @return string - */ - public function getDistReference(); - - /** - * Returns the sha1 checksum for the distribution archive of this version - * - * @return string - */ - public function getDistSha1Checksum(); - - /** - * Returns the version of this package - * - * @return string version - */ - public function getVersion(); - - /** - * Returns the pretty (i.e. non-normalized) version string of this package - * - * @return string version - */ - public function getPrettyVersion(); - - /** - * Returns the stability of this package: one of (dev, alpha, beta, RC, stable) - * - * @return string - */ - public function getStability(); - - /** - * Returns a set of links to packages which need to be installed before - * this package can be installed - * - * @return array An array of package links defining required packages - */ - public function getRequires(); - - /** - * Returns a set of links to packages which must not be installed at the - * same time as this package - * - * @return array An array of package links defining conflicting packages - */ - public function getConflicts(); - - /** - * Returns a set of links to virtual packages that are provided through - * this package - * - * @return array An array of package links defining provided packages - */ - public function getProvides(); - - /** - * Returns a set of links to packages which can alternatively be - * satisfied by installing this package - * - * @return array An array of package links defining replaced packages - */ - public function getReplaces(); - - /** - * Returns a set of links to packages which are required to develop - * this package. These are installed if in dev mode. - * - * @return array An array of package links defining packages required for development - */ - public function getDevRequires(); - - /** - * Returns a set of package names and reasons why they are useful in - * combination with this package. - * - * @return array An array of package suggestions with descriptions - */ - public function getSuggests(); - - /** - * Returns an associative array of autoloading rules - * - * {"": {""}} - * - * Type is either "psr-0" or "pear". Namespaces are mapped to directories - * for autoloading using the type specified. - * - * @return array Mapping of autoloading rules - */ - public function getAutoload(); - - /** - * Returns a list of directories which should get added to PHP's - * include path. - * - * @return array - */ - public function getIncludePaths(); - - /** - * Stores a reference to the repository that owns the package - * - * @param RepositoryInterface $repository - */ - public function setRepository(RepositoryInterface $repository); - - /** - * Returns a reference to the repository that owns the package - * - * @return RepositoryInterface - */ - public function getRepository(); - - /** - * Returns the package binaries - * - * @return array - */ - public function getBinaries(); - - /** - * Returns a version this package should be aliased to - * - * @return string - */ - public function getAlias(); - - /** - * Returns a non-normalized version this package should be aliased to - * - * @return string - */ - public function getPrettyAlias(); - - /** - * Returns package unique name, constructed from name and version. - * - * @return string - */ - public function getUniqueName(); - - /** - * Converts the package into a readable and unique string - * - * @return string - */ - public function __toString(); - - /** - * Converts the package into a pretty readable string - * - * @return string - */ - public function getPrettyString(); -} diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php index 70691ee44..76e1dede4 100644 --- a/src/Composer/Package/Dumper/ArrayDumper.php +++ b/src/Composer/Package/Dumper/ArrayDumper.php @@ -14,6 +14,7 @@ namespace Composer\Package\Dumper; use Composer\Package\BasePackage; use Composer\Package\PackageInterface; +use Composer\Package\CompletePackageInterface; /** * @author Konstantin Kudryashiv @@ -25,19 +26,11 @@ class ArrayDumper { $keys = array( 'binaries' => 'bin', - 'scripts', 'type', 'extra', 'installationSource' => 'installation-source', - 'license', - 'authors', - 'description', - 'homepage', - 'keywords', 'autoload', - 'repositories', 'includePaths' => 'include-path', - 'support', ); $data = array(); @@ -49,10 +42,6 @@ class ArrayDumper $data['target-dir'] = $package->getTargetDir(); } - if ($package->getReleaseDate()) { - $data['time'] = $package->getReleaseDate()->format('Y-m-d H:i:s'); - } - if ($package->getSourceType()) { $data['source']['type'] = $package->getSourceType(); $data['source']['url'] = $package->getSourceUrl(); @@ -78,6 +67,32 @@ class ArrayDumper $data['suggest'] = $packages; } + if ($package->getReleaseDate()) { + $data['time'] = $package->getReleaseDate()->format('Y-m-d H:i:s'); + } + + $data = $this->dumpValues($package, $keys, $data); + + if ($package instanceof CompletePackageInterface) { + $keys = array( + 'scripts', + 'license', + 'authors', + 'description', + 'homepage', + 'keywords', + 'repositories', + 'support', + ); + + $data = $this->dumpValues($package, $keys, $data); + } + + return $data; + } + + private function dumpValues(PackageInterface $package, array $keys, array $data) + { foreach ($keys as $method => $key) { if (is_numeric($method)) { $method = $key; diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 07ef88a59..0a8c9fb53 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -31,7 +31,7 @@ class ArrayLoader implements LoaderInterface $this->versionParser = $parser; } - public function load(array $config) + public function load(array $config, $class = 'Composer\Package\CompletePackage') { if (!isset($config['name'])) { throw new \UnexpectedValueException('Unknown package has no name defined ('.json_encode($config).').'); @@ -46,7 +46,7 @@ class ArrayLoader implements LoaderInterface } else { $version = $this->versionParser->normalize($config['version']); } - $package = new Package\MemoryPackage($config['name'], $version, $config['version']); + $package = new $class($config['name'], $version, $config['version']); $package->setType(isset($config['type']) ? strtolower($config['type']) : 'library'); if (isset($config['target-dir'])) { @@ -67,42 +67,6 @@ class ArrayLoader implements LoaderInterface $package->setBinaries($config['bin']); } - if (isset($config['scripts']) && is_array($config['scripts'])) { - foreach ($config['scripts'] as $event => $listeners) { - $config['scripts'][$event]= (array) $listeners; - } - $package->setScripts($config['scripts']); - } - - if (!empty($config['description']) && is_string($config['description'])) { - $package->setDescription($config['description']); - } - - if (!empty($config['homepage']) && is_string($config['homepage'])) { - $package->setHomepage($config['homepage']); - } - - if (!empty($config['keywords']) && is_array($config['keywords'])) { - $package->setKeywords($config['keywords']); - } - - if (!empty($config['license'])) { - $package->setLicense(is_array($config['license']) ? $config['license'] : array($config['license'])); - } - - if (!empty($config['time'])) { - try { - $date = new \DateTime($config['time']); - $date->setTimezone(new \DateTimeZone('UTC')); - $package->setReleaseDate($date); - } catch (\Exception $e) { - } - } - - if (!empty($config['authors']) && is_array($config['authors'])) { - $package->setAuthors($config['authors']); - } - if (isset($config['installation-source'])) { $package->setInstallationSource($config['installation-source']); } @@ -165,8 +129,46 @@ class ArrayLoader implements LoaderInterface $package->setIncludePaths($config['include-path']); } - if (isset($config['support'])) { - $package->setSupport($config['support']); + if (!empty($config['time'])) { + try { + $date = new \DateTime($config['time']); + $date->setTimezone(new \DateTimeZone('UTC')); + $package->setReleaseDate($date); + } catch (\Exception $e) { + } + } + + if ($package instanceof Package\CompletePackageInterface) { + if (isset($config['scripts']) && is_array($config['scripts'])) { + foreach ($config['scripts'] as $event => $listeners) { + $config['scripts'][$event]= (array) $listeners; + } + $package->setScripts($config['scripts']); + } + + if (!empty($config['description']) && is_string($config['description'])) { + $package->setDescription($config['description']); + } + + if (!empty($config['homepage']) && is_string($config['homepage'])) { + $package->setHomepage($config['homepage']); + } + + if (!empty($config['keywords']) && is_array($config['keywords'])) { + $package->setKeywords($config['keywords']); + } + + if (!empty($config['license'])) { + $package->setLicense(is_array($config['license']) ? $config['license'] : array($config['license'])); + } + + if (!empty($config['authors']) && is_array($config['authors'])) { + $package->setAuthors($config['authors']); + } + + if (isset($config['support'])) { + $package->setSupport($config['support']); + } } return $package; diff --git a/src/Composer/Package/Loader/LoaderInterface.php b/src/Composer/Package/Loader/LoaderInterface.php index 7fd03c49e..295ddf95b 100644 --- a/src/Composer/Package/Loader/LoaderInterface.php +++ b/src/Composer/Package/Loader/LoaderInterface.php @@ -23,7 +23,8 @@ interface LoaderInterface * Converts a package from an array to a real instance * * @param array $package Package config + * @param string $class Package class to use * @return \Composer\Package\PackageInterface */ - public function load(array $package); + public function load(array $package, $class = 'Composer\Package\CompletePackage'); } diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index d4c62bca7..2733744ee 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -40,7 +40,7 @@ class RootPackageLoader extends ArrayLoader parent::__construct($parser); } - public function load(array $config) + public function load(array $config, $class = 'Composer\Package\RootPackage') { if (!isset($config['name'])) { $config['name'] = '__root__'; @@ -62,7 +62,7 @@ class RootPackageLoader extends ArrayLoader $version = $config['version']; } - $package = parent::load($config); + $package = parent::load($config, $class); $aliases = array(); $stabilityFlags = array(); diff --git a/src/Composer/Package/Loader/ValidatingArrayLoader.php b/src/Composer/Package/Loader/ValidatingArrayLoader.php index 7b68fd4d0..653ed4b42 100644 --- a/src/Composer/Package/Loader/ValidatingArrayLoader.php +++ b/src/Composer/Package/Loader/ValidatingArrayLoader.php @@ -36,7 +36,7 @@ class ValidatingArrayLoader implements LoaderInterface $this->versionParser = $parser; } - public function load(array $config) + public function load(array $config, $class = 'Composer\Package\CompletePackage') { $this->config = $config; @@ -168,7 +168,7 @@ class ValidatingArrayLoader implements LoaderInterface throw new \Exception(implode("\n", $this->errors)); } - $package = $this->loader->load($this->config); + $package = $this->loader->load($this->config, $class); $this->errors = array(); $this->config = null; diff --git a/src/Composer/Package/MemoryPackage.php b/src/Composer/Package/Package.php similarity index 70% rename from src/Composer/Package/MemoryPackage.php rename to src/Composer/Package/Package.php index dcb4f2bcf..d2de23673 100644 --- a/src/Composer/Package/MemoryPackage.php +++ b/src/Composer/Package/Package.php @@ -15,11 +15,11 @@ namespace Composer\Package; use Composer\Package\Version\VersionParser; /** - * A package with setters for all members to create it dynamically in memory + * Core package definitions that are needed to resolve dependencies and install packages * * @author Nils Adermann */ -class MemoryPackage extends BasePackage +class Package extends BasePackage { protected $type; protected $targetDir; @@ -33,24 +33,14 @@ class MemoryPackage extends BasePackage protected $distSha1Checksum; protected $version; protected $prettyVersion; - protected $repositories; - protected $license = array(); protected $releaseDate; - protected $keywords; - protected $authors; - protected $description; - protected $homepage; protected $extra = array(); protected $binaries = array(); - protected $scripts = array(); protected $aliases = array(); protected $alias; protected $prettyAlias; protected $dev; - - protected $minimumStability = 'stable'; - protected $stabilityFlags = array(); - protected $references = array(); + protected $stability; protected $requires = array(); protected $conflicts = array(); @@ -60,7 +50,6 @@ class MemoryPackage extends BasePackage protected $suggests = array(); protected $autoload = array(); protected $includePaths = array(); - protected $support = array(); /** * Creates a new in memory package. @@ -160,22 +149,6 @@ class MemoryPackage extends BasePackage return $this->binaries; } - /** - * @param array $scripts - */ - public function setScripts(array $scripts) - { - $this->scripts = $scripts; - } - - /** - * {@inheritDoc} - */ - public function getScripts() - { - return $this->scripts; - } - /** * @param array $aliases */ @@ -352,24 +325,6 @@ class MemoryPackage extends BasePackage return $this->distSha1Checksum; } - /** - * Set the repositories - * - * @param string $repositories - */ - public function setRepositories($repositories) - { - $this->repositories = $repositories; - } - - /** - * {@inheritDoc} - */ - public function getRepositories() - { - return $this->repositories; - } - /** * {@inheritDoc} */ @@ -387,21 +342,21 @@ class MemoryPackage extends BasePackage } /** - * Set the license + * Set the releaseDate * - * @param array $license + * @param DateTime $releaseDate */ - public function setLicense(array $license) + public function setReleaseDate(\DateTime $releaseDate) { - $this->license = $license; + $this->releaseDate = $releaseDate; } /** * {@inheritDoc} */ - public function getLicense() + public function getReleaseDate() { - return $this->license; + return $this->releaseDate; } /** @@ -512,150 +467,6 @@ class MemoryPackage extends BasePackage return $this->suggests; } - /** - * Set the releaseDate - * - * @param DateTime $releaseDate - */ - public function setReleaseDate(\DateTime $releaseDate) - { - $this->releaseDate = $releaseDate; - } - - /** - * {@inheritDoc} - */ - public function getReleaseDate() - { - return $this->releaseDate; - } - - /** - * Set the keywords - * - * @param array $keywords - */ - public function setKeywords(array $keywords) - { - $this->keywords = $keywords; - } - - /** - * {@inheritDoc} - */ - public function getKeywords() - { - return $this->keywords; - } - - /** - * Set the authors - * - * @param array $authors - */ - public function setAuthors(array $authors) - { - $this->authors = $authors; - } - - /** - * {@inheritDoc} - */ - public function getAuthors() - { - return $this->authors; - } - - /** - * Set the description - * - * @param string $description - */ - public function setDescription($description) - { - $this->description = $description; - } - - /** - * {@inheritDoc} - */ - public function getDescription() - { - return $this->description; - } - - /** - * Set the homepage - * - * @param string $homepage - */ - public function setHomepage($homepage) - { - $this->homepage = $homepage; - } - - /** - * {@inheritDoc} - */ - public function getHomepage() - { - return $this->homepage; - } - - /** - * Set the minimumStability - * - * @param string $minimumStability - */ - public function setMinimumStability($minimumStability) - { - $this->minimumStability = $minimumStability; - } - - /** - * {@inheritDoc} - */ - public function getMinimumStability() - { - return $this->minimumStability; - } - - /** - * Set the stabilityFlags - * - * @param array $stabilityFlags - */ - public function setStabilityFlags(array $stabilityFlags) - { - $this->stabilityFlags = $stabilityFlags; - } - - /** - * {@inheritDoc} - */ - public function getStabilityFlags() - { - return $this->stabilityFlags; - } - - /** - * Set the references - * - * @param array $references - */ - public function setReferences(array $references) - { - $this->references = $references; - } - - /** - * {@inheritDoc} - */ - public function getReferences() - { - return $this->references; - } - /** * Set the autoload mapping * @@ -691,22 +502,4 @@ class MemoryPackage extends BasePackage { return $this->includePaths; } - - /** - * Set the support information - * - * @param array $support - */ - public function setSupport(array $support) - { - $this->support = $support; - } - - /** - * {@inheritDoc} - */ - public function getSupport() - { - return $this->support; - } } diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index 5d4958c61..c8b9a7f39 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -12,35 +12,157 @@ namespace Composer\Package; +use Composer\Repository\RepositoryInterface; + /** - * Defines package metadata that is not necessarily needed for solving and installing packages + * Defines the essential information a package has that is used during solving/installation * - * @author Nils Adermann + * @author Jordi Boggiano */ -interface PackageInterface extends CorePackageInterface +interface PackageInterface { /** - * Returns the scripts of this package + * Returns the package's name without version info, thus not a unique identifier * - * @return array array('script name' => array('listeners')) + * @return string package name */ - public function getScripts(); + public function getName(); /** - * Returns an array of repositories + * Returns the package's pretty (i.e. with proper case) name * - * {"": {}} - * - * @return array Repositories + * @return string package name */ - public function getRepositories(); + public function getPrettyName(); /** - * Returns the package license, e.g. MIT, BSD, GPL + * Returns a set of names that could refer to this package * - * @return array The package licenses + * No version or release type information should be included in any of the + * names. Provided or replaced package names need to be returned as well. + * + * @return array An array of strings referring to this package */ - public function getLicense(); + public function getNames(); + + /** + * Allows the solver to set an id for this package to refer to it. + * + * @param int $id + */ + public function setId($id); + + /** + * Retrieves the package's id set through setId + * + * @return int The previously set package id + */ + public function getId(); + + /** + * Returns whether the package is a development virtual package or a concrete one + * + * @return bool + */ + public function isDev(); + + /** + * Returns the package type, e.g. library + * + * @return string The package type + */ + public function getType(); + + /** + * Returns the package targetDir property + * + * @return string The package targetDir + */ + public function getTargetDir(); + + /** + * Returns the package extra data + * + * @return array The package extra data + */ + public function getExtra(); + + /** + * Sets source from which this package was installed (source/dist). + * + * @param string $type source/dist + */ + public function setInstallationSource($type); + + /** + * Returns source from which this package was installed (source/dist). + * + * @param string $type source/dist + */ + public function getInstallationSource(); + + /** + * Returns the repository type of this package, e.g. git, svn + * + * @return string The repository type + */ + public function getSourceType(); + + /** + * Returns the repository url of this package, e.g. git://github.com/naderman/composer.git + * + * @return string The repository url + */ + public function getSourceUrl(); + + /** + * Returns the repository reference of this package, e.g. master, 1.0.0 or a commit hash for git + * + * @return string The repository reference + */ + public function getSourceReference(); + + /** + * Returns the type of the distribution archive of this version, e.g. zip, tarball + * + * @return string The repository type + */ + public function getDistType(); + + /** + * Returns the url of the distribution archive of this version + * + * @return string + */ + public function getDistUrl(); + + /** + * Returns the reference of the distribution archive of this version, e.g. master, 1.0.0 or a commit hash for git + * + * @return string + */ + public function getDistReference(); + + /** + * Returns the sha1 checksum for the distribution archive of this version + * + * @return string + */ + public function getDistSha1Checksum(); + + /** + * Returns the version of this package + * + * @return string version + */ + public function getVersion(); + + /** + * Returns the pretty (i.e. non-normalized) version string of this package + * + * @return string version + */ + public function getPrettyVersion(); /** * Returns the release date of the package @@ -50,39 +172,133 @@ interface PackageInterface extends CorePackageInterface public function getReleaseDate(); /** - * Returns an array of keywords relating to the package - * - * @return array - */ - public function getKeywords(); - - /** - * Returns the package description + * Returns the stability of this package: one of (dev, alpha, beta, RC, stable) * * @return string */ - public function getDescription(); + public function getStability(); /** - * Returns the package homepage + * Returns a set of links to packages which need to be installed before + * this package can be installed + * + * @return array An array of package links defining required packages + */ + public function getRequires(); + + /** + * Returns a set of links to packages which must not be installed at the + * same time as this package + * + * @return array An array of package links defining conflicting packages + */ + public function getConflicts(); + + /** + * Returns a set of links to virtual packages that are provided through + * this package + * + * @return array An array of package links defining provided packages + */ + public function getProvides(); + + /** + * Returns a set of links to packages which can alternatively be + * satisfied by installing this package + * + * @return array An array of package links defining replaced packages + */ + public function getReplaces(); + + /** + * Returns a set of links to packages which are required to develop + * this package. These are installed if in dev mode. + * + * @return array An array of package links defining packages required for development + */ + public function getDevRequires(); + + /** + * Returns a set of package names and reasons why they are useful in + * combination with this package. + * + * @return array An array of package suggestions with descriptions + */ + public function getSuggests(); + + /** + * Returns an associative array of autoloading rules + * + * {"": {""}} + * + * Type is either "psr-0" or "pear". Namespaces are mapped to directories + * for autoloading using the type specified. + * + * @return array Mapping of autoloading rules + */ + public function getAutoload(); + + /** + * Returns a list of directories which should get added to PHP's + * include path. + * + * @return array + */ + public function getIncludePaths(); + + /** + * Stores a reference to the repository that owns the package + * + * @param RepositoryInterface $repository + */ + public function setRepository(RepositoryInterface $repository); + + /** + * Returns a reference to the repository that owns the package + * + * @return RepositoryInterface + */ + public function getRepository(); + + /** + * Returns the package binaries + * + * @return array + */ + public function getBinaries(); + + /** + * Returns a version this package should be aliased to * * @return string */ - public function getHomepage(); + public function getAlias(); /** - * Returns an array of authors of the package + * Returns a non-normalized version this package should be aliased to * - * Each item can contain name/homepage/email keys - * - * @return array + * @return string */ - public function getAuthors(); + public function getPrettyAlias(); /** - * Returns the support information + * Returns package unique name, constructed from name and version. * - * @return array + * @return string */ - public function getSupport(); + public function getUniqueName(); + + /** + * Converts the package into a readable and unique string + * + * @return string + */ + public function __toString(); + + /** + * Converts the package into a pretty readable string + * + * @return string + */ + public function getPrettyString(); } diff --git a/src/Composer/Package/RootPackage.php b/src/Composer/Package/RootPackage.php new file mode 100644 index 000000000..10bd826c5 --- /dev/null +++ b/src/Composer/Package/RootPackage.php @@ -0,0 +1,81 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package; + +use Composer\Package\Version\VersionParser; + +/** + * The root package represents the project's composer.json and contains additional metadata + * + * @author Jordi Boggiano + */ +class RootPackage extends CompletePackage implements RootPackageInterface +{ + protected $minimumStability = 'stable'; + protected $stabilityFlags = array(); + protected $references = array(); + + /** + * Set the minimumStability + * + * @param string $minimumStability + */ + public function setMinimumStability($minimumStability) + { + $this->minimumStability = $minimumStability; + } + + /** + * {@inheritDoc} + */ + public function getMinimumStability() + { + return $this->minimumStability; + } + + /** + * Set the stabilityFlags + * + * @param array $stabilityFlags + */ + public function setStabilityFlags(array $stabilityFlags) + { + $this->stabilityFlags = $stabilityFlags; + } + + /** + * {@inheritDoc} + */ + public function getStabilityFlags() + { + return $this->stabilityFlags; + } + + /** + * Set the references + * + * @param array $references + */ + public function setReferences(array $references) + { + $this->references = $references; + } + + /** + * {@inheritDoc} + */ + public function getReferences() + { + return $this->references; + } +} diff --git a/src/Composer/Package/RootPackageInterface.php b/src/Composer/Package/RootPackageInterface.php index 0b8a30f4e..501376f66 100644 --- a/src/Composer/Package/RootPackageInterface.php +++ b/src/Composer/Package/RootPackageInterface.php @@ -17,7 +17,7 @@ namespace Composer\Package; * * @author Jordi Boggiano */ -interface RootPackageInterface extends PackageInterface +interface RootPackageInterface extends CompletePackageInterface { /** * Returns the minimum stability of the package diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index b8937bb33..51a51fd27 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -127,7 +127,7 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository */ public function loadPackage(array $data) { - $package = $this->loader->load($data['raw']); + $package = $this->loader->load($data['raw'], 'Composer\Package\Package'); $package->setRepository($this); return $package; @@ -154,7 +154,7 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository $repoData = $this->loadDataFromServer(); foreach ($repoData as $package) { - $this->addPackage($this->loader->load($package)); + $this->addPackage($this->loader->load($package, 'Composer\Package\Package')); } } diff --git a/src/Composer/Repository/Pear/ChannelReader.php b/src/Composer/Repository/Pear/ChannelReader.php index 7beb37d3c..bb909b3b7 100644 --- a/src/Composer/Repository/Pear/ChannelReader.php +++ b/src/Composer/Repository/Pear/ChannelReader.php @@ -17,7 +17,7 @@ use Composer\Util\RemoteFilesystem; /** * PEAR Channel package reader. * - * Reads channel packages info from and builds MemoryPackage's + * Reads channel packages info from and builds Package's * * @author Alexey Prilipko */ diff --git a/src/Composer/Repository/PearRepository.php b/src/Composer/Repository/PearRepository.php index 6b3119e47..7af8e6f4d 100644 --- a/src/Composer/Repository/PearRepository.php +++ b/src/Composer/Repository/PearRepository.php @@ -15,7 +15,7 @@ namespace Composer\Repository; use Composer\IO\IOInterface; use Composer\Package\Version\VersionParser; use Composer\Repository\Pear\ChannelReader; -use Composer\Package\MemoryPackage; +use Composer\Package\CompletePackage; use Composer\Repository\Pear\ChannelInfo; use Composer\Package\Link; use Composer\Package\LinkConstraint\VersionConstraint; @@ -81,10 +81,10 @@ class PearRepository extends ArrayRepository } /** - * Builds MemoryPackages from PEAR package definition data. + * Builds CompletePackages from PEAR package definition data. * * @param ChannelInfo $channelInfo - * @return MemoryPackage + * @return CompletePackage */ private function buildComposerPackages(ChannelInfo $channelInfo, VersionParser $versionParser) { @@ -152,7 +152,7 @@ class PearRepository extends ArrayRepository } } - $package = new MemoryPackage($composerPackageName, $normalizedVersion, $version); + $package = new CompletePackage($composerPackageName, $normalizedVersion, $version); $package->setType('pear-library'); $package->setDescription($packageDefinition->getDescription()); $package->setDistType('file'); diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index c7db1f8ea..c6affe9a2 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -12,7 +12,7 @@ namespace Composer\Repository; -use Composer\Package\MemoryPackage; +use Composer\Package\CompletePackage; use Composer\Package\Version\VersionParser; /** @@ -34,7 +34,7 @@ class PlatformRepository extends ArrayRepository $version = $versionParser->normalize($prettyVersion); } - $php = new MemoryPackage('php', $version, $prettyVersion); + $php = new CompletePackage('php', $version, $prettyVersion); $php->setDescription('The PHP interpreter'); parent::addPackage($php); @@ -55,7 +55,7 @@ class PlatformRepository extends ArrayRepository $version = $versionParser->normalize($prettyVersion); } - $ext = new MemoryPackage('ext-'.$name, $version, $prettyVersion); + $ext = new CompletePackage('ext-'.$name, $version, $prettyVersion); $ext->setDescription('The '.$name.' PHP extension'); parent::addPackage($ext); } @@ -107,7 +107,7 @@ class PlatformRepository extends ArrayRepository continue; } - $lib = new MemoryPackage('lib-'.$name, $version, $prettyVersion); + $lib = new CompletePackage('lib-'.$name, $version, $prettyVersion); $lib->setDescription('The '.$name.' PHP library'); parent::addPackage($lib); } diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 9a5d48ba9..33601cc91 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -15,7 +15,7 @@ namespace Composer\Test\Autoload; use Composer\Autoload\AutoloadGenerator; use Composer\Util\Filesystem; use Composer\Package\AliasPackage; -use Composer\Package\MemoryPackage; +use Composer\Package\Package; use Composer\Test\TestCase; class AutoloadGeneratorTest extends TestCase @@ -76,7 +76,7 @@ class AutoloadGeneratorTest extends TestCase public function testMainPackageAutoloading() { - $package = new MemoryPackage('a', '1.0', '1.0'); + $package = new Package('a', '1.0', '1.0'); $package->setAutoload(array( 'psr-0' => array('Main' => 'src/', 'Lala' => array('src/', 'lib/')), 'classmap' => array('composersrc/'), @@ -101,7 +101,7 @@ class AutoloadGeneratorTest extends TestCase { $this->vendorDir = $this->workingDir; - $package = new MemoryPackage('a', '1.0', '1.0'); + $package = new Package('a', '1.0', '1.0'); $package->setAutoload(array( 'psr-0' => array('Main' => 'src/', 'Lala' => 'src/'), 'classmap' => array('composersrc/'), @@ -124,7 +124,7 @@ class AutoloadGeneratorTest extends TestCase public function testMainPackageAutoloadingAlternativeVendorDir() { - $package = new MemoryPackage('a', '1.0', '1.0'); + $package = new Package('a', '1.0', '1.0'); $package->setAutoload(array( 'psr-0' => array('Main' => 'src/', 'Lala' => 'src/'), 'classmap' => array('composersrc/'), @@ -147,7 +147,7 @@ class AutoloadGeneratorTest extends TestCase public function testMainPackageAutoloadingWithTargetDir() { - $package = new MemoryPackage('a', '1.0', '1.0'); + $package = new Package('a', '1.0', '1.0'); $package->setAutoload(array( 'psr-0' => array('Main\\Foo' => '', 'Main\\Bar' => ''), )); @@ -166,11 +166,11 @@ class AutoloadGeneratorTest extends TestCase public function testVendorsAutoloading() { - $package = new MemoryPackage('a', '1.0', '1.0'); + $package = new Package('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'); + $packages[] = $a = new Package('a/a', '1.0', '1.0'); + $packages[] = $b = new Package('b/b', '1.0', '1.0'); $packages[] = $c = new AliasPackage($b, '1.2', '1.2'); $a->setAutoload(array('psr-0' => array('A' => 'src/', 'A\\B' => 'lib/'))); $b->setAutoload(array('psr-0' => array('B\\Sub\\Name' => 'src/'))); @@ -191,11 +191,11 @@ class AutoloadGeneratorTest extends TestCase public function testVendorsClassMapAutoloading() { - $package = new MemoryPackage('a', '1.0', '1.0'); + $package = new Package('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'); + $packages[] = $a = new Package('a/a', '1.0', '1.0'); + $packages[] = $b = new Package('b/b', '1.0', '1.0'); $a->setAutoload(array('classmap' => array('src/'))); $b->setAutoload(array('classmap' => array('src/', 'lib/'))); @@ -226,12 +226,12 @@ class AutoloadGeneratorTest extends TestCase public function testClassMapAutoloadingEmptyDirAndExactFile() { - $package = new MemoryPackage('a', '1.0', '1.0'); + $package = new Package('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'); - $packages[] = $c = new MemoryPackage('c/c', '1.0', '1.0'); + $packages[] = $a = new Package('a/a', '1.0', '1.0'); + $packages[] = $b = new Package('b/b', '1.0', '1.0'); + $packages[] = $c = new Package('c/c', '1.0', '1.0'); $a->setAutoload(array('classmap' => array(''))); $b->setAutoload(array('classmap' => array('test.php'))); $c->setAutoload(array('classmap' => array('./'))); @@ -263,12 +263,12 @@ class AutoloadGeneratorTest extends TestCase public function testFilesAutoloadGeneration() { - $package = new MemoryPackage('a', '1.0', '1.0'); + $package = new Package('a', '1.0', '1.0'); $package->setAutoload(array('files' => array('root.php'))); $packages = array(); - $packages[] = $a = new MemoryPackage('a/a', '1.0', '1.0'); - $packages[] = $b = new MemoryPackage('b/b', '1.0', '1.0'); + $packages[] = $a = new Package('a/a', '1.0', '1.0'); + $packages[] = $b = new Package('b/b', '1.0', '1.0'); $a->setAutoload(array('files' => array('test.php'))); $b->setAutoload(array('files' => array('test2.php'))); @@ -297,12 +297,12 @@ class AutoloadGeneratorTest extends TestCase public function testOverrideVendorsAutoloading() { - $package = new MemoryPackage('a', '1.0', '1.0'); + $package = new Package('a', '1.0', '1.0'); $package->setAutoload(array('psr-0' => array('A\\B' => $this->workingDir.'/lib'))); $packages = array(); - $packages[] = $a = new MemoryPackage('a/a', '1.0', '1.0'); - $packages[] = $b = new MemoryPackage('b/b', '1.0', '1.0'); + $packages[] = $a = new Package('a/a', '1.0', '1.0'); + $packages[] = $b = new Package('b/b', '1.0', '1.0'); $a->setAutoload(array('psr-0' => array('A' => 'src/', 'A\\B' => 'lib/'))); $b->setAutoload(array('psr-0' => array('B\\Sub\\Name' => 'src/'))); @@ -356,16 +356,16 @@ EOF; public function testIncludePathFileGeneration() { - $package = new MemoryPackage('a', '1.0', '1.0'); + $package = new Package('a', '1.0', '1.0'); $packages = array(); - $a = new MemoryPackage("a/a", "1.0", "1.0"); + $a = new Package("a/a", "1.0", "1.0"); $a->setIncludePaths(array("lib/")); - $b = new MemoryPackage("b/b", "1.0", "1.0"); + $b = new Package("b/b", "1.0", "1.0"); $b->setIncludePaths(array("library")); - $c = new MemoryPackage("c", "1.0", "1.0"); + $c = new Package("c", "1.0", "1.0"); $c->setIncludePaths(array("library")); $packages[] = $a; @@ -393,10 +393,10 @@ EOF; public function testIncludePathsArePrependedInAutoloadFile() { - $package = new MemoryPackage('a', '1.0', '1.0'); + $package = new Package('a', '1.0', '1.0'); $packages = array(); - $a = new MemoryPackage("a/a", "1.0", "1.0"); + $a = new Package("a/a", "1.0", "1.0"); $a->setIncludePaths(array("lib/")); $packages[] = $a; @@ -426,10 +426,10 @@ EOF; public function testIncludePathFileWithoutPathsIsSkipped() { - $package = new MemoryPackage('a', '1.0', '1.0'); + $package = new Package('a', '1.0', '1.0'); $packages = array(); - $a = new MemoryPackage("a/a", "1.0", "1.0"); + $a = new Package("a/a", "1.0", "1.0"); $packages[] = $a; $this->repository->expects($this->once()) diff --git a/tests/Composer/Test/ComposerTest.php b/tests/Composer/Test/ComposerTest.php index 6ff841919..c23488251 100644 --- a/tests/Composer/Test/ComposerTest.php +++ b/tests/Composer/Test/ComposerTest.php @@ -18,7 +18,7 @@ class ComposerTest extends TestCase public function testSetGetPackage() { $composer = new Composer(); - $package = $this->getMock('Composer\Package\PackageInterface'); + $package = $this->getMock('Composer\Package\RootPackageInterface'); $composer->setPackage($package); $this->assertSame($package, $composer->getPackage()); diff --git a/tests/Composer/Test/Installer/LibraryInstallerTest.php b/tests/Composer/Test/Installer/LibraryInstallerTest.php index fb130967d..c76052e22 100644 --- a/tests/Composer/Test/Installer/LibraryInstallerTest.php +++ b/tests/Composer/Test/Installer/LibraryInstallerTest.php @@ -235,7 +235,7 @@ class LibraryInstallerTest extends TestCase protected function createPackageMock() { - return $this->getMockBuilder('Composer\Package\MemoryPackage') + return $this->getMockBuilder('Composer\Package\Package') ->setConstructorArgs(array(md5(rand()), '1.0.0.0', '1.0.0')) ->getMock(); } diff --git a/tests/Composer/Test/Installer/MetapackageInstallerTest.php b/tests/Composer/Test/Installer/MetapackageInstallerTest.php index 1aa3d3e66..a590274df 100644 --- a/tests/Composer/Test/Installer/MetapackageInstallerTest.php +++ b/tests/Composer/Test/Installer/MetapackageInstallerTest.php @@ -94,7 +94,7 @@ class MetapackageInstallerTest extends \PHPUnit_Framework_TestCase private function createPackageMock() { - return $this->getMockBuilder('Composer\Package\MemoryPackage') + return $this->getMockBuilder('Composer\Package\Package') ->setConstructorArgs(array(md5(rand()), '1.0.0.0', '1.0.0')) ->getMock(); } diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index ebb684894..d4635f246 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -17,7 +17,7 @@ use Composer\Config; use Composer\Json\JsonFile; use Composer\Repository\ArrayRepository; use Composer\Repository\RepositoryManager; -use Composer\Package\PackageInterface; +use Composer\Package\RootPackageInterface; use Composer\Package\Link; use Composer\Package\Locker; use Composer\Test\Mock\FactoryMock; @@ -32,7 +32,7 @@ class InstallerTest extends TestCase /** * @dataProvider provideInstaller */ - public function testInstaller(PackageInterface $rootPackage, $repositories, array $options) + public function testInstaller(RootPackageInterface $rootPackage, $repositories, array $options) { $io = $this->getMock('Composer\IO\IOInterface'); @@ -80,7 +80,7 @@ class InstallerTest extends TestCase // when A requires B and B requires A, and A is a non-published root package // the install of B should succeed - $a = $this->getPackage('A', '1.0.0'); + $a = $this->getPackage('A', '1.0.0', 'Composer\Package\RootPackage'); $a->setRequires(array( new Link('A', 'B', $this->getVersionConstraint('=', '1.0.0')), )); @@ -100,7 +100,7 @@ class InstallerTest extends TestCase // #480: when A requires B and B requires A, and A is a published root package // only B should be installed, as A is the root - $a = $this->getPackage('A', '1.0.0'); + $a = $this->getPackage('A', '1.0.0', 'Composer\Package\RootPackage'); $a->setRequires(array( new Link('A', 'B', $this->getVersionConstraint('=', '1.0.0')), )); diff --git a/tests/Composer/Test/Package/MemoryPackageTest.php b/tests/Composer/Test/Package/CompletePackageTest.php similarity index 78% rename from tests/Composer/Test/Package/MemoryPackageTest.php rename to tests/Composer/Test/Package/CompletePackageTest.php index b843d18c2..cccdae1c0 100644 --- a/tests/Composer/Test/Package/MemoryPackageTest.php +++ b/tests/Composer/Test/Package/CompletePackageTest.php @@ -12,11 +12,11 @@ namespace Composer\Test\Package; -use Composer\Package\MemoryPackage; +use Composer\Package\Package; use Composer\Package\Version\VersionParser; use Composer\Test\TestCase; -class MemoryPackageTest extends TestCase +class CompletePackageTest extends TestCase { /** * Memory package naming, versioning, and marshalling semantics provider @@ -38,11 +38,11 @@ class MemoryPackageTest extends TestCase * Tests memory package naming semantics * @dataProvider providerVersioningSchemes */ - public function testMemoryPackageHasExpectedNamingSemantics($name, $version) + public function testPackageHasExpectedNamingSemantics($name, $version) { $versionParser = new VersionParser(); $normVersion = $versionParser->normalize($version); - $package = new MemoryPackage($name, $normVersion, $version); + $package = new Package($name, $normVersion, $version); $this->assertEquals(strtolower($name), $package->getName()); } @@ -50,11 +50,11 @@ class MemoryPackageTest extends TestCase * Tests memory package versioning semantics * @dataProvider providerVersioningSchemes */ - public function testMemoryPackageHasExpectedVersioningSemantics($name, $version) + public function testPackageHasExpectedVersioningSemantics($name, $version) { $versionParser = new VersionParser(); $normVersion = $versionParser->normalize($version); - $package = new MemoryPackage($name, $normVersion, $version); + $package = new Package($name, $normVersion, $version); $this->assertEquals($version, $package->getPrettyVersion()); $this->assertEquals($normVersion, $package->getVersion()); } @@ -63,11 +63,11 @@ class MemoryPackageTest extends TestCase * Tests memory package marshalling/serialization semantics * @dataProvider providerVersioningSchemes */ - public function testMemoryPackageHasExpectedMarshallingSemantics($name, $version) + public function testPackageHasExpectedMarshallingSemantics($name, $version) { $versionParser = new VersionParser(); $normVersion = $versionParser->normalize($version); - $package = new MemoryPackage($name, $normVersion, $version); + $package = new Package($name, $normVersion, $version); $this->assertEquals(strtolower($name).'-'.$normVersion, (string) $package); } diff --git a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php index 11ce676e4..9387ef926 100644 --- a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php +++ b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php @@ -23,14 +23,14 @@ class ArrayDumperTest extends \PHPUnit_Framework_TestCase */ private $dumper; /** - * @var \Composer\Package\PackageInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Composer\Package\CompletePackageInterface|\PHPUnit_Framework_MockObject_MockObject */ private $package; public function setUp() { $this->dumper = new ArrayDumper(); - $this->package = $this->getMock('Composer\Package\PackageInterface'); + $this->package = $this->getMock('Composer\Package\CompletePackageInterface'); } public function testRequiredInformation() diff --git a/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php b/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php index 8a93a60aa..127c5689c 100644 --- a/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php +++ b/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php @@ -16,7 +16,7 @@ use Composer\Test\TestCase; use Composer\Package\Version\VersionParser; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\Link; -use Composer\Package\MemoryPackage; +use Composer\Package\CompletePackage; use Composer\Test\Mock\RemoteFilesystemMock; class ChannelReaderTest extends TestCase @@ -117,7 +117,7 @@ class ChannelReaderTest extends TestCase $packages = $ref->invoke($reader, $channelInfo, new VersionParser()); - $expectedPackage = new MemoryPackage('pear-test.loc/sample', '1.0.0.1' , '1.0.0.1'); + $expectedPackage = new CompletePackage('pear-test.loc/sample', '1.0.0.1' , '1.0.0.1'); $expectedPackage->setType('pear-library'); $expectedPackage->setDistType('file'); $expectedPackage->setDescription('description'); diff --git a/tests/Composer/Test/TestCase.php b/tests/Composer/Test/TestCase.php index 0e13a7921..bb4e0f14b 100644 --- a/tests/Composer/Test/TestCase.php +++ b/tests/Composer/Test/TestCase.php @@ -13,7 +13,7 @@ namespace Composer\Test; use Composer\Package\Version\VersionParser; -use Composer\Package\MemoryPackage; +use Composer\Package\Package; use Composer\Package\AliasPackage; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Util\Filesystem; @@ -43,11 +43,11 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase return $constraint; } - protected function getPackage($name, $version) + protected function getPackage($name, $version, $class = 'Composer\Package\Package') { $normVersion = self::getVersionParser()->normalize($version); - return new MemoryPackage($name, $normVersion, $version); + return new $class($name, $normVersion, $version); } protected function getAliasPackage($package, $version) From 9965f02951c5db1edfc173ee1dc8236eaf1d6874 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Aug 2012 16:12:46 +0200 Subject: [PATCH 11/20] Clean up link creation --- src/Composer/DependencyResolver/Pool.php | 17 +++++--------- src/Composer/Package/Loader/ArrayLoader.php | 22 +++++------------- .../Package/Version/VersionParser.php | 23 +++++++++++++++++++ 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 940178ed3..955be6939 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -339,17 +339,12 @@ class Pool if (is_array($candidate)) { $candidateName = $candidate['name']; $candidateVersion = $candidate['version']; - foreach (array('provides', 'replaces') as $linkType) { - ${$linkType} = isset($candidate[rtrim($linkType, 's')]) ? $candidate[rtrim($linkType, 's')] : array(); - foreach (${$linkType} as $target => $constraintDef) { - if ('self.version' === $constraintDef) { - $parsedConstraint = $this->versionParser->parseConstraints($candidateVersion); - } else { - $parsedConstraint = $this->versionParser->parseConstraints($constraintDef); - } - ${$linkType}[$target] = new Link($candidateName, $target, $parsedConstraint, $linkType, $constraintDef); - } - } + $provides = isset($candidate['provide']) + ? $this->versionParser->parseLinks($candidateName, $candidateVersion, 'provides', $candidate['provide']) + : array(); + $replaces = isset($candidate['replace']) + ? $this->versionParser->parseLinks($candidateName, $candidateVersion, 'replaces', $candidate['replace']) + : array(); } else { // handle object packages $candidateName = $candidate->getName(); diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 0a8c9fb53..73c43a908 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -107,7 +107,12 @@ class ArrayLoader implements LoaderInterface if (isset($config[$type])) { $method = 'set'.ucfirst($opts['method']); $package->{$method}( - $this->loadLinksFromConfig($package, $opts['description'], $config[$type]) + $this->versionParser->parseLinks( + $package->getName(), + $package->getPrettyVersion(), + $opts['description'], + $config[$type] + ) ); } } @@ -209,19 +214,4 @@ class ArrayLoader implements LoaderInterface return $validatedTargetBranch; } } - - private function loadLinksFromConfig($package, $description, array $linksSpecs) - { - $links = array(); - foreach ($linksSpecs as $packageName => $constraint) { - if ('self.version' === $constraint) { - $parsedConstraint = $this->versionParser->parseConstraints($package->getPrettyVersion()); - } else { - $parsedConstraint = $this->versionParser->parseConstraints($constraint); - } - $links[] = new Package\Link($package->getName(), $packageName, $parsedConstraint, $description, $constraint); - } - - return $links; - } } diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index cd1ca30d0..ce06891b5 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -14,6 +14,7 @@ namespace Composer\Package\Version; use Composer\Package\BasePackage; use Composer\Package\PackageInterface; +use Composer\Package\Link; use Composer\Package\LinkConstraint\MultiConstraint; use Composer\Package\LinkConstraint\VersionConstraint; @@ -164,6 +165,28 @@ class VersionParser return 'dev-'.$name; } + /** + * @param string $source source package name + * @param string $sourceVersion source package version (pretty version ideally) + * @param string $description link description (e.g. requires, replaces, ..) + * @param array $links array of package name => constraint mappings + * @return Link[] + */ + public function parseLinks($source, $sourceVersion, $description, $links) + { + $res = array(); + foreach ($links as $target => $constraint) { + if ('self.version' === $constraint) { + $parsedConstraint = $this->parseConstraints($sourceVersion); + } else { + $parsedConstraint = $this->parseConstraints($constraint); + } + $res[] = new Link($source, $target, $parsedConstraint, $description, $constraint); + } + + return $res; + } + /** * Parses as constraint string into LinkConstraint objects * From e3b6bd781c7d1305bf605589fcf47dca63597f6a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Aug 2012 19:16:23 +0200 Subject: [PATCH 12/20] Add RepositoryInterface::filterPackages to stream ops on lists This cuts down on memory usage and also speeds up the search command to a third of its previous time --- src/Composer/Repository/ArrayRepository.php | 14 +++++++++++ .../Repository/ComposerRepository.php | 23 ++++++++++++++++++- .../Repository/CompositeRepository.php | 14 +++++++++++ .../Repository/RepositoryInterface.php | 14 +++++++++++ 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/ArrayRepository.php b/src/Composer/Repository/ArrayRepository.php index 590fc3009..07f648e0f 100644 --- a/src/Composer/Repository/ArrayRepository.php +++ b/src/Composer/Repository/ArrayRepository.php @@ -112,6 +112,20 @@ class ArrayRepository implements RepositoryInterface } } + /** + * {@inheritDoc} + */ + public function filterPackages($callback, $class = 'Composer\Package\Package') + { + foreach ($this->getPackages() as $package) { + if (false === $callback($package)) { + return false; + } + } + + return true; + } + protected function createAliasPackage(PackageInterface $package, $alias = null, $prettyAlias = null) { return new AliasPackage($package, $alias ?: $package->getAlias(), $prettyAlias ?: $package->getPrettyAlias()); diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 51a51fd27..ac1f3ac44 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -122,6 +122,27 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository return $this->minimalPackages; } + /** + * {@inheritDoc} + */ + public function filterPackages($callback, $class = 'Composer\Package\Package') + { + $repoData = $this->loadDataFromServer(); + + foreach ($repoData as $package) { + if (false === $callback($package = $this->loader->load($package, $class))) { + return false; + } + if ($package->getAlias()) { + if (false === $callback($this->createAliasPackage($package))) { + return false; + } + } + } + + return true; + } + /** * {@inheritDoc} */ @@ -154,7 +175,7 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository $repoData = $this->loadDataFromServer(); foreach ($repoData as $package) { - $this->addPackage($this->loader->load($package, 'Composer\Package\Package')); + $this->addPackage($this->loader->load($package, 'Composer\Package\CompletePackage')); } } diff --git a/src/Composer/Repository/CompositeRepository.php b/src/Composer/Repository/CompositeRepository.php index 4de713faa..d6e49763c 100644 --- a/src/Composer/Repository/CompositeRepository.php +++ b/src/Composer/Repository/CompositeRepository.php @@ -91,6 +91,20 @@ class CompositeRepository implements RepositoryInterface return call_user_func_array('array_merge', $packages); } + /** + * {@inheritDoc} + */ + public function filterPackages($callback, $class = 'Composer\Package\Package') + { + foreach ($this->repositories as $repository) { + if (false === $repository->filterPackages($callback, $class)) { + return false; + } + } + + return true; + } + /** * {@inheritdoc} */ diff --git a/src/Composer/Repository/RepositoryInterface.php b/src/Composer/Repository/RepositoryInterface.php index 89602378a..25bc3edb7 100644 --- a/src/Composer/Repository/RepositoryInterface.php +++ b/src/Composer/Repository/RepositoryInterface.php @@ -51,6 +51,20 @@ interface RepositoryInterface extends \Countable */ public function findPackages($name, $version = null); + /** + * Filters all the packages throuhg a callback + * + * The packages are not guaranteed to be instances in the repository + * and this can only be used for streaming through a list of packages. + * + * If the callback returns false, the process stops + * + * @param callable $callback + * @param string $class + * @return bool false if the process was interrupted, true otherwise + */ + public function filterPackages($callback, $class = 'Composer\Package\Package'); + /** * Returns list of registered packages. * From 012798b17977f56bad9caf4ad3c1a6762e872b84 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Aug 2012 19:36:43 +0200 Subject: [PATCH 13/20] Convert search command to use the filterPackages method --- src/Composer/Command/SearchCommand.php | 86 +++++++++++++------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/src/Composer/Command/SearchCommand.php b/src/Composer/Command/SearchCommand.php index f212f0621..80bac3abe 100644 --- a/src/Composer/Command/SearchCommand.php +++ b/src/Composer/Command/SearchCommand.php @@ -26,6 +26,11 @@ use Composer\Factory; */ class SearchCommand extends Command { + protected $matches; + protected $lowMatches; + protected $tokens; + protected $output; + protected function configure() { $this @@ -58,59 +63,56 @@ EOT $repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos)); } - $tokens = $input->getArgument('tokens'); - $packages = array(); + $time = microtime(true); - $maxPackageLength = 0; - foreach ($repos->getPackages() as $package) { - if ($package instanceof AliasPackage || isset($packages[$package->getName()])) { + $this->tokens = $input->getArgument('tokens'); + $this->output = $output; + $repos->filterPackages(array($this, 'processPackage'), 'Composer\Package\CompletePackage'); + + foreach ($this->lowMatches as $details) { + $output->writeln($details['name'] . ': '. $details['description']); + } + + var_dump((memory_get_peak_usage() / 1024 / 1024) . 'MB memory, '.round(microtime(true) - $time, 2) .'secs'); + } + + public function processPackage($package) + { + if ($package instanceof AliasPackage || isset($this->matches[$package->getName()])) { + return; + } + + foreach ($this->tokens as $token) { + if (!$score = $this->matchPackage($package, $token)) { continue; } - foreach ($tokens as $token) { - if (!$score = $this->matchPackage($package, $token)) { - continue; - } + 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)); + } else { + $name = $package->getPrettyName(); + } - 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)); - } else { - $name = $package->getPrettyName(); - } + $description = strtok($package->getDescription(), "\r\n"); + if (false !== ($pos = stripos($description, $token))) { + $description = substr($description, 0, $pos) + . '' . substr($description, $pos, strlen($token)) . '' + . substr($description, $pos + strlen($token)); + } - $description = strtok($package->getDescription(), "\r\n"); - if (false !== ($pos = stripos($description, $token))) { - $description = substr($description, 0, $pos) - . '' . substr($description, $pos, strlen($token)) . '' - . substr($description, $pos + strlen($token)); - } - - $packages[$package->getName()] = array( + if ($score >= 3) { + $this->output->writeln($name . ': '. $description); + $this->matches[$package->getName()] = true; + } else { + $this->lowMatches[$package->getName()] = array( 'name' => $name, 'description' => $description, - 'length' => $length = strlen($package->getPrettyName()), - 'score' => $score, ); - - $maxPackageLength = max($maxPackageLength, $length); - - continue 2; - } - } - - usort($packages, function ($a, $b) { - if ($a['score'] === $b['score']) { - return 0; } - return $a['score'] > $b['score'] ? -1 : 1; - }); - - foreach ($packages as $details) { - $extraSpaces = $maxPackageLength - $details['length']; - $output->writeln($details['name'] . str_repeat(' ', $extraSpaces) .' : '. $details['description']); + return; } } From c31d588b7d39f990a03a33790fdf41a481b8c56a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Aug 2012 20:05:16 +0200 Subject: [PATCH 14/20] Update init and depends commands to use the new filterPackages method --- src/Composer/Command/DependsCommand.php | 75 ++++++++----------------- src/Composer/Command/InitCommand.php | 11 ++-- 2 files changed, 27 insertions(+), 59 deletions(-) diff --git a/src/Composer/Command/DependsCommand.php b/src/Composer/Command/DependsCommand.php index a8512c789..70108c09d 100644 --- a/src/Composer/Command/DependsCommand.php +++ b/src/Composer/Command/DependsCommand.php @@ -51,69 +51,38 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { $composer = $this->getComposer(); - $references = $this->getReferences($input, $output, $composer); - - if ($input->getOption('verbose')) { - $this->printReferences($input, $output, $references); - } else { - $this->printPackages($input, $output, $references); - } - } - - /** - * finds a list of packages which depend on another package - * - * @param InputInterface $input - * @param OutputInterface $output - * @param Composer $composer - * @return array - * @throws \InvalidArgumentException - */ - private function getReferences(InputInterface $input, OutputInterface $output, Composer $composer) - { - $needle = $input->getArgument('package'); - - $references = array(); - $verbose = (bool) $input->getOption('verbose'); - $repos = $composer->getRepositoryManager()->getRepositories(); - $types = $input->getOption('link-type'); - foreach ($repos as $repository) { - foreach ($repository->getPackages() as $package) { + $linkTypes = $this->linkTypes; + + $needle = $input->getArgument('package'); + $verbose = (bool) $input->getOption('verbose'); + $types = array_map(function ($type) use ($linkTypes) { + $type = rtrim($type, 's'); + if (!isset($linkTypes[$type])) { + throw new \InvalidArgumentException('Unexpected link type: '.$type.', valid types: '.implode(', ', array_keys($linkTypes))); + } + + return $type; + }, $input->getOption('link-type')); + + foreach ($repos as $repo) { + $repo->filterPackages(function ($package) use ($needle, $types, $output, $verbose) { + static $outputPackages = array(); + foreach ($types as $type) { - $type = rtrim($type, 's'); - if (!isset($this->linkTypes[$type])) { - throw new \InvalidArgumentException('Unexpected link type: '.$type.', valid types: '.implode(', ', array_keys($this->linkTypes))); - } foreach ($package->{'get'.$this->linkTypes[$type]}() as $link) { if ($link->getTarget() === $needle) { if ($verbose) { - $references[] = array($type, $package, $link); - } else { - $references[$package->getName()] = $package->getPrettyName(); + $output->writeln($package->getPrettyName() . ' ' . $package->getPrettyVersion() . ' ' . $type . ' ' . $link->getPrettyConstraint()); + } elseif (!isset($outputPackages[$package->getName()])) { + $output->writeln($package->getPrettyName()); + $outputPackages[$package->getName()] = true; } } } } - } - } - - return $references; - } - - private function printReferences(InputInterface $input, OutputInterface $output, array $references) - { - foreach ($references as $ref) { - $output->writeln($ref[1]->getPrettyName() . ' ' . $ref[1]->getPrettyVersion() . ' ' . $ref[0] . ' ' . $ref[2]->getPrettyConstraint()); - } - } - - private function printPackages(InputInterface $input, OutputInterface $output, array $packages) - { - ksort($packages); - foreach ($packages as $package) { - $output->writeln($package); + }); } } } diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 7fdb353d8..c19c523a2 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -267,13 +267,12 @@ EOT } $token = strtolower($name); - foreach ($this->repos->getPackages() as $package) { - if (false === ($pos = strpos($package->getName(), $token))) { - continue; - } - $packages[] = $package; - } + $this->repos->filterPackages(function ($package) use ($token, &$packages) { + if (false !== strpos($package->getName(), $token)) { + $packages[] = $package; + } + }); return $packages; } From a0903d4d355c867fcb11cf1a3f93fbc629d593b2 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Aug 2012 23:41:15 +0200 Subject: [PATCH 15/20] Update create-project and show commands to use the new filterPackages method --- src/Composer/Command/CreateProjectCommand.php | 27 ++++- src/Composer/Command/SearchCommand.php | 2 - src/Composer/Command/ShowCommand.php | 101 +++++++++++------- 3 files changed, 84 insertions(+), 46 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 6df45c229..c4fa7abf4 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -87,7 +87,7 @@ EOT ); } - public function installProject(IOInterface $io, $packageName, $directory = null, $version = null, $preferSource = false, $installDevPackages = false, $repositoryUrl = null, $disableCustomInstallers = false, $noScripts = false) + public function installProject(IOInterface $io, $packageName, $directory = null, $packageVersion = null, $preferSource = false, $installDevPackages = false, $repositoryUrl = null, $disableCustomInstallers = false, $noScripts = false) { $dm = $this->createDownloadManager($io); if ($preferSource) { @@ -105,9 +105,30 @@ EOT throw new \InvalidArgumentException("Invalid repository url given. Has to be a .json file or an http url."); } - $candidates = $sourceRepo->findPackages($packageName, $version); + $candidates = array(); + $name = strtolower($packageName); + + if ($packageVersion === null) { + $sourceRepo->filterPackages(function ($package) use (&$candidates, $name) { + if ($package->getName() === $name) { + $candidates[] = $package; + } + }); + } else { + $parser = new VersionParser(); + $version = $parser->normalize($packageVersion); + $sourceRepo->filterPackages(function ($package) use (&$candidates, $name, $version) { + if ($package->getName() === $name && $version === $package->getVersion()) { + $candidates[] = $package; + + return false; + } + if ($package->getName() === $name) {var_dump((string) $package);} + }); + } + if (!$candidates) { - throw new \InvalidArgumentException("Could not find package $packageName" . ($version ? " with version $version." : '')); + throw new \InvalidArgumentException("Could not find package $packageName" . ($packageVersion ? " with version $packageVersion." : '')); } if (null === $directory) { diff --git a/src/Composer/Command/SearchCommand.php b/src/Composer/Command/SearchCommand.php index 80bac3abe..a89f6b1dc 100644 --- a/src/Composer/Command/SearchCommand.php +++ b/src/Composer/Command/SearchCommand.php @@ -72,8 +72,6 @@ EOT foreach ($this->lowMatches as $details) { $output->writeln($details['name'] . ': '. $details['description']); } - - var_dump((memory_get_peak_usage() / 1024 / 1024) . 'MB memory, '.round(microtime(true) - $time, 2) .'secs'); } public function processPackage($package) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index a850da552..6be8fdc7c 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -15,6 +15,7 @@ namespace Composer\Command; use Composer\Composer; use Composer\Factory; use Composer\Package\CompletePackageInterface; +use Composer\Package\Version\VersionParser; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; @@ -38,9 +39,9 @@ class ShowCommand extends Command ->setDefinition(array( new InputArgument('package', InputArgument::OPTIONAL, 'Package to inspect'), new InputArgument('version', InputArgument::OPTIONAL, 'Version to inspect'), - new InputOption('installed', null, InputOption::VALUE_NONE, 'List installed packages only'), - new InputOption('platform', null, InputOption::VALUE_NONE, 'List platform packages only'), - new InputOption('self', null, InputOption::VALUE_NONE, 'Show the root package information'), + new InputOption('installed', 'i', InputOption::VALUE_NONE, 'List installed packages only'), + new InputOption('platform', 'p', InputOption::VALUE_NONE, 'List platform packages only'), + new InputOption('self', 's', InputOption::VALUE_NONE, 'Show the root package information'), )) ->setHelp(<<getArgument('package') || !empty($package)) { if (empty($package)) { - $package = $this->getPackage($input, $output, $installedRepo, $repos); - } - if (!$package) { - throw new \InvalidArgumentException('Package '.$input->getArgument('package').' not found'); + list($package, $versions) = $this->getPackage($installedRepo, $repos, $input->getArgument('package'), $input->getArgument('version')); + + if (!$package) { + throw new \InvalidArgumentException('Package '.$input->getArgument('package').' not found'); + } } - $this->printMeta($input, $output, $package, $installedRepo, $repos); + $this->printMeta($input, $output, $package, $versions, $installedRepo, $repos); $this->printLinks($input, $output, $package, 'requires'); $this->printLinks($input, $output, $package, 'devRequires', 'requires (dev)'); if ($package->getSuggests()) { @@ -101,7 +103,7 @@ EOT // list packages $packages = array(); - foreach ($repos->getPackages() as $package) { + $repos->filterPackages(function ($package) use (&$packages, $platformRepo, $installedRepo) { if ($platformRepo->hasPackage($package)) { $type = 'platform:'; } elseif ($installedRepo->hasPackage($package)) { @@ -109,13 +111,12 @@ EOT } else { $type = 'available:'; } - if (isset($packages[$type][$package->getName()]) - && version_compare($packages[$type][$package->getName()]->getVersion(), $package->getVersion(), '>=') + if (!isset($packages[$type][$package->getName()]) + || version_compare($packages[$type][$package->getName()]->getVersion(), $package->getVersion(), '<') ) { - continue; + $packages[$type][$package->getName()] = $package; } - $packages[$type][$package->getName()] = $package; - } + }, 'Composer\Package\CompletePackage'); foreach (array('platform:' => true, 'available:' => false, 'installed:' => true) as $type => $showVersion) { if (isset($packages[$type])) { @@ -132,44 +133,69 @@ EOT /** * finds a package by name and version if provided * - * @param InputInterface $input - * @return CompletePackageInterface + * @return array array(CompletePackageInterface, array of versions) * @throws \InvalidArgumentException */ - protected function getPackage(InputInterface $input, OutputInterface $output, RepositoryInterface $installedRepo, RepositoryInterface $repos) + protected function getPackage(RepositoryInterface $installedRepo, RepositoryInterface $repos, $name, $version = null) { - // we have a name and a version so we can use ::findPackage - if ($input->getArgument('version')) { - return $repos->findPackage($input->getArgument('package'), $input->getArgument('version')); + $name = strtolower($name); + if ($version) { + $parser = new VersionParser(); + $version = $parser->normalize($version); } - // check if we have a local installation so we can grab the right package/version - foreach ($installedRepo->getPackages() as $package) { - if ($package->getName() === $input->getArgument('package')) { - return $package; + $match = null; + $matches = array(); + $repos->filterPackages(function ($package) use ($name, $version, &$matches) { + if ($package->getName() === $name) { + $matches[] = $package; + } + }, 'Composer\Package\CompletePackage'); + + if (null === $version) { + // search for a locally installed version + foreach ($matches as $package) { + if ($installedRepo->hasPackage($package)) { + $match = $package; + break; + } + } + + if (!$match) { + // fallback to the highest version + foreach ($matches as $package) { + if (null === $match || version_compare($package->getVersion(), $match->getVersion(), '>=')) { + $match = $package; + } + } + } + } else { + // select the specified version + foreach ($matches as $package) { + if ($package->getVersion() === $version) { + $match = $package; + } } } - // we only have a name, so search for the highest version of the given package - $highestVersion = null; - foreach ($repos->findPackages($input->getArgument('package')) as $package) { - if (null === $highestVersion || version_compare($package->getVersion(), $highestVersion->getVersion(), '>=')) { - $highestVersion = $package; - } + // build versions array + $versions = array(); + foreach ($matches as $package) { + $versions[$package->getPrettyVersion()] = $package->getVersion(); } - return $highestVersion; + return array($match, $versions); } /** * prints package meta data */ - protected function printMeta(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, RepositoryInterface $installedRepo, RepositoryInterface $repos) + protected function printMeta(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, RepositoryInterface $repos) { $output->writeln('name : ' . $package->getPrettyName()); $output->writeln('descrip. : ' . $package->getDescription()); $output->writeln('keywords : ' . join(', ', $package->getKeywords() ?: array())); - $this->printVersions($input, $output, $package, $installedRepo, $repos); + $this->printVersions($input, $output, $package, $versions, $installedRepo, $repos); $output->writeln('type : ' . $package->getType()); $output->writeln('license : ' . implode(', ', $package->getLicense())); $output->writeln('source : ' . sprintf('[%s] %s %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference())); @@ -206,7 +232,7 @@ EOT /** * prints all available versions of this package and highlights the installed one if any */ - protected function printVersions(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, RepositoryInterface $installedRepo, RepositoryInterface $repos) + protected function printVersions(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, RepositoryInterface $repos) { if ($input->getArgument('version')) { $output->writeln('version : ' . $package->getPrettyVersion()); @@ -214,14 +240,7 @@ EOT return; } - $versions = array(); - - foreach ($repos->findPackages($package->getName()) as $version) { - $versions[$version->getPrettyVersion()] = $version->getVersion(); - } - uasort($versions, 'version_compare'); - $versions = implode(', ', array_keys(array_reverse($versions))); // highlight installed version From e1bd2fd6dfdc57cdaec1ad5fae916396cc493113 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 24 Aug 2012 00:14:54 +0200 Subject: [PATCH 16/20] Clean ups after feedback --- src/Composer/Command/CreateProjectCommand.php | 1 - src/Composer/Command/SearchCommand.php | 2 -- src/Composer/Repository/RepositoryInterface.php | 2 +- src/Composer/Repository/StreamableRepositoryInterface.php | 6 ++++++ 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index c4fa7abf4..9ed7cd44a 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -123,7 +123,6 @@ EOT return false; } - if ($package->getName() === $name) {var_dump((string) $package);} }); } diff --git a/src/Composer/Command/SearchCommand.php b/src/Composer/Command/SearchCommand.php index a89f6b1dc..1b3a535a0 100644 --- a/src/Composer/Command/SearchCommand.php +++ b/src/Composer/Command/SearchCommand.php @@ -63,8 +63,6 @@ EOT $repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos)); } - $time = microtime(true); - $this->tokens = $input->getArgument('tokens'); $this->output = $output; $repos->filterPackages(array($this, 'processPackage'), 'Composer\Package\CompletePackage'); diff --git a/src/Composer/Repository/RepositoryInterface.php b/src/Composer/Repository/RepositoryInterface.php index 25bc3edb7..762164dad 100644 --- a/src/Composer/Repository/RepositoryInterface.php +++ b/src/Composer/Repository/RepositoryInterface.php @@ -52,7 +52,7 @@ interface RepositoryInterface extends \Countable public function findPackages($name, $version = null); /** - * Filters all the packages throuhg a callback + * Filters all the packages through a callback * * The packages are not guaranteed to be instances in the repository * and this can only be used for streaming through a list of packages. diff --git a/src/Composer/Repository/StreamableRepositoryInterface.php b/src/Composer/Repository/StreamableRepositoryInterface.php index 36439932c..f5c694642 100644 --- a/src/Composer/Repository/StreamableRepositoryInterface.php +++ b/src/Composer/Repository/StreamableRepositoryInterface.php @@ -23,6 +23,8 @@ interface StreamableRepositoryInterface extends RepositoryInterface /** * Return partial package data without loading them all to save on memory * + * The function must return an array of package arrays. + * * The package array must contain the following fields: * - name: package name (normalized/lowercased) * - repo: reference to the repository instance @@ -32,6 +34,10 @@ interface StreamableRepositoryInterface extends RepositoryInterface * - alias: pretty alias that this package should be aliased to, optional * - alias_normalized: normalized alias that this package should be aliased to, optional * + * Any additional information can be returned and will be sent back + * into loadPackage/loadAliasPackage for completing the package loading + * when it's needed. + * * @return array */ public function getMinimalPackages(); From c14bc368b031438cf3efb1f9e9d5589d13d879d4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 24 Aug 2012 02:29:03 +0200 Subject: [PATCH 17/20] Fix memory usage of the update command --- src/Composer/Installer.php | 9 ++++++++- src/Composer/Repository/ComposerRepository.php | 15 ++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 282fafb78..1987db300 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -391,7 +391,14 @@ class Installer continue; } - $newPackage = $this->repositoryManager->findPackage($package->getName(), $package->getVersion()); + $newPackage = null; + $matches = $pool->whatProvides($package->getName(), new VersionConstraint('=', $package->getVersion())); + foreach ($matches as $match) { + if (null === $newPackage || $newPackage->getReleaseDate() < $match->getReleaseDate()) { + $newPackage = $match; + } + } + if ($newPackage && $newPackage->getSourceReference() !== $package->getSourceReference()) { $operations[] = new UpdateOperation($package, $newPackage); } diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index ac1f3ac44..99c8bbd9a 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -31,8 +31,9 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository protected $io; protected $cache; protected $notifyUrl; - protected $minimalPackages; protected $loader; + private $rawData; + private $minimalPackages; public function __construct(array $repoConfig, IOInterface $io, Config $config) { @@ -90,12 +91,14 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository return $this->minimalPackages; } - $repoData = $this->loadDataFromServer(); + if (null === $this->rawData) { + $this->rawData = $this->loadDataFromServer(); + } $this->minimalPackages = array(); $versionParser = new VersionParser; - foreach ($repoData as $package) { + foreach ($this->rawData as $package) { $version = !empty($package['version_normalized']) ? $package['version_normalized'] : $versionParser->normalize($package['version']); $data = array( 'name' => strtolower($package['name']), @@ -127,9 +130,11 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository */ public function filterPackages($callback, $class = 'Composer\Package\Package') { - $repoData = $this->loadDataFromServer(); + if (null === $this->rawData) { + $this->rawData = $this->loadDataFromServer(); + } - foreach ($repoData as $package) { + foreach ($this->rawData as $package) { if (false === $callback($package = $this->loader->load($package, $class))) { return false; } From f9f86f03ae37ce7efc76b55e4df2a82a905d37e7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 24 Aug 2012 11:38:42 +0200 Subject: [PATCH 18/20] Performance improvements to create-project --- src/Composer/Command/CreateProjectCommand.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 9ed7cd44a..22ae23f73 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -142,6 +142,7 @@ EOT $package = $candidate; } } + unset($candidates); $io->write('Installing ' . $package->getName() . ' (' . VersionParser::formatVersion($package, false) . ')', true); @@ -164,6 +165,10 @@ EOT putenv('COMPOSER_ROOT_VERSION='.$package->getPrettyVersion()); + // clean up memory + unset($dm, $config, $projectInstaller, $sourceRepo, $package); + + // install dependencies of the created project $composer = Factory::create($io); $installer = Installer::create($io, $composer); From c97720c6074bc13fabfbbe7f2a1b8c11d063f303 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 24 Aug 2012 12:06:29 +0200 Subject: [PATCH 19/20] Fix picking of new dev packages for updates to latest source ref --- src/Composer/Installer.php | 15 ++++++++++++--- .../Fixtures/installer/update-alias-lock.test | 18 ++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 1987db300..6c1761134 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -247,7 +247,8 @@ class Installer // creating repository pool $pool = new Pool($minimumStability, $stabilityFlags); $pool->addRepository($installedRepo, $aliases); - foreach ($this->repositoryManager->getRepositories() as $repository) { + $repositories = $this->repositoryManager->getRepositories(); + foreach ($repositories as $repository) { $pool->addRepository($repository, $aliases); } @@ -360,6 +361,10 @@ class Installer continue; } + if ($package instanceof AliasPackage) { + continue; + } + // skip packages that will be updated/uninstalled foreach ($operations as $operation) { if (('update' === $operation->getJobType() && $operation->getInitialPackage()->equals($package)) @@ -394,9 +399,13 @@ class Installer $newPackage = null; $matches = $pool->whatProvides($package->getName(), new VersionConstraint('=', $package->getVersion())); foreach ($matches as $match) { - if (null === $newPackage || $newPackage->getReleaseDate() < $match->getReleaseDate()) { - $newPackage = $match; + // skip local packages + if (!in_array($match->getRepository(), $repositories, true)) { + continue; } + + $newPackage = $match; + break; } if ($newPackage && $newPackage->getSourceReference() !== $package->getSourceReference()) { diff --git a/tests/Composer/Test/Fixtures/installer/update-alias-lock.test b/tests/Composer/Test/Fixtures/installer/update-alias-lock.test index cc69f17d1..03744bcfd 100644 --- a/tests/Composer/Test/Fixtures/installer/update-alias-lock.test +++ b/tests/Composer/Test/Fixtures/installer/update-alias-lock.test @@ -1,5 +1,5 @@ --TEST-- -Update aliased package to non-aliased version +Update aliased package does not mess up the lock file --COMPOSER-- { "repositories": [ @@ -12,12 +12,22 @@ Update aliased package to non-aliased version "source": { "reference": "master", "type": "git", "url": "" } } ] + }, + { + "type": "package", + "package": [ + { + "name": "a/a", "version": "dev-master", + "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, + "source": { "reference": "lowpriomaster", "type": "git", "url": "" } + } + ] } ], "require": { "a/a": "1.*" }, - "minimum-stability": "stable" + "minimum-stability": "dev" } --LOCK-- { @@ -27,7 +37,7 @@ Update aliased package to non-aliased version ], "packages-dev": null, "aliases": [], - "minimum-stability": "stable", + "minimum-stability": "dev", "stability-flags": [] } --INSTALLED-- @@ -48,7 +58,7 @@ update ], "packages-dev": null, "aliases": [], - "minimum-stability": "stable", + "minimum-stability": "dev", "stability-flags": [] } --EXPECT-- From 6f77df296aa9e7cd529aa3623f60d48009fc5164 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 24 Aug 2012 16:46:08 +0200 Subject: [PATCH 20/20] Speed up Pool::match for common case --- src/Composer/DependencyResolver/Pool.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 955be6939..520b0c6e0 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -339,6 +339,17 @@ class Pool if (is_array($candidate)) { $candidateName = $candidate['name']; $candidateVersion = $candidate['version']; + } else { + // handle object packages + $candidateName = $candidate->getName(); + $candidateVersion = $candidate->getVersion(); + } + + if ($candidateName === $name) { + return $constraint->matches(new VersionConstraint('==', $candidateVersion)) ? self::MATCH : self::MATCH_NAME; + } + + if (is_array($candidate)) { $provides = isset($candidate['provide']) ? $this->versionParser->parseLinks($candidateName, $candidateVersion, 'provides', $candidate['provide']) : array(); @@ -346,17 +357,10 @@ class Pool ? $this->versionParser->parseLinks($candidateName, $candidateVersion, 'replaces', $candidate['replace']) : array(); } else { - // handle object packages - $candidateName = $candidate->getName(); - $candidateVersion = $candidate->getVersion(); $provides = $candidate->getProvides(); $replaces = $candidate->getReplaces(); } - if ($candidateName === $name) { - return $constraint->matches(new VersionConstraint('==', $candidateVersion)) ? self::MATCH : self::MATCH_NAME; - } - foreach ($provides as $link) { if ($link->getTarget() === $name && $constraint->matches($link->getConstraint())) { return self::MATCH_PROVIDE;