2011-04-17 22:14:44 +00:00
< ? php
/*
* This file is part of Composer .
*
* ( c ) Nils Adermann < naderman @ naderman . de >
* Jordi Boggiano < j . boggiano @ seld . be >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Composer\Repository ;
2011-09-20 21:34:06 +00:00
use Composer\Package\Loader\ArrayLoader ;
2012-04-13 00:58:40 +00:00
use Composer\Package\PackageInterface ;
2012-10-13 16:54:48 +00:00
use Composer\Package\AliasPackage ;
2016-02-24 17:27:42 +00:00
use Composer\Package\Version\VersionParser ;
2011-10-01 13:01:33 +00:00
use Composer\Json\JsonFile ;
2012-04-06 20:39:43 +00:00
use Composer\Cache ;
2012-04-09 14:36:06 +00:00
use Composer\Config ;
2014-03-01 17:01:44 +00:00
use Composer\Factory ;
2012-03-18 20:04:15 +00:00
use Composer\IO\IOInterface ;
2018-09-12 16:58:54 +00:00
use Composer\Util\HttpDownloader ;
2013-11-19 16:45:28 +00:00
use Composer\Plugin\PluginEvents ;
use Composer\Plugin\PreFileDownloadEvent ;
use Composer\EventDispatcher\EventDispatcher ;
2016-01-18 12:25:37 +00:00
use Composer\Downloader\TransportException ;
2015-09-24 14:32:36 +00:00
use Composer\Semver\Constraint\ConstraintInterface ;
use Composer\Semver\Constraint\Constraint ;
2018-11-12 16:21:23 +00:00
use Composer\Semver\Constraint\EmptyConstraint ;
2011-04-17 22:14:44 +00:00
/**
* @ author Jordi Boggiano < j . boggiano @ seld . be >
*/
2015-11-23 22:18:24 +00:00
class ComposerRepository extends ArrayRepository implements ConfigurableRepositoryInterface
2011-04-17 22:14:44 +00:00
{
2012-04-13 00:58:40 +00:00
protected $config ;
2015-11-23 22:18:24 +00:00
protected $repoConfig ;
2012-10-03 09:56:31 +00:00
protected $options ;
2011-09-20 21:34:06 +00:00
protected $url ;
2012-10-14 14:33:53 +00:00
protected $baseUrl ;
2012-03-18 20:04:15 +00:00
protected $io ;
2018-09-12 16:58:54 +00:00
protected $httpDownloader ;
2012-04-06 20:39:43 +00:00
protected $cache ;
2012-04-13 00:58:40 +00:00
protected $notifyUrl ;
2013-03-10 12:27:49 +00:00
protected $searchUrl ;
2012-10-14 14:33:53 +00:00
protected $hasProviders = false ;
2013-02-21 15:50:04 +00:00
protected $providersUrl ;
2013-07-01 23:46:20 +00:00
protected $lazyProvidersUrl ;
2012-10-14 14:33:53 +00:00
protected $providerListing ;
2012-10-01 11:58:01 +00:00
protected $providers = array ();
protected $providersByUid = array ();
2012-08-20 13:44:45 +00:00
protected $loader ;
2012-10-24 14:11:32 +00:00
protected $rootAliases ;
2013-02-21 16:37:18 +00:00
protected $allowSslDowngrade = false ;
2013-11-19 16:45:28 +00:00
protected $eventDispatcher ;
2013-07-01 23:45:43 +00:00
protected $sourceMirrors ;
protected $distMirrors ;
2012-10-11 19:26:11 +00:00
private $degradedMode = false ;
2012-10-01 11:58:01 +00:00
private $rootData ;
2016-11-30 21:34:59 +00:00
private $hasPartialPackages ;
private $partialPackagesByName ;
2018-09-12 16:58:54 +00:00
private $versionParser ;
2011-04-17 22:14:44 +00:00
2018-11-12 14:34:54 +00:00
public function __construct ( array $repoConfig , IOInterface $io , Config $config , HttpDownloader $httpDownloader , EventDispatcher $eventDispatcher = null )
2011-04-17 22:14:44 +00:00
{
2016-02-27 21:39:03 +00:00
parent :: __construct ();
2012-10-11 18:54:59 +00:00
if ( ! preg_match ( '{^[\w.]+\??://}' , $repoConfig [ 'url' ])) {
2011-12-12 09:57:57 +00:00
// assume http as the default protocol
2012-04-09 14:36:06 +00:00
$repoConfig [ 'url' ] = 'http://' . $repoConfig [ 'url' ];
2011-12-03 11:43:38 +00:00
}
2012-04-09 14:36:06 +00:00
$repoConfig [ 'url' ] = rtrim ( $repoConfig [ 'url' ], '/' );
2012-10-11 18:54:59 +00:00
if ( 'https?' === substr ( $repoConfig [ 'url' ], 0 , 6 )) {
$repoConfig [ 'url' ] = ( extension_loaded ( 'openssl' ) ? 'https' : 'http' ) . substr ( $repoConfig [ 'url' ], 6 );
}
2012-11-05 14:39:43 +00:00
$urlBits = parse_url ( $repoConfig [ 'url' ]);
2013-10-30 16:46:35 +00:00
if ( $urlBits === false || empty ( $urlBits [ 'scheme' ])) {
2012-04-09 14:36:06 +00:00
throw new \UnexpectedValueException ( 'Invalid url given for Composer repository: ' . $repoConfig [ 'url' ]);
2011-09-20 21:34:06 +00:00
}
2012-10-03 09:56:31 +00:00
if ( ! isset ( $repoConfig [ 'options' ])) {
$repoConfig [ 'options' ] = array ();
}
2013-02-21 16:37:18 +00:00
if ( isset ( $repoConfig [ 'allow_ssl_downgrade' ]) && true === $repoConfig [ 'allow_ssl_downgrade' ]) {
$this -> allowSslDowngrade = true ;
}
2012-10-03 09:56:31 +00:00
2012-04-13 00:58:40 +00:00
$this -> config = $config ;
2012-10-03 09:56:31 +00:00
$this -> options = $repoConfig [ 'options' ];
2012-04-09 14:36:06 +00:00
$this -> url = $repoConfig [ 'url' ];
2018-07-24 07:30:06 +00:00
// force url for packagist.org to repo.packagist.org
2018-07-24 16:20:04 +00:00
if ( preg_match ( '{^(?P<proto>https?)://packagist\.org/?$}i' , $this -> url , $match )) {
2018-07-24 07:30:06 +00:00
$this -> url = $match [ 'proto' ] . '://repo.packagist.org' ;
}
2016-09-17 11:27:29 +00:00
$this -> baseUrl = rtrim ( preg_replace ( '{(?:/[^/\\\\]+\.json)?(?:[?#].*)?$}' , '' , $this -> url ), '/' );
2012-03-18 20:04:15 +00:00
$this -> io = $io ;
2013-02-21 15:50:04 +00:00
$this -> cache = new Cache ( $io , $config -> get ( 'cache-repo-dir' ) . '/' . preg_replace ( '{[^a-z0-9.]}i' , '-' , $this -> url ), 'a-z0-9.$' );
2018-09-12 16:58:54 +00:00
$this -> versionParser = new VersionParser ();
$this -> loader = new ArrayLoader ( $this -> versionParser );
2018-11-12 14:34:54 +00:00
if ( $this -> options ) {
2018-10-31 11:44:54 +00:00
// TODO solve this somehow - should be sent at request time not on the instance
2018-09-12 16:58:54 +00:00
$httpDownloader = clone $httpDownloader ;
$httpDownloader -> setOptions ( $this -> options );
}
$this -> httpDownloader = $httpDownloader ;
2013-11-19 16:45:28 +00:00
$this -> eventDispatcher = $eventDispatcher ;
2015-11-23 22:18:24 +00:00
$this -> repoConfig = $repoConfig ;
}
public function getRepoConfig ()
{
return $this -> repoConfig ;
2011-04-17 22:14:44 +00:00
}
2012-10-24 14:11:32 +00:00
public function setRootAliases ( array $rootAliases )
{
$this -> rootAliases = $rootAliases ;
}
2014-05-13 21:54:48 +00:00
/**
* { @ inheritDoc }
*/
2015-06-18 16:44:58 +00:00
public function findPackage ( $name , $constraint )
2013-03-10 12:33:41 +00:00
{
2018-11-12 16:44:18 +00:00
// this call initializes loadRootServerFile which is needed for the rest below to work
$hasProviders = $this -> hasProviders ();
2015-06-18 16:44:58 +00:00
2014-05-13 21:54:48 +00:00
$name = strtolower ( $name );
2015-09-24 14:32:36 +00:00
if ( ! $constraint instanceof ConstraintInterface ) {
2018-09-12 16:58:54 +00:00
$constraint = $this -> versionParser -> parseConstraints ( $constraint );
2015-06-18 16:44:58 +00:00
}
2014-05-13 21:54:48 +00:00
2018-11-12 16:44:18 +00:00
// TODO we need a new way for the repo to report this v2 protocol somehow
if ( $this -> lazyProvidersUrl ) {
return $this -> loadAsyncPackages ( array ( $name => $constraint ), function ( $name , $stability ) {
return true ;
});
}
if ( ! $hasProviders ) {
return parent :: findPackage ( $name , $constraint );
}
2014-05-13 21:54:48 +00:00
foreach ( $this -> getProviderNames () as $providerName ) {
if ( $name === $providerName ) {
2018-09-11 11:33:29 +00:00
$packages = $this -> whatProvides ( $providerName );
2014-05-13 21:54:48 +00:00
foreach ( $packages as $package ) {
2015-06-18 16:44:58 +00:00
if ( $name === $package -> getName ()) {
2015-09-24 14:32:36 +00:00
$pkgConstraint = new Constraint ( '==' , $package -> getVersion ());
2015-06-18 16:44:58 +00:00
if ( $constraint -> matches ( $pkgConstraint )) {
return $package ;
}
2014-05-13 21:54:48 +00:00
}
}
2016-01-20 06:29:32 +00:00
break ;
2014-05-13 21:54:48 +00:00
}
2013-03-10 12:33:41 +00:00
}
}
2012-08-22 15:47:05 +00:00
/**
* { @ inheritDoc }
*/
2015-06-18 16:44:58 +00:00
public function findPackages ( $name , $constraint = null )
2012-08-20 13:44:45 +00:00
{
2018-11-12 16:21:23 +00:00
// this call initializes loadRootServerFile which is needed for the rest below to work
$hasProviders = $this -> hasProviders ();
// TODO we need a new way for the repo to report this v2 protocol somehow
if ( $this -> lazyProvidersUrl ) {
2018-11-12 16:44:18 +00:00
return $this -> loadAsyncPackages ( array ( $name => $constraint ? : new EmptyConstraint ()), function ( $name , $stability ) {
2018-11-12 16:21:23 +00:00
return true ;
});
}
if ( ! $hasProviders ) {
2015-06-18 16:44:58 +00:00
return parent :: findPackages ( $name , $constraint );
2012-08-20 13:44:45 +00:00
}
2018-11-12 16:21:23 +00:00
2014-05-13 21:54:48 +00:00
// normalize name
$name = strtolower ( $name );
2012-08-20 13:44:45 +00:00
2015-09-24 14:32:36 +00:00
if ( null !== $constraint && ! $constraint instanceof ConstraintInterface ) {
2018-09-12 16:58:54 +00:00
$constraint = $this -> versionParser -> parseConstraints ( $constraint );
2012-08-24 00:29:03 +00:00
}
2012-08-20 13:44:45 +00:00
2014-05-13 21:54:48 +00:00
$packages = array ();
2012-08-20 13:44:45 +00:00
2014-05-13 21:54:48 +00:00
foreach ( $this -> getProviderNames () as $providerName ) {
if ( $name === $providerName ) {
2018-09-11 12:40:37 +00:00
$candidates = $this -> whatProvides ( $providerName );
2015-06-18 16:44:58 +00:00
foreach ( $candidates as $package ) {
if ( $name === $package -> getName ()) {
2015-09-24 14:32:36 +00:00
$pkgConstraint = new Constraint ( '==' , $package -> getVersion ());
2015-06-18 16:44:58 +00:00
if ( null === $constraint || $constraint -> matches ( $pkgConstraint )) {
$packages [] = $package ;
}
2014-05-13 21:54:48 +00:00
}
}
2016-01-20 06:29:32 +00:00
break ;
2012-08-20 13:44:45 +00:00
}
2014-05-13 21:54:48 +00:00
}
2012-08-20 13:44:45 +00:00
2014-05-13 21:54:48 +00:00
return $packages ;
}
2012-08-22 12:20:43 +00:00
2013-03-10 12:33:41 +00:00
public function getPackages ()
{
if ( $this -> hasProviders ()) {
throw new \LogicException ( 'Composer repositories that have providers can not load the complete list of packages, use getProviderNames instead.' );
2012-08-20 13:44:45 +00:00
}
2013-03-10 12:33:41 +00:00
return parent :: getPackages ();
2012-08-20 13:44:45 +00:00
}
2018-09-12 12:11:26 +00:00
public function loadPackages ( array $packageNameMap , $isPackageAcceptableCallable )
{
2018-11-12 16:21:23 +00:00
// this call initializes loadRootServerFile which is needed for the rest below to work
$hasProviders = $this -> hasProviders ();
// TODO we need a new way for the repo to report this v2 protocol somehow
2018-09-12 16:58:54 +00:00
if ( $this -> lazyProvidersUrl ) {
return $this -> loadAsyncPackages ( $packageNameMap , $isPackageAcceptableCallable );
}
2018-11-12 16:21:23 +00:00
if ( ! $hasProviders ) {
2018-09-12 12:11:26 +00:00
return parent :: loadPackages ( $packageNameMap , $isPackageAcceptableCallable );
}
$packages = array ();
2018-09-12 14:32:14 +00:00
foreach ( $packageNameMap as $name => $constraint ) {
2018-09-12 12:11:26 +00:00
$matches = array ();
2018-09-12 14:32:14 +00:00
$candidates = $this -> whatProvides ( $name , false , $isPackageAcceptableCallable );
foreach ( $candidates as $candidate ) {
if ( $candidate -> getName () === $name && ( ! $constraint || $constraint -> matches ( new Constraint ( '==' , $candidate -> getVersion ())))) {
$matches [ spl_object_hash ( $candidate )] = $candidate ;
if ( $candidate instanceof AliasPackage && ! isset ( $matches [ spl_object_hash ( $candidate -> getAliasOf ())])) {
$matches [ spl_object_hash ( $candidate -> getAliasOf ())] = $candidate -> getAliasOf ();
}
}
}
foreach ( $candidates as $candidate ) {
if ( $candidate instanceof AliasPackage ) {
if ( isset ( $result [ spl_object_hash ( $candidate -> getAliasOf ())])) {
$matches [ spl_object_hash ( $candidate )] = $candidate ;
}
2018-09-12 12:11:26 +00:00
}
}
$packages = array_merge ( $packages , $matches );
}
2018-11-12 16:21:23 +00:00
2018-09-12 12:11:26 +00:00
return $packages ;
}
2012-08-23 17:16:23 +00:00
/**
* { @ inheritDoc }
*/
2016-06-21 14:38:52 +00:00
public function search ( $query , $mode = 0 , $type = null )
2012-08-23 17:16:23 +00:00
{
2013-03-10 12:32:59 +00:00
$this -> loadRootServerFile ();
if ( $this -> searchUrl && $mode === self :: SEARCH_FULLTEXT ) {
2016-06-21 14:38:52 +00:00
$url = str_replace ( array ( '%query%' , '%type%' ), array ( $query , $type ), $this -> searchUrl );
2013-03-10 12:32:59 +00:00
2018-09-12 16:58:54 +00:00
$search = $this -> httpDownloader -> get ( $url ) -> decodeJson ();
2013-03-10 12:32:59 +00:00
2018-05-15 14:06:56 +00:00
if ( empty ( $search [ 'results' ])) {
return array ();
}
2018-05-15 13:20:18 +00:00
$results = array ();
foreach ( $search [ 'results' ] as $result ) {
// do not show virtual packages in results as they are not directly useful from a composer perspective
if ( empty ( $result [ 'virtual' ])) {
$results [] = $result ;
}
}
return $results ;
2012-08-24 00:29:03 +00:00
}
2012-08-23 17:16:23 +00:00
2013-03-10 12:32:59 +00:00
if ( $this -> hasProviders ()) {
$results = array ();
$regex = '{(?:' . implode ( '|' , preg_split ( '{\s+}' , $query )) . ')}i' ;
foreach ( $this -> getProviderNames () as $name ) {
if ( preg_match ( $regex , $name )) {
$results [] = array ( 'name' => $name );
2012-08-23 17:16:23 +00:00
}
}
2013-03-10 12:32:59 +00:00
return $results ;
}
return parent :: search ( $query , $mode );
}
public function getProviderNames ()
{
$this -> loadRootServerFile ();
if ( null === $this -> providerListing ) {
$this -> loadProviderListings ( $this -> loadRootServerFile ());
}
2015-01-31 21:24:33 +00:00
if ( $this -> lazyProvidersUrl ) {
// Can not determine list of provided packages for lazy repositories
return array ();
}
2013-03-10 12:32:59 +00:00
if ( $this -> providersUrl ) {
return array_keys ( $this -> providerListing );
}
2016-02-15 20:37:19 +00:00
return array ();
2012-08-23 17:16:23 +00:00
}
2014-05-07 17:10:55 +00:00
protected function configurePackageTransportOptions ( PackageInterface $package )
2012-08-22 15:47:05 +00:00
{
2014-05-07 17:12:58 +00:00
foreach ( $package -> getDistUrls () as $url ) {
if ( strpos ( $url , $this -> baseUrl ) === 0 ) {
$package -> setTransportOptions ( $this -> options );
2012-08-22 15:47:05 +00:00
2014-05-07 17:12:58 +00:00
return ;
}
2013-08-19 07:40:54 +00:00
}
2012-08-22 15:47:05 +00:00
}
2012-10-01 11:58:01 +00:00
public function hasProviders ()
{
$this -> loadRootServerFile ();
2012-10-14 14:33:53 +00:00
return $this -> hasProviders ;
2012-10-01 11:58:01 +00:00
}
2016-03-27 17:39:36 +00:00
/**
2018-09-11 11:33:29 +00:00
* @ param string $name package name
* @ param bool $bypassFilters If set to true , this bypasses the stability filtering , and forces a recompute without cache
* @ param callable $isPackageAcceptableCallable
2016-04-06 23:12:30 +00:00
* @ return array | mixed
2016-03-27 17:39:36 +00:00
*/
2018-09-11 11:33:29 +00:00
public function whatProvides ( $name , $bypassFilters = false , $isPackageAcceptableCallable = null )
2012-10-01 11:58:01 +00:00
{
2016-03-27 17:39:36 +00:00
if ( isset ( $this -> providers [ $name ]) && ! $bypassFilters ) {
2012-10-01 11:58:01 +00:00
return $this -> providers [ $name ];
}
2016-11-30 21:34:59 +00:00
if ( $this -> hasPartialPackages && null === $this -> partialPackagesByName ) {
$this -> initializePartialPackages ();
2012-10-14 14:33:53 +00:00
}
2012-10-01 11:58:01 +00:00
2016-11-30 21:34:59 +00:00
if ( ! $this -> hasPartialPackages || ! isset ( $this -> partialPackagesByName [ $name ])) {
// skip platform packages, root package and composer-plugin-api
if ( preg_match ( PlatformRepository :: PLATFORM_PACKAGE_REGEX , $name ) || '__root__' === $name || 'composer-plugin-api' === $name ) {
2013-02-21 15:50:04 +00:00
return array ();
}
2012-10-14 14:33:53 +00:00
2016-11-30 21:34:59 +00:00
if ( null === $this -> providerListing ) {
$this -> loadProviderListings ( $this -> loadRootServerFile ());
}
2012-10-14 14:33:53 +00:00
2016-11-30 21:34:59 +00:00
$useLastModifiedCheck = false ;
if ( $this -> lazyProvidersUrl && ! isset ( $this -> providerListing [ $name ])) {
$hash = null ;
$url = str_replace ( '%package%' , $name , $this -> lazyProvidersUrl );
$cacheKey = 'provider-' . strtr ( $name , '/' , '$' ) . '.json' ;
$useLastModifiedCheck = true ;
} elseif ( $this -> providersUrl ) {
// package does not exist in this repo
if ( ! isset ( $this -> providerListing [ $name ])) {
return array ();
}
$hash = $this -> providerListing [ $name ][ 'sha256' ];
$url = str_replace ( array ( '%package%' , '%hash%' ), array ( $name , $hash ), $this -> providersUrl );
$cacheKey = 'provider-' . strtr ( $name , '/' , '$' ) . '.json' ;
} else {
return array ();
}
$packages = null ;
if ( $cacheKey ) {
if ( ! $useLastModifiedCheck && $hash && $this -> cache -> sha256 ( $cacheKey ) === $hash ) {
$packages = json_decode ( $this -> cache -> read ( $cacheKey ), true );
} elseif ( $useLastModifiedCheck ) {
if ( $contents = $this -> cache -> read ( $cacheKey )) {
$contents = json_decode ( $contents , true );
if ( isset ( $contents [ 'last-modified' ])) {
$response = $this -> fetchFileIfLastModified ( $url , $cacheKey , $contents [ 'last-modified' ]);
if ( true === $response ) {
$packages = $contents ;
} elseif ( $response ) {
$packages = $response ;
}
2016-01-18 12:25:37 +00:00
}
}
}
}
2016-11-30 21:34:59 +00:00
if ( ! $packages ) {
try {
$packages = $this -> fetchFile ( $url , $cacheKey , $hash , $useLastModifiedCheck );
} catch ( TransportException $e ) {
// 404s are acceptable for lazy provider repos
if ( $e -> getStatusCode () === 404 && $this -> lazyProvidersUrl ) {
$packages = array ( 'packages' => array ());
} else {
throw $e ;
}
2016-01-18 12:25:37 +00:00
}
}
2016-11-30 21:34:59 +00:00
$loadingPartialPackage = false ;
} else {
$packages = array ( 'packages' => array ( 'versions' => $this -> partialPackagesByName [ $name ]));
$loadingPartialPackage = true ;
2012-10-01 11:58:01 +00:00
}
$this -> providers [ $name ] = array ();
2012-10-13 16:54:48 +00:00
foreach ( $packages [ 'packages' ] as $versions ) {
2012-10-01 11:58:01 +00:00
foreach ( $versions as $version ) {
2016-11-30 21:34:59 +00:00
if ( ! $loadingPartialPackage && $this -> hasPartialPackages && isset ( $this -> partialPackagesByName [ $version [ 'name' ]])) {
continue ;
}
2012-10-13 16:54:48 +00:00
// avoid loading the same objects twice
2012-10-01 11:58:01 +00:00
if ( isset ( $this -> providersByUid [ $version [ 'uid' ]])) {
2012-10-13 16:54:48 +00:00
// skip if already assigned
if ( ! isset ( $this -> providers [ $name ][ $version [ 'uid' ]])) {
// expand alias in two packages
if ( $this -> providersByUid [ $version [ 'uid' ]] instanceof AliasPackage ) {
$this -> providers [ $name ][ $version [ 'uid' ]] = $this -> providersByUid [ $version [ 'uid' ]] -> getAliasOf ();
$this -> providers [ $name ][ $version [ 'uid' ] . '-alias' ] = $this -> providersByUid [ $version [ 'uid' ]];
} else {
$this -> providers [ $name ][ $version [ 'uid' ]] = $this -> providersByUid [ $version [ 'uid' ]];
}
2012-10-24 14:11:32 +00:00
// check for root aliases
if ( isset ( $this -> providersByUid [ $version [ 'uid' ] . '-root' ])) {
$this -> providers [ $name ][ $version [ 'uid' ] . '-root' ] = $this -> providersByUid [ $version [ 'uid' ] . '-root' ];
}
2012-10-13 16:54:48 +00:00
}
2012-10-01 11:58:01 +00:00
} else {
2018-09-11 12:40:37 +00:00
if ( ! $bypassFilters && $isPackageAcceptableCallable && ! call_user_func ( $isPackageAcceptableCallable , strtolower ( $version [ 'name' ]), VersionParser :: parseStability ( $version [ 'version' ]))) {
2012-10-13 16:54:48 +00:00
continue ;
}
// load acceptable packages in the providers
2016-01-20 06:34:37 +00:00
$package = $this -> createPackage ( $version , 'Composer\Package\CompletePackage' );
2012-10-01 11:58:01 +00:00
$package -> setRepository ( $this );
2013-04-01 06:10:15 +00:00
if ( $package instanceof AliasPackage ) {
$aliased = $package -> getAliasOf ();
$aliased -> setRepository ( $this );
2012-10-01 11:58:01 +00:00
2013-04-01 06:10:15 +00:00
$this -> providers [ $name ][ $version [ 'uid' ]] = $aliased ;
$this -> providers [ $name ][ $version [ 'uid' ] . '-alias' ] = $package ;
2012-10-01 11:58:01 +00:00
2012-10-13 16:54:48 +00:00
// override provider with its alias so it can be expanded in the if block above
2013-04-01 06:10:15 +00:00
$this -> providersByUid [ $version [ 'uid' ]] = $package ;
} else {
$this -> providers [ $name ][ $version [ 'uid' ]] = $package ;
$this -> providersByUid [ $version [ 'uid' ]] = $package ;
2012-10-01 11:58:01 +00:00
}
2012-10-24 14:11:32 +00:00
// handle root package aliases
unset ( $rootAliasData );
2014-06-09 17:36:06 +00:00
if ( isset ( $this -> rootAliases [ $package -> getName ()][ $package -> getVersion ()])) {
$rootAliasData = $this -> rootAliases [ $package -> getName ()][ $package -> getVersion ()];
} elseif ( $package instanceof AliasPackage && isset ( $this -> rootAliases [ $package -> getName ()][ $package -> getAliasOf () -> getVersion ()])) {
$rootAliasData = $this -> rootAliases [ $package -> getName ()][ $package -> getAliasOf () -> getVersion ()];
2012-10-24 14:11:32 +00:00
}
if ( isset ( $rootAliasData )) {
$alias = $this -> createAliasPackage ( $package , $rootAliasData [ 'alias_normalized' ], $rootAliasData [ 'alias' ]);
$alias -> setRepository ( $this );
$this -> providers [ $name ][ $version [ 'uid' ] . '-root' ] = $alias ;
$this -> providersByUid [ $version [ 'uid' ] . '-root' ] = $alias ;
}
2012-10-01 11:58:01 +00:00
}
}
}
2016-03-27 17:39:36 +00:00
$result = $this -> providers [ $name ];
// clean up the cache because otherwise using this puts the repo in an inconsistent state with a polluted unfiltered cache
// which is likely not an issue but might cause hard to track behaviors depending on how the repo is used
if ( $bypassFilters ) {
foreach ( $this -> providers [ $name ] as $uid => $provider ) {
unset ( $this -> providersByUid [ $uid ]);
}
unset ( $this -> providers [ $name ]);
}
return $result ;
2012-10-01 11:58:01 +00:00
}
2012-08-22 15:47:05 +00:00
/**
* { @ inheritDoc }
*/
2011-04-17 22:14:44 +00:00
protected function initialize ()
{
parent :: initialize ();
2012-04-06 20:39:43 +00:00
2012-08-20 13:44:45 +00:00
$repoData = $this -> loadDataFromServer ();
foreach ( $repoData as $package ) {
2012-09-08 00:00:02 +00:00
$this -> addPackage ( $this -> createPackage ( $package , 'Composer\Package\CompletePackage' ));
2012-08-20 13:44:45 +00:00
}
}
2013-08-19 07:40:54 +00:00
/**
* Adds a new package to the repository
*
* @ param PackageInterface $package
*/
public function addPackage ( PackageInterface $package )
{
parent :: addPackage ( $package );
2014-05-07 17:10:55 +00:00
$this -> configurePackageTransportOptions ( $package );
2013-08-19 07:40:54 +00:00
}
2018-09-12 16:58:54 +00:00
private function loadAsyncPackages ( array $packageNames , $isPackageAcceptableCallable )
{
$this -> loadRootServerFile ();
$packages = array ();
$repo = $this ;
$createPackageIfAcceptable = function ( $version , $constraint ) use ( & $packages , $isPackageAcceptableCallable , $repo ) {
if ( ! call_user_func ( $isPackageAcceptableCallable , strtolower ( $version [ 'name' ]), VersionParser :: parseStability ( $version [ 'version' ]))) {
return ;
}
if ( isset ( $version [ 'version_normalized' ]) && $constraint && ! $constraint -> matches ( new Constraint ( '==' , $version [ 'version_normalized' ]))) {
return ;
}
// load acceptable packages in the providers
$package = $this -> createPackage ( $version , 'Composer\Package\CompletePackage' );
$package -> setRepository ( $repo );
// if there was no version_normalized, then we need to check now for the constraint
if ( ! $constraint || isset ( $version [ 'version_normalized' ]) || $constraint -> matches ( new Constraint ( '==' , $package -> getVersion ()))) {
$packages [ spl_object_hash ( $package )] = $package ;
if ( $package instanceof AliasPackage && ! isset ( $packages [ spl_object_hash ( $package -> getAliasOf ())])) {
$packages [ spl_object_hash ( $package -> getAliasOf ())] = $package -> getAliasOf ();
}
}
};
if ( $this -> lazyProvidersUrl ) {
foreach ( $packageNames as $name => $constraint ) {
2018-11-12 16:21:23 +00:00
// skip platform packages, root package and composer-plugin-api
if ( preg_match ( PlatformRepository :: PLATFORM_PACKAGE_REGEX , $name ) || '__root__' === $name || 'composer-plugin-api' === $name ) {
continue ;
}
2018-09-12 16:58:54 +00:00
$url = str_replace ( '%package%' , $name , $this -> lazyProvidersUrl );
$cacheKey = 'provider-' . strtr ( $name , '/' , '$' ) . '.json' ;
$lastModified = null ;
if ( $contents = $this -> cache -> read ( $cacheKey )) {
$contents = json_decode ( $contents , true );
$lastModified = isset ( $contents [ 'last-modified' ]) ? $contents [ 'last-modified' ] : null ;
}
$this -> asyncFetchFile ( $url , $cacheKey , $lastModified )
-> then ( function ( $response ) use ( & $packages , $contents , $name , $constraint , $createPackageIfAcceptable ) {
if ( true === $response ) {
$response = $contents ;
}
2018-10-31 11:44:54 +00:00
if ( ! isset ( $response [ 'packages' ][ $name ])) {
return ;
}
2018-09-12 16:58:54 +00:00
$uniqKeys = array ( 'version' , 'version_normalized' , 'source' , 'dist' , 'time' );
foreach ( $response [ 'packages' ][ $name ] as $version ) {
if ( isset ( $version [ 'versions' ])) {
$baseVersion = $version ;
foreach ( $uniqKeys as $key ) {
unset ( $baseVersion [ $key . 's' ]);
}
foreach ( $version [ 'versions' ] as $index => $dummy ) {
$unpackedVersion = $baseVersion ;
foreach ( $uniqKeys as $key ) {
$unpackedVersion [ $key ] = $version [ $key . 's' ][ $index ];
}
$createPackageIfAcceptable ( $unpackedVersion , $constraint );
}
} else {
$createPackageIfAcceptable ( $version , $constraint );
}
}
}, function ( $e ) {
// TODO use ->done() above instead with react/promise 2.0
var_dump ( 'Uncaught Ex' , $e -> getMessage ());
2018-10-31 11:44:54 +00:00
throw $e ;
2018-09-12 16:58:54 +00:00
});
}
}
$this -> httpDownloader -> wait ();
return $packages ;
// RepositorySet should call loadMetadata, getMetadata when all promises resolved, then metadataComplete when done so we can GC the loaded json and whatnot then as needed
}
2012-10-01 11:58:01 +00:00
protected function loadRootServerFile ()
2012-08-20 13:44:45 +00:00
{
2012-10-01 11:58:01 +00:00
if ( null !== $this -> rootData ) {
return $this -> rootData ;
}
2012-07-20 08:27:02 +00:00
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 );
}
2012-10-11 19:26:11 +00:00
$jsonUrlParts = parse_url ( $this -> url );
2012-08-29 13:12:08 +00:00
2014-12-08 22:04:10 +00:00
if ( isset ( $jsonUrlParts [ 'path' ]) && false !== strpos ( $jsonUrlParts [ 'path' ], '.json' )) {
2012-10-11 19:26:11 +00:00
$jsonUrl = $this -> url ;
} else {
$jsonUrl = $this -> url . '/packages.json' ;
}
2012-04-13 00:58:40 +00:00
2012-10-11 19:26:11 +00:00
$data = $this -> fetchFile ( $jsonUrl , 'packages.json' );
2012-04-13 00:58:40 +00:00
2013-02-21 15:50:04 +00:00
if ( ! empty ( $data [ 'notify-batch' ])) {
2013-03-10 12:27:49 +00:00
$this -> notifyUrl = $this -> canonicalizeUrl ( $data [ 'notify-batch' ]);
} elseif ( ! empty ( $data [ 'notify' ])) {
$this -> notifyUrl = $this -> canonicalizeUrl ( $data [ 'notify' ]);
2012-11-28 17:44:49 +00:00
}
2013-03-10 12:27:49 +00:00
if ( ! empty ( $data [ 'search' ])) {
$this -> searchUrl = $this -> canonicalizeUrl ( $data [ 'search' ]);
2012-04-06 20:39:43 +00:00
}
2012-04-06 18:40:31 +00:00
2013-07-01 23:45:43 +00:00
if ( ! empty ( $data [ 'mirrors' ])) {
foreach ( $data [ 'mirrors' ] as $mirror ) {
2013-10-19 15:41:29 +00:00
if ( ! empty ( $mirror [ 'git-url' ])) {
$this -> sourceMirrors [ 'git' ][] = array ( 'url' => $mirror [ 'git-url' ], 'preferred' => ! empty ( $mirror [ 'preferred' ]));
}
if ( ! empty ( $mirror [ 'hg-url' ])) {
$this -> sourceMirrors [ 'hg' ][] = array ( 'url' => $mirror [ 'hg-url' ], 'preferred' => ! empty ( $mirror [ 'preferred' ]));
2013-07-01 23:45:43 +00:00
}
if ( ! empty ( $mirror [ 'dist-url' ])) {
2017-05-04 08:47:16 +00:00
$this -> distMirrors [] = array (
'url' => $this -> canonicalizeUrl ( $mirror [ 'dist-url' ]),
2017-05-19 13:14:47 +00:00
'preferred' => ! empty ( $mirror [ 'preferred' ]),
2017-05-04 08:47:16 +00:00
);
2013-07-01 23:45:43 +00:00
}
}
}
2013-07-01 23:46:20 +00:00
if ( ! empty ( $data [ 'providers-lazy-url' ])) {
$this -> lazyProvidersUrl = $this -> canonicalizeUrl ( $data [ 'providers-lazy-url' ]);
$this -> hasProviders = true ;
2016-11-30 21:34:59 +00:00
$this -> hasPartialPackages = ! empty ( $data [ 'packages' ]) && is_array ( $data [ 'packages' ]);
2013-07-01 23:46:20 +00:00
}
2013-02-27 12:32:21 +00:00
if ( $this -> allowSslDowngrade ) {
$this -> url = str_replace ( 'https://' , 'http://' , $this -> url );
2015-06-10 16:13:56 +00:00
$this -> baseUrl = str_replace ( 'https://' , 'http://' , $this -> baseUrl );
2013-02-27 12:32:21 +00:00
}
2013-02-21 15:50:04 +00:00
if ( ! empty ( $data [ 'providers-url' ])) {
2013-03-10 12:27:49 +00:00
$this -> providersUrl = $this -> canonicalizeUrl ( $data [ 'providers-url' ]);
2013-02-21 15:50:04 +00:00
$this -> hasProviders = true ;
}
2012-10-14 14:33:53 +00:00
if ( ! empty ( $data [ 'providers' ]) || ! empty ( $data [ 'providers-includes' ])) {
$this -> hasProviders = true ;
2012-10-01 11:58:01 +00:00
}
2018-10-31 11:44:54 +00:00
// TODO this is for testing only, remove once packagist reports v2 protocol support
if ( preg_match ( '{^https?://repo\.packagist\.org/?$}i' , $this -> url )) {
$this -> repoConfig [ 'force-lazy-providers' ] = true ;
}
2016-01-18 12:25:37 +00:00
// force values for packagist
2018-07-24 07:30:06 +00:00
if ( preg_match ( '{^https?://repo\.packagist\.org/?$}i' , $this -> url ) && ! empty ( $this -> repoConfig [ 'force-lazy-providers' ])) {
$this -> url = 'https://repo.packagist.org' ;
$this -> baseUrl = 'https://repo.packagist.org' ;
$this -> lazyProvidersUrl = $this -> canonicalizeUrl ( 'https://repo.packagist.org/p/%package%.json' );
2016-01-18 12:25:37 +00:00
$this -> providersUrl = null ;
2016-02-12 14:01:11 +00:00
} elseif ( ! empty ( $this -> repoConfig [ 'force-lazy-providers' ])) {
$this -> lazyProvidersUrl = $this -> canonicalizeUrl ( '/p/%package%.json' );
$this -> providersUrl = null ;
2016-01-18 12:25:37 +00:00
}
2012-10-01 11:58:01 +00:00
return $this -> rootData = $data ;
}
2013-03-10 12:27:49 +00:00
protected function canonicalizeUrl ( $url )
{
if ( '/' === $url [ 0 ]) {
return preg_replace ( '{(https?://[^/]+).*}i' , '$1' . $url , $this -> url );
}
return $url ;
}
2012-10-01 11:58:01 +00:00
protected function loadDataFromServer ()
{
$data = $this -> loadRootServerFile ();
2012-08-20 13:44:45 +00:00
return $this -> loadIncludes ( $data );
2012-04-06 18:40:31 +00:00
}
2012-10-14 14:33:53 +00:00
protected function loadProviderListings ( $data )
{
if ( isset ( $data [ 'providers' ])) {
if ( ! is_array ( $this -> providerListing )) {
$this -> providerListing = array ();
}
$this -> providerListing = array_merge ( $this -> providerListing , $data [ 'providers' ]);
}
2013-02-21 15:50:04 +00:00
if ( $this -> providersUrl && isset ( $data [ 'provider-includes' ])) {
$includes = $data [ 'provider-includes' ];
2013-02-21 17:51:22 +00:00
foreach ( $includes as $include => $metadata ) {
$url = $this -> baseUrl . '/' . str_replace ( '%hash%' , $metadata [ 'sha256' ], $include );
$cacheKey = str_replace ( array ( '%hash%' , '$' ), '' , $include );
if ( $this -> cache -> sha256 ( $cacheKey ) === $metadata [ 'sha256' ]) {
$includedData = json_decode ( $this -> cache -> read ( $cacheKey ), true );
} else {
$includedData = $this -> fetchFile ( $url , $cacheKey , $metadata [ 'sha256' ]);
}
2012-10-14 14:33:53 +00:00
$this -> loadProviderListings ( $includedData );
}
}
}
2012-08-20 13:44:45 +00:00
protected function loadIncludes ( $data )
2012-04-06 18:40:31 +00:00
{
2012-08-20 13:44:45 +00:00
$packages = array ();
2012-04-06 18:40:31 +00:00
// legacy repo handling
if ( ! isset ( $data [ 'packages' ]) && ! isset ( $data [ 'includes' ])) {
foreach ( $data as $pkg ) {
foreach ( $pkg [ 'versions' ] as $metadata ) {
2012-08-20 13:44:45 +00:00
$packages [] = $metadata ;
2012-04-06 18:40:31 +00:00
}
}
2012-10-22 20:40:32 +00:00
return $packages ;
2011-04-17 22:14:44 +00:00
}
2012-04-06 18:40:31 +00:00
if ( isset ( $data [ 'packages' ])) {
foreach ( $data [ 'packages' ] as $package => $versions ) {
foreach ( $versions as $version => $metadata ) {
2012-08-20 13:44:45 +00:00
$packages [] = $metadata ;
2012-04-06 18:40:31 +00:00
}
}
2012-04-06 17:56:34 +00:00
}
2012-04-06 18:40:31 +00:00
if ( isset ( $data [ 'includes' ])) {
foreach ( $data [ 'includes' ] as $include => $metadata ) {
2012-04-06 20:39:43 +00:00
if ( $this -> cache -> sha1 ( $include ) === $metadata [ 'sha1' ]) {
$includedData = json_decode ( $this -> cache -> read ( $include ), true );
} else {
2012-10-11 19:26:11 +00:00
$includedData = $this -> fetchFile ( $include );
2012-04-06 20:39:43 +00:00
}
2012-08-20 13:44:45 +00:00
$packages = array_merge ( $packages , $this -> loadIncludes ( $includedData ));
2011-10-29 05:43:26 +00:00
}
2011-04-23 18:52:37 +00:00
}
2012-08-20 13:44:45 +00:00
return $packages ;
2011-04-23 18:52:37 +00:00
}
2012-09-08 00:00:02 +00:00
2016-01-20 06:34:37 +00:00
protected function createPackage ( array $data , $class = 'Composer\Package\CompletePackage' )
2012-09-08 00:00:02 +00:00
{
try {
2013-05-14 13:24:18 +00:00
if ( ! isset ( $data [ 'notification-url' ])) {
$data [ 'notification-url' ] = $this -> notifyUrl ;
}
2012-11-29 08:24:28 +00:00
2016-01-20 06:34:37 +00:00
$package = $this -> loader -> load ( $data , $class );
2013-10-19 15:41:29 +00:00
if ( isset ( $this -> sourceMirrors [ $package -> getSourceType ()])) {
$package -> setSourceMirrors ( $this -> sourceMirrors [ $package -> getSourceType ()]);
}
2013-07-01 23:45:43 +00:00
$package -> setDistMirrors ( $this -> distMirrors );
2014-05-07 17:10:55 +00:00
$this -> configurePackageTransportOptions ( $package );
return $package ;
2012-09-08 00:00:02 +00:00
} catch ( \Exception $e ) {
2012-09-08 11:49:37 +00:00
throw new \RuntimeException ( 'Could not load package ' . ( isset ( $data [ 'name' ]) ? $data [ 'name' ] : json_encode ( $data )) . ' in ' . $this -> url . ': [' . get_class ( $e ) . '] ' . $e -> getMessage (), 0 , $e );
2012-09-08 00:00:02 +00:00
}
}
2012-10-11 19:26:11 +00:00
2016-01-18 12:25:37 +00:00
protected function fetchFile ( $filename , $cacheKey = null , $sha256 = null , $storeLastModifiedTime = false )
2012-10-11 19:26:11 +00:00
{
2013-07-01 23:46:20 +00:00
if ( null === $cacheKey ) {
2012-10-11 19:26:11 +00:00
$cacheKey = $filename ;
2012-10-14 14:33:53 +00:00
$filename = $this -> baseUrl . '/' . $filename ;
2012-10-11 19:26:11 +00:00
}
2015-09-20 17:26:23 +00:00
// url-encode $ signs in URLs as bad proxies choke on them
2015-09-28 08:59:26 +00:00
if (( $pos = strpos ( $filename , '$' )) && preg_match ( '{^https?://.*}i' , $filename )) {
2015-09-28 09:51:14 +00:00
$filename = substr ( $filename , 0 , $pos ) . '%24' . substr ( $filename , $pos + 1 );
2015-09-20 17:26:23 +00:00
}
2012-10-11 19:26:11 +00:00
$retries = 3 ;
while ( $retries -- ) {
try {
2018-11-12 14:34:54 +00:00
$httpDownloader = $this -> httpDownloader ;
2014-05-07 16:38:58 +00:00
2018-11-12 14:34:54 +00:00
if ( $this -> eventDispatcher ) {
$preFileDownloadEvent = new PreFileDownloadEvent ( PluginEvents :: PRE_FILE_DOWNLOAD , $this -> httpDownloader , $filename );
$this -> eventDispatcher -> dispatch ( $preFileDownloadEvent -> getName (), $preFileDownloadEvent );
$httpDownloader = $preFileDownloadEvent -> getHttpDownloader ();
}
2016-06-21 14:38:52 +00:00
2018-09-12 16:58:54 +00:00
$response = $httpDownloader -> get ( $filename );
$json = $response -> getBody ();
2013-02-21 15:50:04 +00:00
if ( $sha256 && $sha256 !== hash ( 'sha256' , $json )) {
2016-06-24 13:58:32 +00:00
// undo downgrade before trying again if http seems to be hijacked or modifying content somehow
if ( $this -> allowSslDowngrade ) {
$this -> url = str_replace ( 'http://' , 'https://' , $this -> url );
$this -> baseUrl = str_replace ( 'http://' , 'https://' , $this -> baseUrl );
$filename = str_replace ( 'http://' , 'https://' , $filename );
}
2012-10-21 14:10:47 +00:00
if ( $retries ) {
2013-05-03 10:29:18 +00:00
usleep ( 100000 );
2012-10-21 14:10:47 +00:00
continue ;
}
2013-02-21 16:07:53 +00:00
// TODO use scarier wording once we know for sure it doesn't do false positives anymore
2018-01-16 14:13:36 +00:00
throw new RepositorySecurityException ( 'The contents of ' . $filename . ' do not match its signature. This could indicate a man-in-the-middle attack or e.g. antivirus software corrupting files. Try running composer again and report this if you think it is a mistake.' );
2012-10-15 12:37:27 +00:00
}
2016-01-18 12:25:37 +00:00
2018-09-12 16:58:54 +00:00
$data = $response -> decodeJson ();
2017-03-08 13:10:50 +00:00
if ( ! empty ( $data [ 'warning' ])) {
$this -> io -> writeError ( '<warning>Warning from ' . $this -> url . ': ' . $data [ 'warning' ] . '</warning>' );
}
if ( ! empty ( $data [ 'info' ])) {
$this -> io -> writeError ( '<info>Info from ' . $this -> url . ': ' . $data [ 'info' ] . '</info>' );
}
2013-07-01 23:46:20 +00:00
if ( $cacheKey ) {
2016-01-18 12:25:37 +00:00
if ( $storeLastModifiedTime ) {
2018-09-12 16:58:54 +00:00
$lastModifiedDate = $response -> getHeader ( 'last-modified' );
2016-01-18 12:25:37 +00:00
if ( $lastModifiedDate ) {
$data [ 'last-modified' ] = $lastModifiedDate ;
$json = json_encode ( $data );
}
}
2013-07-01 23:46:20 +00:00
$this -> cache -> write ( $cacheKey , $json );
}
2012-10-11 19:26:11 +00:00
2018-09-12 16:58:54 +00:00
$response -> collect ();
2012-10-11 19:26:11 +00:00
break ;
} catch ( \Exception $e ) {
2018-09-12 16:58:54 +00:00
if ( $e instanceof \LogicException ) {
throw $e ;
}
2016-01-18 12:25:37 +00:00
if ( $e instanceof TransportException && $e -> getStatusCode () === 404 ) {
throw $e ;
}
2013-02-21 17:16:54 +00:00
if ( $retries ) {
2013-05-03 10:29:18 +00:00
usleep ( 100000 );
2013-02-21 17:16:54 +00:00
continue ;
}
2013-02-21 16:41:38 +00:00
if ( $e instanceof RepositorySecurityException ) {
throw $e ;
}
2013-07-01 23:46:20 +00:00
if ( $cacheKey && ( $contents = $this -> cache -> read ( $cacheKey ))) {
2013-02-21 17:16:54 +00:00
if ( ! $this -> degradedMode ) {
2015-02-06 12:52:44 +00:00
$this -> io -> writeError ( '<warning>' . $e -> getMessage () . '</warning>' );
$this -> io -> writeError ( '<warning>' . $this -> url . ' could not be fully loaded, package information was loaded from the local cache and may be out of date</warning>' );
2012-10-11 19:26:11 +00:00
}
2013-02-21 17:16:54 +00:00
$this -> degradedMode = true ;
$data = JsonFile :: parseJson ( $contents , $this -> cache -> getRoot () . $cacheKey );
2012-10-11 19:35:51 +00:00
2013-02-21 17:16:54 +00:00
break ;
2012-10-11 19:26:11 +00:00
}
2013-02-21 17:16:54 +00:00
throw $e ;
2012-10-11 19:26:11 +00:00
}
}
return $data ;
}
2016-01-18 12:25:37 +00:00
protected function fetchFileIfLastModified ( $filename , $cacheKey , $lastModifiedTime )
{
$retries = 3 ;
while ( $retries -- ) {
try {
2018-11-12 14:34:54 +00:00
$httpDownloader = $this -> httpDownloader ;
if ( $this -> eventDispatcher ) {
$preFileDownloadEvent = new PreFileDownloadEvent ( PluginEvents :: PRE_FILE_DOWNLOAD , $this -> httpDownloader , $filename );
$this -> eventDispatcher -> dispatch ( $preFileDownloadEvent -> getName (), $preFileDownloadEvent );
$httpDownloader = $preFileDownloadEvent -> getHttpDownloader ();
}
2016-01-18 12:25:37 +00:00
$options = array ( 'http' => array ( 'header' => array ( 'If-Modified-Since: ' . $lastModifiedTime )));
2018-09-12 16:58:54 +00:00
$response = $httpDownloader -> get ( $filename , $options );
$json = $response -> getBody ();
if ( $json === '' && $response -> getStatusCode () === 304 ) {
2016-01-18 12:25:37 +00:00
return true ;
}
2018-09-12 16:58:54 +00:00
$data = $response -> decodeJson ();
2017-03-08 13:10:50 +00:00
if ( ! empty ( $data [ 'warning' ])) {
$this -> io -> writeError ( '<warning>Warning from ' . $this -> url . ': ' . $data [ 'warning' ] . '</warning>' );
}
if ( ! empty ( $data [ 'info' ])) {
$this -> io -> writeError ( '<info>Info from ' . $this -> url . ': ' . $data [ 'info' ] . '</info>' );
}
2018-09-12 16:58:54 +00:00
$lastModifiedDate = $response -> getHeader ( 'last-modified' );
$response -> collect ();
2016-01-18 12:25:37 +00:00
if ( $lastModifiedDate ) {
$data [ 'last-modified' ] = $lastModifiedDate ;
$json = json_encode ( $data );
}
$this -> cache -> write ( $cacheKey , $json );
return $data ;
} catch ( \Exception $e ) {
2018-09-12 16:58:54 +00:00
if ( $e instanceof \LogicException ) {
throw $e ;
}
2016-01-18 12:25:37 +00:00
if ( $e instanceof TransportException && $e -> getStatusCode () === 404 ) {
throw $e ;
}
if ( $retries ) {
usleep ( 100000 );
continue ;
}
if ( ! $this -> degradedMode ) {
$this -> io -> writeError ( '<warning>' . $e -> getMessage () . '</warning>' );
$this -> io -> writeError ( '<warning>' . $this -> url . ' could not be fully loaded, package information was loaded from the local cache and may be out of date</warning>' );
}
$this -> degradedMode = true ;
2016-01-26 13:07:05 +00:00
2016-01-18 12:25:37 +00:00
return true ;
}
}
}
2016-11-30 21:34:59 +00:00
2018-09-12 16:58:54 +00:00
protected function asyncFetchFile ( $filename , $cacheKey , $lastModifiedTime = null )
{
$retries = 3 ;
2018-11-12 14:34:54 +00:00
$httpDownloader = $this -> httpDownloader ;
if ( $this -> eventDispatcher ) {
$preFileDownloadEvent = new PreFileDownloadEvent ( PluginEvents :: PRE_FILE_DOWNLOAD , $this -> httpDownloader , $filename );
$this -> eventDispatcher -> dispatch ( $preFileDownloadEvent -> getName (), $preFileDownloadEvent );
$httpDownloader = $preFileDownloadEvent -> getHttpDownloader ();
}
2018-09-12 16:58:54 +00:00
$options = $lastModifiedTime ? array ( 'http' => array ( 'header' => array ( 'If-Modified-Since: ' . $lastModifiedTime ))) : array ();
$io = $this -> io ;
$url = $this -> url ;
$cache = $this -> cache ;
$degradedMode =& $this -> degradedMode ;
$accept = function ( $response ) use ( $io , $url , $cache , $cacheKey ) {
2018-10-31 11:44:54 +00:00
// package not found is acceptable for a v2 protocol repository
if ( $response -> getStatusCode () === 404 ) {
return array ( 'packages' => array ());
}
2018-09-12 16:58:54 +00:00
$json = $response -> getBody ();
if ( $json === '' && $response -> getStatusCode () === 304 ) {
return true ;
}
$data = $response -> decodeJson ();
if ( ! empty ( $data [ 'warning' ])) {
$io -> writeError ( '<warning>Warning from ' . $url . ': ' . $data [ 'warning' ] . '</warning>' );
}
if ( ! empty ( $data [ 'info' ])) {
$io -> writeError ( '<info>Info from ' . $url . ': ' . $data [ 'info' ] . '</info>' );
}
$lastModifiedDate = $response -> getHeader ( 'last-modified' );
$response -> collect ();
if ( $lastModifiedDate ) {
$data [ 'last-modified' ] = $lastModifiedDate ;
$json = JsonFile :: encode ( $data , JsonFile :: JSON_UNESCAPED_SLASHES | JsonFile :: JSON_UNESCAPED_UNICODE );
}
$cache -> write ( $cacheKey , $json );
return $data ;
};
$reject = function ( $e ) use ( & $retries , $httpDownloader , $filename , $options , & $reject , $accept , $io , $url , $cache , & $degradedMode ) {
var_dump ( 'Caught8' , $e -> getMessage ());
if ( $e instanceof TransportException && $e -> getStatusCode () === 404 ) {
return false ;
}
if ( -- $retries ) {
usleep ( 100000 );
return $httpDownloader -> add ( $filename , $options ) -> then ( $accept , $reject );
}
if ( ! $degradedMode ) {
$io -> writeError ( '<warning>' . $e -> getMessage () . '</warning>' );
$io -> writeError ( '<warning>' . $url . ' could not be fully loaded, package information was loaded from the local cache and may be out of date</warning>' );
}
$degradedMode = true ;
return true ;
};
return $httpDownloader -> add ( $filename , $options ) -> then ( $accept , $reject );
}
2016-11-30 21:34:59 +00:00
/**
* This initializes the packages key of a partial packages . json that contain some packages inlined + a providers - lazy - url
*
* This should only be called once
*/
private function initializePartialPackages ()
{
$rootData = $this -> loadRootServerFile ();
$this -> partialPackagesByName = array ();
foreach ( $rootData [ 'packages' ] as $package => $versions ) {
2016-12-06 20:56:09 +00:00
$package = strtolower ( $package );
foreach ( $versions as $version ) {
$this -> partialPackagesByName [ $package ][] = $version ;
if ( ! empty ( $version [ 'provide' ]) && is_array ( $version [ 'provide' ])) {
foreach ( $version [ 'provide' ] as $provided => $providedVersion ) {
$this -> partialPackagesByName [ strtolower ( $provided )][] = $version ;
}
}
if ( ! empty ( $version [ 'replace' ]) && is_array ( $version [ 'replace' ])) {
foreach ( $version [ 'replace' ] as $provided => $providedVersion ) {
$this -> partialPackagesByName [ strtolower ( $provided )][] = $version ;
}
}
}
2016-11-30 21:34:59 +00:00
}
// wipe rootData as it is fully consumed at this point and this saves some memory
$this -> rootData = true ;
}
2011-09-17 11:39:37 +00:00
}