From c8a685be6b0fc0f8c1badd933c9fd67c009a490f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Aug 2012 15:44:45 +0200 Subject: [PATCH] Reduce memory usage by only loading packages that are actually needed, fixes #456 --- src/Composer/DependencyResolver/Pool.php | 178 +++++++++-- 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, 597 insertions(+), 359 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; - - foreach ($package->getNames() as $name) { - $this->packageByName[$name][] = $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; + + // 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 - * - * @param RepositoryInterface $repository - */ - public function setRepository(RepositoryInterface $repository); - - /** - * Returns a reference to the repository that owns the package + * Returns the package license, e.g. MIT, BSD, GPL * - * @return RepositoryInterface + * @return array The package licenses */ - 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); +}