1
0
Fork 0

Merge branch 'master' into 2.0

pull/6942/head^2
Jordi Boggiano 2019-07-31 17:50:01 +02:00
commit 6c4357a7ed
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
39 changed files with 492 additions and 136 deletions

View File

@ -30,9 +30,11 @@ matrix:
env: env:
- deps=high - deps=high
- php: nightly - php: nightly
- php: 7.4snapshot
fast_finish: true fast_finish: true
allow_failures: allow_failures:
- php: nightly - php: nightly
- php: 7.4snapshot
before_install: before_install:
# disable xdebug if available # disable xdebug if available

View File

@ -259,6 +259,10 @@ match the platform requirements of the installed packages. This can be used
to verify that a production server has all the extensions needed to run a to verify that a production server has all the extensions needed to run a
project after installing it for example. project after installing it for example.
Unlike update/install, this command will ignore config.platform settings and
check the real platform packages so you can be certain you have the required
platform dependencies.
## global ## global
The global command allows you to run other commands like `install`, `remove`, `require` The global command allows you to run other commands like `install`, `remove`, `require`

View File

@ -112,6 +112,19 @@ Note that this will still need to pull and scan all of your VCS repositories
because any VCS repository might contain (on any branch) one of the selected because any VCS repository might contain (on any branch) one of the selected
packages. packages.
If you want to scan only the selected package and not all VCS repositories you need
to declare a *name* for all your package (this only work on VCS repositories type) :
```json
{
"repositories": [
{ "name": "company/privaterepo", "type": "vcs", "url": "https://github.com/mycompany/privaterepo" },
{ "name": "private/repo", "type": "vcs", "url": "http://svn.example.org/private/repo" },
{ "name": "mycompany/privaterepo2", "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" }
]
}
```
If you want to scan only a single repository and update all packages found in If you want to scan only a single repository and update all packages found in
it, pass the VCS repository URL as an optional argument: it, pass the VCS repository URL as an optional argument:

View File

@ -34,6 +34,8 @@ class CheckPlatformReqsCommand extends BaseCommand
<<<EOT <<<EOT
Checks that your PHP and extensions versions match the platform requirements of the installed packages. Checks that your PHP and extensions versions match the platform requirements of the installed packages.
Unlike update/install, this command will ignore config.platform settings and check the real platform packages so you can be certain you have the required platform dependencies.
<info>php composer.phar check-platform-reqs</info> <info>php composer.phar check-platform-reqs</info>
EOT EOT
@ -49,6 +51,10 @@ EOT
$dependencies = $composer->getLocker()->getLockedRepository(!$input->getOption('no-dev'))->getPackages(); $dependencies = $composer->getLocker()->getLockedRepository(!$input->getOption('no-dev'))->getPackages();
} else { } else {
$dependencies = $composer->getRepositoryManager()->getLocalRepository()->getPackages(); $dependencies = $composer->getRepositoryManager()->getLocalRepository()->getPackages();
// fallback to lockfile if installed repo is empty
if (!$dependencies) {
$dependencies = $composer->getLocker()->getLockedRepository(true)->getPackages();
}
$requires += $composer->getPackage()->getDevRequires(); $requires += $composer->getPackage()->getDevRequires();
} }
foreach ($requires as $require => $link) { foreach ($requires as $require => $link) {

View File

@ -168,13 +168,25 @@ EOT
if ($repositories) { if ($repositories) {
$config = Factory::createConfig($io); $config = Factory::createConfig($io);
$repos = array(new PlatformRepository); $repos = array(new PlatformRepository);
$createDefaultPackagistRepo = true;
foreach ($repositories as $repo) { foreach ($repositories as $repo) {
$repos[] = RepositoryFactory::fromString($io, $config, $repo); $repoConfig = RepositoryFactory::configFromString($io, $config, $repo);
if (
(isset($repoConfig['packagist']) && $repoConfig === array('packagist' => false))
|| (isset($repoConfig['packagist.org']) && $repoConfig === array('packagist.org' => false))
) {
$createDefaultPackagistRepo = false;
continue;
}
$repos[] = RepositoryFactory::createRepo($io, $config, $repoConfig);
}
if ($createDefaultPackagistRepo) {
$repos[] = RepositoryFactory::createRepo($io, $config, array(
'type' => 'composer',
'url' => 'https://repo.packagist.org',
));
} }
$repos[] = RepositoryFactory::createRepo($io, $config, array(
'type' => 'composer',
'url' => 'https://repo.packagist.org',
));
$this->repos = new CompositeRepository($repos); $this->repos = new CompositeRepository($repos);
unset($repos, $config, $repositories); unset($repos, $config, $repositories);

View File

@ -26,6 +26,7 @@ use Composer\Plugin\PluginEvents;
use Composer\Repository\CompositeRepository; use Composer\Repository\CompositeRepository;
use Composer\Repository\PlatformRepository; use Composer\Repository\PlatformRepository;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Util\Silencer;
/** /**
* @author Jérémy Romey <jeremy@free-agent.fr> * @author Jérémy Romey <jeremy@free-agent.fr>
@ -103,11 +104,6 @@ EOT
return 1; return 1;
} }
if (!is_writable($this->file)) {
$io->writeError('<error>'.$this->file.' is not writable.</error>');
return 1;
}
if (filesize($this->file) === 0) { if (filesize($this->file) === 0) {
file_put_contents($this->file, "{\n}\n"); file_put_contents($this->file, "{\n}\n");
@ -116,6 +112,14 @@ EOT
$this->json = new JsonFile($this->file); $this->json = new JsonFile($this->file);
$this->composerBackup = file_get_contents($this->json->getPath()); $this->composerBackup = file_get_contents($this->json->getPath());
// check for writability by writing to the file as is_writable can not be trusted on network-mounts
// see https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926
if (!is_writable($this->file) && !Silencer::call('file_put_contents', $this->file, $this->composerBackup)) {
$io->writeError('<error>'.$this->file.' is not writable.</error>');
return 1;
}
$composer = $this->getComposer(true, $input->getOption('no-plugins')); $composer = $this->getComposer(true, $input->getOption('no-plugins'));
$repos = $composer->getRepositoryManager()->getRepositories(); $repos = $composer->getRepositoryManager()->getRepositories();
@ -141,7 +145,12 @@ EOT
// validate requirements format // validate requirements format
$versionParser = new VersionParser(); $versionParser = new VersionParser();
foreach ($requirements as $constraint) { foreach ($requirements as $package => $constraint) {
if (strtolower($package) === $composer->getPackage()->getName()) {
$io->writeError(sprintf('<error>Root package \'%s\' cannot require itself in its composer.json</error>', $package));
return 1;
}
$versionParser->parseConstraints($constraint); $versionParser->parseConstraints($constraint);
} }

View File

@ -379,6 +379,9 @@ class Application extends BaseApplication
public function resetComposer() public function resetComposer()
{ {
$this->composer = null; $this->composer = null;
if ($this->getIO() && method_exists($this->getIO(), 'resetAuthentications')) {
$this->getIO()->resetAuthentications();
}
} }
/** /**

View File

@ -197,7 +197,7 @@ class RuleSetGenerator
} }
} }
protected function addConflictRules() protected function addConflictRules($ignorePlatformReqs = false)
{ {
/** @var PackageInterface $package */ /** @var PackageInterface $package */
foreach ($this->addedPackages as $package) { foreach ($this->addedPackages as $package) {
@ -206,6 +206,10 @@ class RuleSetGenerator
continue; continue;
} }
if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) {
continue;
}
/** @var PackageInterface $possibleConflict */ /** @var PackageInterface $possibleConflict */
foreach ($this->addedPackagesByNames[$link->getTarget()] as $possibleConflict) { foreach ($this->addedPackagesByNames[$link->getTarget()] as $possibleConflict) {
$conflictMatch = $this->pool->match($possibleConflict, $link->getTarget(), $link->getConstraint(), true); $conflictMatch = $this->pool->match($possibleConflict, $link->getTarget(), $link->getConstraint(), true);
@ -304,7 +308,7 @@ class RuleSetGenerator
$this->addRulesForJobs($ignorePlatformReqs); $this->addRulesForJobs($ignorePlatformReqs);
$this->addConflictRules(); $this->addConflictRules($ignorePlatformReqs);
// Remove references to packages // Remove references to packages
$this->addedPackages = $this->addedPackagesByNames = null; $this->addedPackages = $this->addedPackagesByNames = null;

View File

@ -87,8 +87,6 @@ class PerforceDownloader extends VcsDownloader
public function getLocalChanges(PackageInterface $package, $path) public function getLocalChanges(PackageInterface $package, $path)
{ {
$this->io->writeError('Perforce driver does not check for local changes before overriding', true); $this->io->writeError('Perforce driver does not check for local changes before overriding', true);
return null;
} }
/** /**

View File

@ -200,7 +200,9 @@ class EventDispatcher
try { try {
/** @var InstallerEvent $event */ /** @var InstallerEvent $event */
$return = $this->dispatch($scriptName, new Script\Event($scriptName, $event->getComposer(), $event->getIO(), $event->isDevMode(), $args, $flags)); $scriptEvent = new Script\Event($scriptName, $event->getComposer(), $event->getIO(), $event->isDevMode(), $args, $flags);
$scriptEvent->setOriginatingEvent($event);
$return = $this->dispatch($scriptName, $scriptEvent);
} catch (ScriptExecutionException $e) { } catch (ScriptExecutionException $e) {
$this->io->writeError(sprintf('<error>Script %s was called via %s</error>', $callable, $event->getName()), true, IOInterface::QUIET); $this->io->writeError(sprintf('<error>Script %s was called via %s</error>', $callable, $event->getName()), true, IOInterface::QUIET);
throw $e; throw $e;

View File

@ -28,6 +28,14 @@ abstract class BaseIO implements IOInterface
return $this->authentications; return $this->authentications;
} }
/**
* {@inheritDoc}
*/
public function resetAuthentications()
{
$this->authentications = array();
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View File

@ -326,9 +326,10 @@ class JsonManipulator
} }
// try and find a match for the subkey // try and find a match for the subkey
if ($this->pregMatch('{"'.preg_quote($name).'"\s*:}i', $children)) { $keyRegex = str_replace('/', '\\\\?/', preg_quote($name));
if ($this->pregMatch('{"'.$keyRegex.'"\s*:}i', $children)) {
// find best match for the value of "name" // find best match for the value of "name"
if (preg_match_all('{'.self::$DEFINES.'"'.preg_quote($name).'"\s*:\s*(?:(?&json))}x', $children, $matches)) { if (preg_match_all('{'.self::$DEFINES.'"'.$keyRegex.'"\s*:\s*(?:(?&json))}x', $children, $matches)) {
$bestMatch = ''; $bestMatch = '';
foreach ($matches[0] as $match) { foreach ($matches[0] as $match) {
if (strlen($bestMatch) < strlen($match)) { if (strlen($bestMatch) < strlen($match)) {

View File

@ -16,6 +16,7 @@ use Composer\IO\IOInterface;
use Composer\Json\JsonFile; use Composer\Json\JsonFile;
use Composer\Package\Loader\ArrayLoader; use Composer\Package\Loader\ArrayLoader;
use Composer\Package\Loader\LoaderInterface; use Composer\Package\Loader\LoaderInterface;
use Composer\Util\Zip;
/** /**
* @author Serge Smertin <serg.smertin@gmail.com> * @author Serge Smertin <serg.smertin@gmail.com>
@ -80,76 +81,15 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito
} }
} }
/**
* Find a file by name, returning the one that has the shortest path.
*
* @param \ZipArchive $zip
* @param string $filename
* @return bool|int
*/
private function locateFile(\ZipArchive $zip, $filename)
{
$indexOfShortestMatch = false;
$lengthOfShortestMatch = -1;
for ($i = 0; $i < $zip->numFiles; $i++) {
$stat = $zip->statIndex($i);
if (strcmp(basename($stat['name']), $filename) === 0) {
$directoryName = dirname($stat['name']);
if ($directoryName == '.') {
//if composer.json is in root directory
//it has to be the one to use.
return $i;
}
if (strpos($directoryName, '\\') !== false ||
strpos($directoryName, '/') !== false) {
//composer.json files below first directory are rejected
continue;
}
$length = strlen($stat['name']);
if ($indexOfShortestMatch === false || $length < $lengthOfShortestMatch) {
//Check it's not a directory.
$contents = $zip->getFromIndex($i);
if ($contents !== false) {
$indexOfShortestMatch = $i;
$lengthOfShortestMatch = $length;
}
}
}
}
return $indexOfShortestMatch;
}
private function getComposerInformation(\SplFileInfo $file) private function getComposerInformation(\SplFileInfo $file)
{ {
$zip = new \ZipArchive(); $json = Zip::getComposerJson($file->getPathname());
if ($zip->open($file->getPathname()) !== true) {
if (null === $json) {
return false; return false;
} }
if (0 == $zip->numFiles) { $package = JsonFile::parseJson($json, $file->getPathname().'#composer.json');
$zip->close();
return false;
}
$foundFileIndex = $this->locateFile($zip, 'composer.json');
if (false === $foundFileIndex) {
$zip->close();
return false;
}
$configurationFileName = $zip->getNameIndex($foundFileIndex);
$zip->close();
$composerFile = "zip://{$file->getPathname()}#$configurationFileName";
$json = file_get_contents($composerFile);
$package = JsonFile::parseJson($json, $composerFile);
$package['dist'] = array( $package['dist'] = array(
'type' => 'zip', 'type' => 'zip',
'url' => strtr($file->getPathname(), '\\', '/'), 'url' => strtr($file->getPathname(), '\\', '/'),

View File

@ -308,6 +308,10 @@ class GitHubDriver extends VcsDriver
*/ */
protected function generateSshUrl() protected function generateSshUrl()
{ {
if (false !== strpos($this->originUrl, ':')) {
return 'ssh://git@' . $this->originUrl . '/'.$this->owner.'/'.$this->repository.'.git';
}
return 'git@' . $this->originUrl . ':'.$this->owner.'/'.$this->repository.'.git'; return 'git@' . $this->originUrl . ':'.$this->owner.'/'.$this->repository.'.git';
} }

View File

@ -68,9 +68,9 @@ class GitLabDriver extends VcsDriver
private $isPrivate = true; private $isPrivate = true;
/** /**
* @var int port number * @var bool true if the origin has a port number or a path component in it
*/ */
protected $portNumber; private $hasNonstandardOrigin = false;
const URL_REGEX = '#^(?:(?P<scheme>https?)://(?P<domain>.+?)(?::(?P<port>[0-9]+))?/|git@(?P<domain2>[^:]+):)(?P<parts>.+)/(?P<repo>[^/]+?)(?:\.git|/)?$#'; const URL_REGEX = '#^(?:(?P<scheme>https?)://(?P<domain>.+?)(?::(?P<port>[0-9]+))?/|git@(?P<domain2>[^:]+):)(?P<parts>.+)/(?P<repo>[^/]+?)(?:\.git|/)?$#';
@ -95,11 +95,10 @@ class GitLabDriver extends VcsDriver
? $match['scheme'] ? $match['scheme']
: (isset($this->repoConfig['secure-http']) && $this->repoConfig['secure-http'] === false ? 'http' : 'https') : (isset($this->repoConfig['secure-http']) && $this->repoConfig['secure-http'] === false ? 'http' : 'https')
; ;
$this->originUrl = $this->determineOrigin($configuredDomains, $guessedDomain, $urlParts); $this->originUrl = $this->determineOrigin($configuredDomains, $guessedDomain, $urlParts, $match['port']);
if (!empty($match['port']) && true === is_numeric($match['port'])) { if (false !== strpos($this->originUrl, ':') || false !== strpos($this->originUrl, '/')) {
// If it is an HTTP based URL, and it has a port $this->hasNonstandardOrigin = true;
$this->portNumber = (int) $match['port'];
} }
$this->namespace = implode('/', $urlParts); $this->namespace = implode('/', $urlParts);
@ -260,10 +259,7 @@ class GitLabDriver extends VcsDriver
*/ */
public function getApiUrl() public function getApiUrl()
{ {
$domainName = $this->originUrl; return $this->scheme.'://'.$this->originUrl.'/api/v4/projects/'.$this->urlEncodeAll($this->namespace).'%2F'.$this->urlEncodeAll($this->repository);
$portNumber = (true === is_numeric($this->portNumber)) ? sprintf(':%s', $this->portNumber) : '';
return $this->scheme.'://'.$domainName.$portNumber.'/api/v4/projects/'.$this->urlEncodeAll($this->namespace).'%2F'.$this->urlEncodeAll($this->repository);
} }
/** /**
@ -362,6 +358,10 @@ class GitLabDriver extends VcsDriver
*/ */
protected function generateSshUrl() protected function generateSshUrl()
{ {
if ($this->hasNonstandardOrigin) {
return 'ssh://git@'.$this->originUrl.'/'.$this->namespace.'/'.$this->repository.'.git';
}
return 'git@' . $this->originUrl . ':'.$this->namespace.'/'.$this->repository.'.git'; return 'git@' . $this->originUrl . ':'.$this->namespace.'/'.$this->repository.'.git';
} }
@ -464,7 +464,7 @@ class GitLabDriver extends VcsDriver
$guessedDomain = !empty($match['domain']) ? $match['domain'] : $match['domain2']; $guessedDomain = !empty($match['domain']) ? $match['domain'] : $match['domain2'];
$urlParts = explode('/', $match['parts']); $urlParts = explode('/', $match['parts']);
if (false === self::determineOrigin((array) $config->get('gitlab-domains'), $guessedDomain, $urlParts)) { if (false === self::determineOrigin((array) $config->get('gitlab-domains'), $guessedDomain, $urlParts, $match['port'])) {
return false; return false;
} }
@ -495,16 +495,16 @@ class GitLabDriver extends VcsDriver
* @param array $urlParts * @param array $urlParts
* @return bool|string * @return bool|string
*/ */
private static function determineOrigin(array $configuredDomains, $guessedDomain, array &$urlParts) private static function determineOrigin(array $configuredDomains, $guessedDomain, array &$urlParts, $portNumber)
{ {
if (in_array($guessedDomain, $configuredDomains)) { if (in_array($guessedDomain, $configuredDomains) || ($portNumber && in_array($guessedDomain.':'.$portNumber, $configuredDomains))) {
return $guessedDomain; return $guessedDomain;
} }
while (null !== ($part = array_shift($urlParts))) { while (null !== ($part = array_shift($urlParts))) {
$guessedDomain .= '/' . $part; $guessedDomain .= '/' . $part;
if (in_array($guessedDomain, $configuredDomains)) { if (in_array($guessedDomain, $configuredDomains) || ($portNumber && in_array(preg_replace('{/}', ':'.$portNumber.'/', $guessedDomain, 1), $configuredDomains))) {
return $guessedDomain; return $guessedDomain;
} }
} }

View File

@ -39,6 +39,11 @@ class Event extends BaseEvent
*/ */
private $devMode; private $devMode;
/**
* @var BaseEvent
*/
private $originatingEvent;
/** /**
* Constructor. * Constructor.
* *
@ -55,6 +60,7 @@ class Event extends BaseEvent
$this->composer = $composer; $this->composer = $composer;
$this->io = $io; $this->io = $io;
$this->devMode = $devMode; $this->devMode = $devMode;
$this->originatingEvent = null;
} }
/** /**
@ -86,4 +92,42 @@ class Event extends BaseEvent
{ {
return $this->devMode; return $this->devMode;
} }
/**
* Set the originating event.
*
* @return \Composer\EventDispatcher\Event|null
*/
public function getOriginatingEvent()
{
return $this->originatingEvent;
}
/**
* Set the originating event.
*
* @param \Composer\EventDispatcher\Event $event
* @return $this
*/
public function setOriginatingEvent(BaseEvent $event)
{
$this->originatingEvent = $this->calculateOriginatingEvent($event);
return $this;
}
/**
* Returns the upper-most event in chain.
*
* @param \Composer\EventDispatcher\Event $event
* @return \Composer\EventDispatcher\Event
*/
private function calculateOriginatingEvent(BaseEvent $event)
{
if ($event instanceof Event && $event->getOriginatingEvent()) {
return $this->calculateOriginatingEvent($event->getOriginatingEvent());
}
return $event;
}
} }

View File

@ -33,6 +33,7 @@ class ErrorHandler
* *
* @static * @static
* @throws \ErrorException * @throws \ErrorException
* @return bool
*/ */
public static function handle($level, $message, $file, $line) public static function handle($level, $message, $file, $line)
{ {
@ -63,6 +64,8 @@ class ErrorHandler
}, array_slice(debug_backtrace(), 2)))); }, array_slice(debug_backtrace(), 2))));
} }
} }
return true;
} }
/** /**

View File

@ -57,7 +57,10 @@ class GitLab
*/ */
public function authorizeOAuth($originUrl) public function authorizeOAuth($originUrl)
{ {
if (!in_array($originUrl, $this->config->get('gitlab-domains'), true)) { // before composer 1.9, origin URLs had no port number in them
$bcOriginUrl = preg_replace('{:\d+}', '', $originUrl);
if (!in_array($originUrl, $this->config->get('gitlab-domains'), true) && !in_array($bcOriginUrl, $this->config->get('gitlab-domains'), true)) {
return false; return false;
} }
@ -77,6 +80,12 @@ class GitLab
return true; return true;
} }
if (isset($authTokens[$bcOriginUrl])) {
$this->io->setAuthentication($originUrl, $authTokens[$bcOriginUrl], 'private-token');
return true;
}
return false; return false;
} }

View File

@ -363,8 +363,6 @@ class Perforce
while ($line !== false) { while ($line !== false) {
$line = fgets($pipe); $line = fgets($pipe);
} }
return;
} }
public function windowsLogin($password) public function windowsLogin($password)

View File

@ -286,6 +286,8 @@ class RemoteFilesystem
$errorMessage .= "\n"; $errorMessage .= "\n";
} }
$errorMessage .= preg_replace('{^file_get_contents\(.*?\): }', '', $msg); $errorMessage .= preg_replace('{^file_get_contents\(.*?\): }', '', $msg);
return true;
}); });
try { try {
$result = $this->getRemoteContents($originUrl, $fileUrl, $ctx, $http_response_header); $result = $this->getRemoteContents($originUrl, $fileUrl, $ctx, $http_response_header);
@ -459,6 +461,8 @@ class RemoteFilesystem
$errorMessage .= "\n"; $errorMessage .= "\n";
} }
$errorMessage .= preg_replace('{^file_put_contents\(.*?\): }', '', $msg); $errorMessage .= preg_replace('{^file_put_contents\(.*?\): }', '', $msg);
return true;
}); });
$result = (bool) file_put_contents($fileName, $result); $result = (bool) file_put_contents($fileName, $result);
restore_error_handler(); restore_error_handler();

View File

@ -19,8 +19,6 @@ use Composer\CaBundle\CaBundle;
*/ */
final class TlsHelper final class TlsHelper
{ {
private static $useOpensslParse;
/** /**
* Match hostname against a certificate. * Match hostname against a certificate.
* *

View File

@ -70,6 +70,9 @@ class Url
} }
$origin = (string) parse_url($url, PHP_URL_HOST); $origin = (string) parse_url($url, PHP_URL_HOST);
if ($port = parse_url($url, PHP_URL_PORT)) {
$origin .= ':'.$port;
}
if (strpos($origin, '.github.com') === (strlen($origin) - 11)) { if (strpos($origin, '.github.com') === (strlen($origin) - 11)) {
return 'github.com'; return 'github.com';

108
src/Composer/Util/Zip.php Normal file
View File

@ -0,0 +1,108 @@
<?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\Util;
/**
* @author Andreas Schempp <andreas.schempp@terminal42.ch>
*/
class Zip
{
/**
* Gets content of the root composer.json inside a ZIP archive.
*
* @param string $pathToZip
* @param string $filename
*
* @return string|null
*/
public static function getComposerJson($pathToZip)
{
if (!extension_loaded('zip')) {
throw new \RuntimeException('The Zip Util requires PHP\'s zip extension');
}
$zip = new \ZipArchive();
if ($zip->open($pathToZip) !== true) {
return null;
}
if (0 == $zip->numFiles) {
$zip->close();
return null;
}
$foundFileIndex = self::locateFile($zip, 'composer.json');
if (false === $foundFileIndex) {
$zip->close();
return null;
}
$content = null;
$configurationFileName = $zip->getNameIndex($foundFileIndex);
$stream = $zip->getStream($configurationFileName);
if (false !== $stream) {
$content = stream_get_contents($stream);
}
$zip->close();
return $content;
}
/**
* Find a file by name, returning the one that has the shortest path.
*
* @param \ZipArchive $zip
* @param string $filename
*
* @return bool|int
*/
private static function locateFile(\ZipArchive $zip, $filename)
{
$indexOfShortestMatch = false;
$lengthOfShortestMatch = -1;
for ($i = 0; $i < $zip->numFiles; $i++) {
$stat = $zip->statIndex($i);
if (strcmp(basename($stat['name']), $filename) === 0) {
$directoryName = dirname($stat['name']);
if ($directoryName === '.') {
//if composer.json is in root directory
//it has to be the one to use.
return $i;
}
if (strpos($directoryName, '\\') !== false ||
strpos($directoryName, '/') !== false) {
//composer.json files below first directory are rejected
continue;
}
$length = strlen($stat['name']);
if ($indexOfShortestMatch === false || $length < $lengthOfShortestMatch) {
//Check it's not a directory.
$contents = $zip->getFromIndex($i);
if ($contents !== false) {
$indexOfShortestMatch = $i;
$lengthOfShortestMatch = $length;
}
}
}
}
return $indexOfShortestMatch;
}
}

View File

@ -162,18 +162,18 @@ class AllFunctionalTest extends TestCase
} }
}; };
for ($i = 0, $c = count($tokens); $i < $c; $i++) { foreach ($tokens as $token) {
if ('' === $tokens[$i] && null === $section) { if ('' === $token && null === $section) {
continue; continue;
} }
// Handle section headers. // Handle section headers.
if (null === $section) { if (null === $section) {
$section = $tokens[$i]; $section = $token;
continue; continue;
} }
$sectionData = $tokens[$i]; $sectionData = $token;
// Allow sections to validate, or modify their section data. // Allow sections to validate, or modify their section data.
switch ($section) { switch ($section) {

View File

@ -152,11 +152,4 @@ class RuleSetTest extends TestCase
$this->assertContains('JOB : Install command rule (install foo 2.1)', $ruleSet->getPrettyString($pool)); $this->assertContains('JOB : Install command rule (install foo 2.1)', $ruleSet->getPrettyString($pool));
} }
private function getRuleMock()
{
return $this->getMockBuilder('Composer\DependencyResolver\Rule')
->disableOriginalConstructor()
->getMock();
}
} }

View File

@ -1448,6 +1448,22 @@ class JsonManipulatorTest extends TestCase
"repositories": { "repositories": {
} }
} }
',
),
'works on simple ones escaped slash' => array(
'{
"repositories": {
"foo\/bar": {
"bar": "baz"
}
}
}',
'foo/bar',
true,
'{
"repositories": {
}
}
', ',
), ),
'works on simple ones middle' => array( 'works on simple ones middle' => array(

View File

@ -148,7 +148,6 @@ class ArrayLoaderTest extends TestCase
{ {
$package = $this->loader->load($config); $package = $this->loader->load($config);
$dumper = new ArrayDumper; $dumper = new ArrayDumper;
$expectedConfig = $config;
$expectedConfig = $this->fixConfigWhenLoadConfigIsFalse($config); $expectedConfig = $this->fixConfigWhenLoadConfigIsFalse($config);
$this->assertEquals($expectedConfig, $dumper->dump($package)); $this->assertEquals($expectedConfig, $dumper->dump($package));
} }

View File

@ -40,15 +40,6 @@ class FossilDriverTest extends TestCase
$fs->removeDirectory($this->home); $fs->removeDirectory($this->home);
} }
private function getCmd($cmd)
{
if (Platform::isWindows()) {
return strtr($cmd, "'", '"');
}
return $cmd;
}
public static function supportProvider() public static function supportProvider()
{ {
return array( return array(

View File

@ -71,15 +71,6 @@ class SvnDriverTest extends TestCase
$svn->initialize(); $svn->initialize();
} }
private function getCmd($cmd)
{
if (Platform::isWindows()) {
return strtr($cmd, "'", '"');
}
return $cmd;
}
public static function supportProvider() public static function supportProvider()
{ {
return array( return array(

View File

@ -0,0 +1,80 @@
<?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\Test\Script;
use Composer\Composer;
use Composer\Config;
use Composer\Script\Event;
use Composer\Test\TestCase;
class EventTest extends TestCase
{
public function testEventSetsOriginatingEvent()
{
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
$composer = $this->createComposerInstance();
$originatingEvent = new \Composer\EventDispatcher\Event('originatingEvent');
$scriptEvent = new Event('test', $composer, $io, true);
$this->assertNull(
$scriptEvent->getOriginatingEvent(),
'originatingEvent is initialized as null'
);
$scriptEvent->setOriginatingEvent($originatingEvent);
$this->assertSame(
$originatingEvent,
$scriptEvent->getOriginatingEvent(),
'getOriginatingEvent() SHOULD return test event'
);
}
public function testEventCalculatesNestedOriginatingEvent()
{
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
$composer = $this->createComposerInstance();
$originatingEvent = new \Composer\EventDispatcher\Event('upperOriginatingEvent');
$intermediateEvent = new Event('intermediate', $composer, $io, true);
$intermediateEvent->setOriginatingEvent($originatingEvent);
$scriptEvent = new Event('test', $composer, $io, true);
$scriptEvent->setOriginatingEvent($intermediateEvent);
$this->assertNotSame(
$intermediateEvent,
$scriptEvent->getOriginatingEvent(),
'getOriginatingEvent() SHOULD NOT return intermediate events'
);
$this->assertSame(
$originatingEvent,
$scriptEvent->getOriginatingEvent(),
'getOriginatingEvent() SHOULD return upper-most event'
);
}
private function createComposerInstance()
{
$composer = new Composer;
$config = new Config;
$composer->setConfig($config);
$package = $this->getMockBuilder('Composer\Package\RootPackageInterface')->getMock();
$composer->setPackage($package);
return $composer;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -24,12 +24,9 @@ use RecursiveIteratorIterator;
*/ */
class GitHubTest extends TestCase class GitHubTest extends TestCase
{ {
private $username = 'username';
private $password = 'password'; private $password = 'password';
private $authcode = 'authcode';
private $message = 'mymessage'; private $message = 'mymessage';
private $origin = 'github.com'; private $origin = 'github.com';
private $token = 'githubtoken';
public function testUsernamePasswordAuthenticationFlow() public function testUsernamePasswordAuthenticationFlow()
{ {

View File

@ -24,7 +24,6 @@ class GitLabTest extends TestCase
{ {
private $username = 'username'; private $username = 'username';
private $password = 'password'; private $password = 'password';
private $authcode = 'authcode';
private $message = 'mymessage'; private $message = 'mymessage';
private $origin = 'gitlab.com'; private $origin = 'gitlab.com';
private $token = 'gitlabtoken'; private $token = 'gitlabtoken';

View File

@ -0,0 +1,117 @@
<?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\Test\Util;
use Composer\Util\Zip;
use PHPUnit\Framework\TestCase;
/**
* @author Andreas Schempp <andreas.schempp@terminal42.ch>
*/
class ZipTest extends TestCase
{
public function testThrowsExceptionIfZipExcentionIsNotLoaded()
{
if (extension_loaded('zip')) {
$this->markTestSkipped('The PHP zip extension is loaded.');
}
$this->setExpectedException('\RuntimeException', 'The Zip Util requires PHP\'s zip extension');
Zip::getComposerJson('');
}
public function testReturnsNullifTheZipIsNotFound()
{
if (!extension_loaded('zip')) {
$this->markTestSkipped('The PHP zip extension is not loaded.');
return;
}
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/invalid.zip');
$this->assertNull($result);
}
public function testReturnsNullIfTheZipIsEmpty()
{
if (!extension_loaded('zip')) {
$this->markTestSkipped('The PHP zip extension is not loaded.');
return;
}
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/empty.zip');
$this->assertNull($result);
}
public function testReturnsNullIfTheZipHasNoComposerJson()
{
if (!extension_loaded('zip')) {
$this->markTestSkipped('The PHP zip extension is not loaded.');
return;
}
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/nojson.zip');
$this->assertNull($result);
}
public function testReturnsNullIfTheComposerJsonIsInASubSubfolder()
{
if (!extension_loaded('zip')) {
$this->markTestSkipped('The PHP zip extension is not loaded.');
return;
}
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/subfolder.zip');
$this->assertNull($result);
}
public function testReturnsComposerJsonInZipRoot()
{
if (!extension_loaded('zip')) {
$this->markTestSkipped('The PHP zip extension is not loaded.');
return;
}
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/root.zip');
$this->assertEquals("{\n \"name\": \"foo/bar\"\n}\n", $result);
}
public function testReturnsComposerJsonInFirstFolder()
{
if (!extension_loaded('zip')) {
$this->markTestSkipped('The PHP zip extension is not loaded.');
return;
}
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/folder.zip');
$this->assertEquals("{\n \"name\": \"foo/bar\"\n}\n", $result);
}
public function testReturnsRootComposerJsonAndSkipsSubfolders()
{
if (!extension_loaded('zip')) {
$this->markTestSkipped('The PHP zip extension is not loaded.');
return;
}
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/multiple.zip');
$this->assertEquals("{\n \"name\": \"foo/bar\"\n}\n", $result);
}
}