1
0
Fork 0

Merge pull request #10420 from Seldaek/php53_cleanups

Cleanup PHP 5.3 hacks and legacy TODOs
pull/10425/head
Jordi Boggiano 2022-01-03 16:44:41 +01:00 committed by GitHub
commit e6cfc924f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 142 additions and 444 deletions

View File

@ -69,13 +69,8 @@
},
"autoload-dev": {
"psr-4": {
"Composer\\Test\\": "tests/Composer/Test",
"Composer\\PHPStanRules\\": "phpstan/Rules/src",
"Composer\\PHPStanRulesTests\\": "phpstan/Rules/tests"
},
"classmap": [
"phpstan/Rules/tests/data"
]
"Composer\\Test\\": "tests/Composer/Test"
}
},
"bin": [
"bin/composer"

View File

@ -1,46 +0,0 @@
<?php declare(strict_types = 1);
namespace Composer\PHPStanRules;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
/**
* @phpstan-implements Rule<\PhpParser\Node\Expr\Variable>
*/
final class AnonymousFunctionWithThisRule implements Rule
{
/**
* @inheritDoc
*/
public function getNodeType(): string
{
return \PhpParser\Node\Expr\Variable::class;
}
/**
* @inheritDoc
*/
public function processNode(Node $node, Scope $scope): array
{
if (!\is_string($node->name) || $node->name !== 'this') {
return [];
}
if ($scope->isInClosureBind()) {
return [];
}
if (!$scope->isInClass()) {
// reported in other standard rule on level 0
return [];
}
if ($scope->isInAnonymousFunction()) {
return ['Using $this inside anonymous function is prohibited because of PHP 5.3 support.'];
}
return [];
}
}

View File

@ -1,28 +0,0 @@
<?php declare(strict_types = 1);
namespace Composer\PHPStanRulesTests;
use Composer\PHPStanRules\AnonymousFunctionWithThisRule;
use PHPStan\Testing\RuleTestCase;
/**
* @phpstan-extends RuleTestCase<AnonymousFunctionWithThisRule>
*/
final class AnonymousFunctionWithThisRuleTest extends RuleTestCase
{
/**
* @inheritDoc
*/
protected function getRule(): \PHPStan\Rules\Rule
{
return new AnonymousFunctionWithThisRule();
}
public function testWithThis(): void
{
$this->analyse([__DIR__ . '/data/method-with-this.php'], [
['Using $this inside anonymous function is prohibited because of PHP 5.3 support.', 13],
['Using $this inside anonymous function is prohibited because of PHP 5.3 support.', 17],
]);
}
}

View File

@ -1,34 +0,0 @@
<?php
class FirstClass
{
/**
* @var int
*/
private $firstProp = 9;
public function funMethod()
{
function() {
$this->firstProp;
};
call_user_func(function() {
$this->funMethod();
}, $this);
$bind = 'bind';
function() use($bind) {
};
}
}
function global_ok() {
$_SERVER['REMOTE_ADDR'];
}
function global_this() {
// not checked by our rule, it is checked with standard phpstan rule on level 0
$this['REMOTE_ADDR'];
}

View File

@ -13,8 +13,6 @@ parameters:
- '../tests/Composer/Test/Autoload/Fixtures/*'
- '../tests/Composer/Test/Plugin/Fixtures/*'
- '../tests/Composer/Test/PolyfillTestCase.php'
# TODO Remove in 2.3
- '../src/Composer/Console/HtmlOutputFormatter.php'
reportUnmatchedIgnoredErrors: false
@ -58,6 +56,3 @@ parameters:
- Composer\Composer::VERSION
- Composer\Composer::RELEASE_DATE
- Composer\Composer::SOURCE_VERSION
rules:
- Composer\PHPStanRules\AnonymousFunctionWithThisRule

View File

@ -266,13 +266,7 @@ abstract class BaseCommand extends Command
$renderer = new Table($output);
$renderer->setStyle('compact');
$rendererStyle = $renderer->getStyle();
if (method_exists($rendererStyle, 'setVerticalBorderChars')) {
$rendererStyle->setVerticalBorderChars('');
} else {
// TODO remove in composer 2.2
// @phpstan-ignore-next-line
$rendererStyle->setVerticalBorderChar('');
}
$rendererStyle->setCellRowContentFormat('%s ');
$renderer->setRows($table)->render();
}
@ -282,20 +276,9 @@ abstract class BaseCommand extends Command
*/
protected function getTerminalWidth()
{
if (class_exists('Symfony\Component\Console\Terminal')) {
$terminal = new Terminal();
$width = $terminal->getWidth();
} else {
// For versions of Symfony console before 3.2
// TODO remove in composer 2.2
// @phpstan-ignore-next-line
list($width) = $this->getApplication()->getTerminalDimensions();
}
if (null === $width) {
// In case the width is not detected, we're probably running the command
// outside of a real terminal, use space without a limit
$width = PHP_INT_MAX;
}
if (Platform::isWindows()) {
$width--;
} else {

View File

@ -348,15 +348,14 @@ EOT
}
}
$self = $this;
$author = $io->askAndValidate(
'Author [<comment>'.$author.'</comment>, n to skip]: ',
function ($value) use ($self, $author) {
function ($value) use ($author) {
if ($value === 'n' || $value === 'no') {
return;
}
$value = $value ?: $author;
$author = $self->parseAuthorString($value);
$author = $this->parseAuthorString($value);
return sprintf('%s <%s>', $author['name'], $author['email']);
},
@ -731,13 +730,7 @@ EOT
$finder = new ExecutableFinder();
$gitBin = $finder->find('git');
// TODO in v2.3 always call with an array
if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) {
$cmd = new Process(array($gitBin, 'config', '-l'));
} else {
// @phpstan-ignore-next-line
$cmd = new Process(sprintf('%s config -l', ProcessExecutor::escape($gitBin)));
}
$cmd->run();
if ($cmd->isSuccessful()) {

View File

@ -85,13 +85,7 @@ EOT
$table = new Table($output);
$table->setStyle('compact');
$tableStyle = $table->getStyle();
if (method_exists($tableStyle, 'setVerticalBorderChars')) {
$tableStyle->setVerticalBorderChars('');
} else {
// TODO remove in composer 2.2
// @phpstan-ignore-next-line
$tableStyle->setVerticalBorderChar('');
}
$tableStyle->setCellRowContentFormat('%s ');
$table->setHeaders(array('Name', 'Version', 'License'));
foreach ($packages as $package) {

View File

@ -64,13 +64,7 @@ class Compiler
$this->versionDate = new \DateTime(trim($process->getOutput()));
$this->versionDate->setTimezone(new \DateTimeZone('UTC'));
// TODO in v2.3 always call with an array
if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) {
$process = new Process(array('git', 'describe', '--tags', '--exact-match', 'HEAD'), __DIR__);
} else {
// @phpstan-ignore-next-line
$process = new Process('git describe --tags --exact-match HEAD');
}
if ($process->run() == 0) {
$this->version = trim($process->getOutput());
} else {

View File

@ -514,14 +514,12 @@ class Config
*/
private function process($value, $flags)
{
$config = $this;
if (!is_string($value)) {
return $value;
}
return Preg::replaceCallback('#\{\$(.+)\}#', function ($match) use ($config, $flags) {
return $config->get($match[1], $flags);
return Preg::replaceCallback('#\{\$(.+)\}#', function ($match) use ($flags) {
return $this->get($match[1], $flags);
}, $value);
}

View File

@ -440,8 +440,7 @@ class Application extends BaseApplication
} catch (\InvalidArgumentException $e) {
if ($required) {
$this->io->writeError($e->getMessage());
// TODO composer 2.3 simplify to $this->areExceptionsCaught()
if (!method_exists($this, 'areExceptionsCaught') || $this->areExceptionsCaught()) {
if ($this->areExceptionsCaught()) {
exit(1);
}
throw $e;

View File

@ -12,6 +12,7 @@
namespace Composer\Console;
use Closure;
use Composer\Pcre\Preg;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
@ -64,18 +65,19 @@ class HtmlOutputFormatter extends OutputFormatter
{
$formatted = parent::format($message);
if ($formatted === null) {
return null;
}
$clearEscapeCodes = '(?:39|49|0|22|24|25|27|28)';
// TODO in 2.3 replace with Closure::fromCallable and then use Preg::replaceCallback
return preg_replace_callback("{\033\[([0-9;]+)m(.*?)\033\[(?:".$clearEscapeCodes.";)*?".$clearEscapeCodes."m}s", array($this, 'formatHtml'), $formatted);
return Preg::replaceCallback("{\033\[([0-9;]+)m(.*?)\033\[(?:".$clearEscapeCodes.";)*?".$clearEscapeCodes."m}s", Closure::fromCallable([$this, 'formatHtml']), $formatted);
}
/**
* @param string[] $matches
*
* @return string
*/
private function formatHtml($matches)
private function formatHtml(array $matches): string
{
$out = '<span style="';
foreach (explode(';', $matches[1]) as $code) {

View File

@ -64,11 +64,10 @@ class DefaultPolicy implements PolicyInterface
public function selectPreferredPackages(Pool $pool, array $literals, $requiredPackage = null)
{
$packages = $this->groupLiteralsByName($pool, $literals);
$policy = $this;
foreach ($packages as &$nameLiterals) {
usort($nameLiterals, function ($a, $b) use ($policy, $pool, $requiredPackage) {
return $policy->compareByPriority($pool, $pool->literalToPackage($a), $pool->literalToPackage($b), $requiredPackage, true);
usort($nameLiterals, function ($a, $b) use ($pool, $requiredPackage) {
return $this->compareByPriority($pool, $pool->literalToPackage($a), $pool->literalToPackage($b), $requiredPackage, true);
});
}
@ -80,8 +79,8 @@ class DefaultPolicy implements PolicyInterface
$selected = \call_user_func_array('array_merge', array_values($packages));
// now sort the result across all packages to respect replaces across packages
usort($selected, function ($a, $b) use ($policy, $pool, $requiredPackage) {
return $policy->compareByPriority($pool, $pool->literalToPackage($a), $pool->literalToPackage($b), $requiredPackage);
usort($selected, function ($a, $b) use ($pool, $requiredPackage) {
return $this->compareByPriority($pool, $pool->literalToPackage($a), $pool->literalToPackage($b), $requiredPackage);
});
return $selected;

View File

@ -28,9 +28,8 @@ abstract class ArchiveDownloader extends FileDownloader
{
/**
* @var array<string, true>
* @protected
*/
public $cleanupExecuted = array();
protected $cleanupExecuted = array();
/**
* @return PromiseInterface|null
@ -92,22 +91,20 @@ abstract class ArchiveDownloader extends FileDownloader
$fileName = $this->getFileName($package, $path);
$filesystem = $this->filesystem;
$self = $this;
$cleanup = function () use ($path, $filesystem, $temporaryDir, $package, $self) {
$cleanup = function () use ($path, $filesystem, $temporaryDir, $package) {
// remove cache if the file was corrupted
$self->clearLastCacheWrite($package);
$this->clearLastCacheWrite($package);
// clean up
$filesystem->removeDirectory($temporaryDir);
if (is_dir($path) && realpath($path) !== getcwd()) {
$filesystem->removeDirectory($path);
}
$self->removeCleanupPath($package, $temporaryDir);
$self->removeCleanupPath($package, realpath($path));
$this->removeCleanupPath($package, $temporaryDir);
$this->removeCleanupPath($package, realpath($path));
};
$promise = null;
try {
$promise = $this->extract($package, $fileName, $temporaryDir);
} catch (\Exception $e) {
@ -119,7 +116,7 @@ abstract class ArchiveDownloader extends FileDownloader
$promise = \React\Promise\resolve();
}
return $promise->then(function () use ($self, $package, $filesystem, $fileName, $temporaryDir, $path) {
return $promise->then(function () use ($package, $filesystem, $fileName, $temporaryDir, $path) {
$filesystem->unlink($fileName);
/**
@ -203,9 +200,9 @@ abstract class ArchiveDownloader extends FileDownloader
$promise = $filesystem->removeDirectoryAsync($temporaryDir);
return $promise->then(function () use ($self, $package, $path, $temporaryDir) {
$self->removeCleanupPath($package, $temporaryDir);
$self->removeCleanupPath($package, $path);
return $promise->then(function () use ($package, $path, $temporaryDir) {
$this->removeCleanupPath($package, $temporaryDir);
$this->removeCleanupPath($package, $path);
});
}, function ($e) use ($cleanup) {
$cleanup();

View File

@ -192,16 +192,15 @@ class DownloadManager
$sources = $this->getAvailableSources($package, $prevPackage);
$io = $this->io;
$self = $this;
$download = function ($retry = false) use (&$sources, $io, $package, $self, $targetDir, &$download, $prevPackage) {
$download = function ($retry = false) use (&$sources, $io, $package, $targetDir, &$download, $prevPackage) {
$source = array_shift($sources);
if ($retry) {
$io->writeError(' <warning>Now trying to download from ' . $source . '</warning>');
}
$package->setInstallationSource($source);
$downloader = $self->getDownloaderForPackage($package);
$downloader = $this->getDownloaderForPackage($package);
if (!$downloader) {
return \React\Promise\resolve();
}
@ -332,10 +331,8 @@ class DownloadManager
// we wipe the dir and do a new install instead of updating it
$promise = $initialDownloader->remove($initial, $targetDir);
if ($promise) {
$self = $this;
return $promise->then(function ($res) use ($self, $target, $targetDir) {
return $self->install($target, $targetDir);
return $promise->then(function ($res) use ($target, $targetDir) {
return $this->install($target, $targetDir);
});
}

View File

@ -150,11 +150,10 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
$httpDownloader = $this->httpDownloader;
$eventDispatcher = $this->eventDispatcher;
$filesystem = $this->filesystem;
$self = $this;
$accept = null;
$reject = null;
$download = function () use ($io, $output, $httpDownloader, $cache, $cacheKeyGenerator, $eventDispatcher, $package, $fileName, &$urls, &$accept, &$reject, $self) {
$download = function () use ($io, $output, $httpDownloader, $cache, $cacheKeyGenerator, $eventDispatcher, $package, $fileName, &$urls, &$accept, &$reject) {
/** @var array{base: string, processed: string, cacheKey: string} $url */
$url = reset($urls);
$index = key($urls);
@ -184,7 +183,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
// the cache is corrupt the archive will be deleted and the next attempt will re-download it
// see https://github.com/composer/composer/issues/10028
if (!$cache->isReadOnly()) {
$self->lastCacheWrites[$package->getName()] = $cacheKey;
$this->lastCacheWrites[$package->getName()] = $cacheKey;
}
$result = \React\Promise\resolve($fileName);
} else {
@ -222,13 +221,13 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
});
};
$accept = function ($response) use ($cache, $package, $fileName, $self, &$urls) {
$accept = function ($response) use ($cache, $package, $fileName, &$urls) {
$url = reset($urls);
$cacheKey = $url['cacheKey'];
FileDownloader::$downloadMetadata[$package->getName()] = @filesize($fileName) ?: $response->getHeader('Content-Length') ?: '?';
if ($cache && !$cache->isReadOnly()) {
$self->lastCacheWrites[$package->getName()] = $cacheKey;
$this->lastCacheWrites[$package->getName()] = $cacheKey;
$cache->copyFrom($cacheKey, $fileName);
}
@ -237,12 +236,12 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
return $fileName;
};
$reject = function ($e) use ($io, &$urls, $download, $fileName, $package, &$retries, $filesystem, $self) {
$reject = function ($e) use ($io, &$urls, $download, $fileName, $package, &$retries, $filesystem) {
// clean up
if (file_exists($fileName)) {
$filesystem->unlink($fileName);
}
$self->clearLastCacheWrite($package);
$this->clearLastCacheWrite($package);
if ($e instanceof IrrecoverableDownloadException) {
throw $e;
@ -361,12 +360,9 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
}
/**
* TODO mark private in v3
* @protected This is public due to PHP 5.3
*
* @return void
*/
public function clearLastCacheWrite(PackageInterface $package)
protected function clearLastCacheWrite(PackageInterface $package)
{
if ($this->cache && isset($this->lastCacheWrites[$package->getName()])) {
$this->cache->remove($this->lastCacheWrites[$package->getName()]);
@ -375,27 +371,21 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
}
/**
* TODO mark private in v3
* @protected This is public due to PHP 5.3
*
* @param string $path
*
* @return void
*/
public function addCleanupPath(PackageInterface $package, $path)
protected function addCleanupPath(PackageInterface $package, $path)
{
$this->additionalCleanupPaths[$package->getName()][] = $path;
}
/**
* TODO mark private in v3
* @protected This is public due to PHP 5.3
*
* @param string $path
*
* @return void
*/
public function removeCleanupPath(PackageInterface $package, $path)
protected function removeCleanupPath(PackageInterface $package, $path)
{
if (isset($this->additionalCleanupPaths[$package->getName()])) {
$idx = array_search($path, $this->additionalCleanupPaths[$package->getName()]);
@ -416,11 +406,9 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve();
}
$self = $this;
$io = $this->io;
return $promise->then(function () use ($self, $target, $path) {
$promise = $self->install($target, $path, false);
return $promise->then(function () use ($target, $path) {
$promise = $this->install($target, $path, false);
return $promise;
});

View File

@ -126,9 +126,8 @@ class ZipDownloader extends ArchiveDownloader
$executable = $commandSpec[0];
$self = $this;
$io = $this->io;
$tryFallback = function ($processError) use ($isLastChance, $io, $self, $file, $path, $package, $executable) {
$tryFallback = function ($processError) use ($isLastChance, $io, $file, $path, $package, $executable) {
if ($isLastChance) {
throw $processError;
}
@ -143,15 +142,15 @@ class ZipDownloader extends ArchiveDownloader
$io->writeError(' Unzip with '.$executable.' command failed, falling back to ZipArchive class');
}
return $self->extractWithZipArchive($package, $file, $path);
return $this->extractWithZipArchive($package, $file, $path);
};
try {
$promise = $this->process->executeAsync($command);
return $promise->then(function ($process) use ($tryFallback, $command, $package, $file, $self) {
return $promise->then(function ($process) use ($tryFallback, $command, $package, $file) {
if (!$process->isSuccessful()) {
if (isset($self->cleanupExecuted[$package->getName()])) {
if (isset($this->cleanupExecuted[$package->getName()])) {
throw new \RuntimeException('Failed to extract '.$package->getName().' as the installation was aborted by another package operation.');
}
@ -161,8 +160,6 @@ class ZipDownloader extends ArchiveDownloader
return $tryFallback(new \RuntimeException('Failed to extract '.$package->getName().': ('.$process->getExitCode().') '.$command."\n\n".$output));
}
});
} catch (\Exception $e) {
return $tryFallback($e);
} catch (\Throwable $e) {
return $tryFallback($e);
}
@ -174,11 +171,8 @@ class ZipDownloader extends ArchiveDownloader
* @param string $file File to extract
* @param string $path Path where to extract file
* @return PromiseInterface
*
* TODO v3 should make this private once we can drop PHP 5.3 support
* @protected
*/
public function extractWithZipArchive(PackageInterface $package, $file, $path)
private function extractWithZipArchive(PackageInterface $package, $file, $path)
{
$processError = null;
$zipArchive = $this->zipArchiveObject ?: new ZipArchive();
@ -199,8 +193,6 @@ class ZipDownloader extends ArchiveDownloader
}
} catch (\ErrorException $e) {
$processError = new \RuntimeException('The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems): '.$e->getMessage(), 0, $e);
} catch (\Exception $e) {
$processError = $e;
} catch (\Throwable $e) {
$processError = $e;
}
@ -214,11 +206,8 @@ class ZipDownloader extends ArchiveDownloader
* @param string $file File to extract
* @param string $path Path where to extract file
* @return PromiseInterface|null
*
* TODO v3 should make this private once we can drop PHP 5.3 support
* @protected
*/
public function extract(PackageInterface $package, $file, $path)
protected function extract(PackageInterface $package, $file, $path)
{
return $this->extractWithSystemUnzip($package, $file, $path);
}

View File

@ -323,18 +323,10 @@ class EventDispatcher
break;
}
}
} catch (\Exception $e) { // TODO Composer 2.2 turn all this into a finally
} finally {
$this->popEvent();
throw $e;
} catch (\Throwable $e) {
$this->popEvent();
throw $e;
}
$this->popEvent();
return $returnMax;
}

View File

@ -436,7 +436,6 @@ class InstallationManager
}
$dispatcher = $this->eventDispatcher;
$installManager = $this;
$io = $this->io;
$promise = $installer->prepare($opType, $package, $initialPackage);
@ -444,11 +443,11 @@ class InstallationManager
$promise = \React\Promise\resolve();
}
$promise = $promise->then(function () use ($opType, $installManager, $repo, $operation) {
return $installManager->$opType($repo, $operation);
$promise = $promise->then(function () use ($opType, $repo, $operation) {
return $this->$opType($repo, $operation);
})->then($cleanupPromises[$index])
->then(function () use ($installManager, $devMode, $repo) {
$repo->write($devMode, $installManager);
->then(function () use ($devMode, $repo) {
$repo->write($devMode, $this);
}, function ($e) use ($opType, $package, $io) {
$io->writeError(' <error>' . ucfirst($opType) .' of '.$package->getPrettyName().' failed</error>');

View File

@ -287,15 +287,8 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
$promise = \React\Promise\resolve();
}
$self = $this;
return $promise->then(function () use ($self, $target) {
$reflMethod = new \ReflectionMethod($self, 'installCode');
$reflMethod->setAccessible(true);
// equivalent of $this->installCode($target) with php 5.3 support
// TODO remove this once 5.3 support is dropped
return $reflMethod->invoke($self, $target);
return $promise->then(function () use ($target) {
return $this->installCode($target);
});
}

View File

@ -70,15 +70,12 @@ class PluginInstaller extends LibraryInstaller
$promise = \React\Promise\resolve();
}
$pluginManager = $this->composer->getPluginManager();
$self = $this;
return $promise->then(function () use ($self, $pluginManager, $package, $repo) {
return $promise->then(function () use ($package, $repo) {
try {
Platform::workaroundFilesystemIssues();
$pluginManager->registerPackage($package, true);
$this->composer->getPluginManager()->registerPackage($package, true);
} catch (\Exception $e) {
$self->rollbackInstall($e, $repo, $package);
$this->rollbackInstall($e, $repo, $package);
}
});
}
@ -93,16 +90,13 @@ class PluginInstaller extends LibraryInstaller
$promise = \React\Promise\resolve();
}
$pluginManager = $this->composer->getPluginManager();
$self = $this;
return $promise->then(function () use ($self, $pluginManager, $initial, $target, $repo) {
return $promise->then(function () use ($initial, $target, $repo) {
try {
Platform::workaroundFilesystemIssues();
$pluginManager->deactivatePackage($initial);
$pluginManager->registerPackage($target, true);
$this->composer->getPluginManager()->deactivatePackage($initial);
$this->composer->getPluginManager()->registerPackage($target, true);
} catch (\Exception $e) {
$self->rollbackInstall($e, $repo, $target);
$this->rollbackInstall($e, $repo, $target);
}
});
}
@ -114,13 +108,7 @@ class PluginInstaller extends LibraryInstaller
return parent::uninstall($repo, $package);
}
/**
* TODO v3 should make this private once we can drop PHP 5.3 support
* @private
*
* @return void
*/
public function rollbackInstall(\Exception $e, InstalledRepositoryInterface $repo, PackageInterface $package)
private function rollbackInstall(\Exception $e, InstalledRepositoryInterface $repo, PackageInterface $package): void
{
$this->io->writeError('Plugin initialization failed ('.$e->getMessage().'), uninstalling plugin');
parent::uninstall($repo, $package);

View File

@ -294,12 +294,10 @@ class JsonManipulator
return false;
}
$that = $this;
// child exists
$childRegex = '{'.self::$DEFINES.'(?P<start>"'.preg_quote($name).'"\s*:\s*)(?P<content>(?&json))(?P<end>,?)}x';
if (Preg::isMatch($childRegex, $children, $matches)) {
$children = Preg::replaceCallback($childRegex, function ($matches) use ($subName, $value, $that) {
$children = Preg::replaceCallback($childRegex, function ($matches) use ($subName, $value) {
if ($subName !== null) {
$curVal = json_decode($matches['content'], true);
if (!is_array($curVal)) {
@ -309,7 +307,7 @@ class JsonManipulator
$value = $curVal;
}
return $matches['start'] . $that->format($value, 1) . $matches['end'];
return $matches['start'] . $this->format($value, 1) . $matches['end'];
}, $children);
} else {
Preg::match('#^{ (?P<leadingspace>\s*?) (?P<content>\S+.*?)? (?P<trailingspace>\s*) }$#sx', $children, $match);
@ -452,12 +450,11 @@ class JsonManipulator
return true;
}
$that = $this;
$this->contents = Preg::replaceCallback($nodeRegex, function ($matches) use ($that, $name, $subName, $childrenClean) {
$this->contents = Preg::replaceCallback($nodeRegex, function ($matches) use ($name, $subName, $childrenClean) {
if ($subName !== null) {
$curVal = json_decode($matches['content'], true);
unset($curVal[$name][$subName]);
$childrenClean = $that->format($curVal);
$childrenClean = $this->format($curVal);
}
return $matches['start'] . $childrenClean . $matches['end'];

View File

@ -106,28 +106,23 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
private $partialPackagesByName = null;
/**
* TODO v3 should make this private once we can drop PHP 5.3 support
* @private
* @var array list of package names which are fresh and can be loaded from the cache directly in case loadPackage is called several times
* useful for v2 metadata repositories with lazy providers
* @phpstan-var array<string, true>
*/
public $freshMetadataUrls = array();
private $freshMetadataUrls = array();
/**
* TODO v3 should make this private once we can drop PHP 5.3 support
* @private
* @var array list of package names which returned a 404 and should not be re-fetched in case loadPackage is called several times
* useful for v2 metadata repositories with lazy providers
* @phpstan-var array<string, true>
*/
public $packagesNotFoundCache = array();
private $packagesNotFoundCache = array();
/**
* TODO v3 should make this private once we can drop PHP 5.3 support
* @private
* @var VersionParser
*/
public $versionParser;
private $versionParser;
/**
* @param array<string, mixed> $repoConfig
@ -867,7 +862,6 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$packages = array();
$namesFound = array();
$promises = array();
$repo = $this;
if (!$this->lazyProvidersUrl) {
throw new \LogicException('loadAsyncPackages only supports v2 protocol composer repos with a metadata-url');
@ -903,7 +897,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
}
$promises[] = $this->asyncFetchFile($url, $cacheKey, $lastModified)
->then(function ($response) use (&$packages, &$namesFound, $url, $cacheKey, $contents, $realName, $constraint, $repo, $acceptableStabilities, $stabilityFlags, $alreadyLoaded) {
->then(function ($response) use (&$packages, &$namesFound, $url, $cacheKey, $contents, $realName, $constraint, $acceptableStabilities, $stabilityFlags, $alreadyLoaded) {
$packagesSource = 'downloaded file ('.Url::sanitize($url).')';
if (true === $response) {
@ -925,10 +919,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$versionsToLoad = array();
foreach ($versions as $version) {
if (!isset($version['version_normalized'])) {
$version['version_normalized'] = $repo->versionParser->normalize($version['version']);
$version['version_normalized'] = $this->versionParser->normalize($version['version']);
} elseif ($version['version_normalized'] === VersionParser::DEFAULT_BRANCH_ALIAS) {
// handling of existing repos which need to remain composer v1 compatible, in case the version_normalized contained VersionParser::DEFAULT_BRANCH_ALIAS, we renormalize it
$version['version_normalized'] = $repo->versionParser->normalize($version['version']);
$version['version_normalized'] = $this->versionParser->normalize($version['version']);
}
// avoid loading packages which have already been loaded
@ -936,18 +930,18 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
continue;
}
if ($repo->isVersionAcceptable($constraint, $realName, $version, $acceptableStabilities, $stabilityFlags)) {
if ($this->isVersionAcceptable($constraint, $realName, $version, $acceptableStabilities, $stabilityFlags)) {
$versionsToLoad[] = $version;
}
}
$loadedPackages = $repo->createPackages($versionsToLoad, $packagesSource);
$loadedPackages = $this->createPackages($versionsToLoad, $packagesSource);
foreach ($loadedPackages as $package) {
$package->setRepository($repo);
$package->setRepository($this);
$packages[spl_object_hash($package)] = $package;
if ($package instanceof AliasPackage && !isset($packages[spl_object_hash($package->getAliasOf())])) {
$package->getAliasOf()->setRepository($repo);
$package->getAliasOf()->setRepository($this);
$packages[spl_object_hash($package->getAliasOf())] = $package->getAliasOf();
}
}
@ -961,10 +955,6 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
}
/**
* TODO v3 should make this private once we can drop PHP 5.3 support
*
* @private
*
* @param ConstraintInterface|null $constraint
* @param string $name package name (must be lowercased already)
* @param array<string, mixed> $versionData
@ -975,7 +965,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
*
* @return bool
*/
public function isVersionAcceptable($constraint, $name, $versionData, array $acceptableStabilities = null, array $stabilityFlags = null)
private function isVersionAcceptable($constraint, $name, $versionData, array $acceptableStabilities = null, array $stabilityFlags = null)
{
$versions = array($versionData['version_normalized']);
@ -1248,15 +1238,12 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
}
/**
* TODO v3 should make this private once we can drop PHP 5.3 support
* @private
*
* @param mixed[] $packages
* @param string|null $source
*
* @return list<CompletePackage|CompleteAliasPackage>
*/
public function createPackages(array $packages, $source = null)
private function createPackages(array $packages, $source = null)
{
if (!$packages) {
return array();
@ -1499,25 +1486,24 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$cache = $this->cache;
$degradedMode = &$this->degradedMode;
$eventDispatcher = $this->eventDispatcher;
$repo = $this;
$accept = function ($response) use ($io, $url, $filename, $cache, $cacheKey, $eventDispatcher, $repo) {
$accept = function ($response) use ($io, $url, $filename, $cache, $cacheKey, $eventDispatcher) {
// package not found is acceptable for a v2 protocol repository
if ($response->getStatusCode() === 404) {
$repo->packagesNotFoundCache[$filename] = true;
$this->packagesNotFoundCache[$filename] = true;
return array('packages' => array());
}
$json = (string) $response->getBody();
if ($json === '' && $response->getStatusCode() === 304) {
$repo->freshMetadataUrls[$filename] = true;
$this->freshMetadataUrls[$filename] = true;
return true;
}
if ($eventDispatcher) {
$postFileDownloadEvent = new PostFileDownloadEvent(PluginEvents::POST_FILE_DOWNLOAD, null, null, $filename, 'metadata', array('response' => $response, 'repository' => $repo));
$postFileDownloadEvent = new PostFileDownloadEvent(PluginEvents::POST_FILE_DOWNLOAD, null, null, $filename, 'metadata', array('response' => $response, 'repository' => $this));
$eventDispatcher->dispatch($postFileDownloadEvent->getName(), $postFileDownloadEvent);
}
@ -1533,14 +1519,14 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
if (!$cache->isReadOnly()) {
$cache->write($cacheKey, $json);
}
$repo->freshMetadataUrls[$filename] = true;
$this->freshMetadataUrls[$filename] = true;
return $data;
};
$reject = function ($e) use ($filename, $accept, $io, $url, &$degradedMode, $repo, $lastModifiedTime) {
$reject = function ($e) use ($filename, $accept, $io, $url, &$degradedMode, $lastModifiedTime) {
if ($e instanceof TransportException && $e->getStatusCode() === 404) {
$repo->packagesNotFoundCache[$filename] = true;
$this->packagesNotFoundCache[$filename] = true;
return false;
}

View File

@ -157,9 +157,7 @@ class Filesystem
$promise = $this->getProcess()->executeAsync($cmd);
$self = $this;
return $promise->then(function ($process) use ($directory, $self) {
return $promise->then(function ($process) use ($directory) {
// clear stat cache because external processes aren't tracked by the php stat cache
clearstatcache();
@ -169,7 +167,7 @@ class Filesystem
}
}
return \React\Promise\resolve($self->removeDirectoryPhp($directory));
return \React\Promise\resolve($this->removeDirectoryPhp($directory));
});
}

View File

@ -264,7 +264,6 @@ class HttpDownloader
};
}
$downloader = $this;
$curl = $this->curl;
$canceler = function () use (&$job, $curl) {
@ -282,19 +281,18 @@ class HttpDownloader
};
$promise = new Promise($resolver, $canceler);
$promise = $promise->then(function ($response) use (&$job, $downloader) {
$promise = $promise->then(function ($response) use (&$job) {
$job['status'] = HttpDownloader::STATUS_COMPLETED;
$job['response'] = $response;
// TODO 3.0 this should be done directly on $this when PHP 5.3 is dropped
$downloader->markJobDone();
$this->markJobDone();
return $response;
}, function ($e) use (&$job, $downloader) {
}, function ($e) use (&$job) {
$job['status'] = HttpDownloader::STATUS_FAILED;
$job['exception'] = $e;
$downloader->markJobDone();
$this->markJobDone();
throw $e;
});
@ -351,11 +349,7 @@ class HttpDownloader
}
}
/**
* @private
* @return void
*/
public function markJobDone()
private function markJobDone(): void
{
$this->runningJobs--;
}

View File

@ -518,13 +518,7 @@ class Perforce
{
$command = $this->generateP4Command(' login -a');
// TODO in v3 generate command as an array
if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) {
$process = Process::fromShellCommandline($command, null, null, $password);
} else {
// @phpstan-ignore-next-line
$process = new Process($command, null, null, $password);
}
return $process->run();
}

View File

@ -115,16 +115,6 @@ class ProcessExecutor
$this->io->writeError('Executing command ('.($cwd ?: 'CWD').'): '.$safeCommand);
}
// TODO in 2.2, these two checks can be dropped as Symfony 4+ supports them out of the box
// make sure that null translate to the proper directory in case the dir is a symlink
// and we call a git command, because msysgit does not handle symlinks properly
if (null === $cwd && Platform::isWindows() && false !== strpos($command, 'git') && getcwd()) {
$cwd = realpath(getcwd());
}
if (null !== $cwd && !is_dir($cwd)) {
throw new \RuntimeException('The given CWD for the process does not exist: '.$cwd);
}
$this->captureOutput = func_num_args() > 3;
$this->errorOutput = '';
@ -181,8 +171,6 @@ class ProcessExecutor
$job['reject'] = $reject;
};
$self = $this;
$canceler = function () use (&$job) {
if ($job['status'] === ProcessExecutor::STATUS_QUEUED) {
$job['status'] = ProcessExecutor::STATUS_ABORTED;
@ -204,21 +192,20 @@ class ProcessExecutor
};
$promise = new Promise($resolver, $canceler);
$promise = $promise->then(function () use (&$job, $self) {
$promise = $promise->then(function () use (&$job) {
if ($job['process']->isSuccessful()) {
$job['status'] = ProcessExecutor::STATUS_COMPLETED;
} else {
$job['status'] = ProcessExecutor::STATUS_FAILED;
}
// TODO 3.0 this should be done directly on $this when PHP 5.3 is dropped
$self->markJobDone();
$this->markJobDone();
return $job['process'];
}, function ($e) use (&$job, $self) {
}, function ($e) use (&$job) {
$job['status'] = ProcessExecutor::STATUS_FAILED;
$self->markJobDone();
$this->markJobDone();
throw $e;
});
@ -261,16 +248,6 @@ class ProcessExecutor
$this->io->writeError('Executing async command ('.($cwd ?: 'CWD').'): '.$safeCommand);
}
// TODO in 2.2, these two checks can be dropped as Symfony 4+ supports them out of the box
// make sure that null translate to the proper directory in case the dir is a symlink
// and we call a git command, because msysgit does not handle symlinks properly
if (null === $cwd && Platform::isWindows() && false !== strpos($command, 'git') && getcwd()) {
$cwd = realpath(getcwd());
}
if (null !== $cwd && !is_dir($cwd)) {
throw new \RuntimeException('The given CWD for the process does not exist: '.$cwd);
}
try {
// TODO in v3, commands should be passed in as arrays of cmd + args
if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) {
@ -278,10 +255,6 @@ class ProcessExecutor
} else {
$process = new Process($command, $cwd, null, null, static::getTimeout());
}
} catch (\Exception $e) {
call_user_func($job['reject'], $e);
return;
} catch (\Throwable $e) {
call_user_func($job['reject'], $e);
@ -292,10 +265,6 @@ class ProcessExecutor
try {
$process->start();
} catch (\Exception $e) {
call_user_func($job['reject'], $e);
return;
} catch (\Throwable $e) {
call_user_func($job['reject'], $e);
@ -367,12 +336,7 @@ class ProcessExecutor
return $active;
}
/**
* @private
*
* @return void
*/
public function markJobDone()
private function markJobDone(): void
{
$this->runningJobs--;
}

View File

@ -573,7 +573,6 @@ class RemoteFilesystem
// passing `null` to file_get_contents will convert `null` to `0` and return 0 bytes
$result = file_get_contents($fileUrl, false, $context);
}
} catch (\Exception $e) {
} catch (\Throwable $e) {
}

View File

@ -85,13 +85,7 @@ class AllFunctionalTest extends TestCase
}
}
// TODO in v2.3 always call with an array
if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) {
$proc = new Process(array((defined('PHP_BINARY') ? PHP_BINARY : 'php'), '-dphar.readonly=0', './bin/compile'), $target);
} else {
// @phpstan-ignore-next-line
$proc = new Process((defined('PHP_BINARY') ? escapeshellcmd(PHP_BINARY) : 'php').' -dphar.readonly=0 '.escapeshellarg('./bin/compile'), $target);
}
$proc = new Process([PHP_BINARY, '-dphar.readonly=0', './bin/compile'], $target);
$exitcode = $proc->run();
if ($exitcode !== 0 || trim($proc->getOutput())) {

View File

@ -92,7 +92,6 @@ class AutoloadGeneratorTest extends TestCase
protected function setUp(): void
{
$this->fs = new Filesystem;
$that = $this;
$this->workingDir = $this->getUniqueTmpDirectory();
$this->vendorDir = $this->workingDir.DIRECTORY_SEPARATOR.'composer-test-autoload';
@ -101,8 +100,8 @@ class AutoloadGeneratorTest extends TestCase
$this->config = $this->getMockBuilder('Composer\Config')->getMock();
$this->configValueMap = array(
'vendor-dir' => function () use ($that) {
return $that->vendorDir;
'vendor-dir' => function () {
return $this->vendorDir;
},
'platform-check' => function () {
return true;
@ -111,10 +110,10 @@ class AutoloadGeneratorTest extends TestCase
$this->config->expects($this->atLeastOnce())
->method('get')
->will($this->returnCallback(function ($arg) use ($that) {
->will($this->returnCallback(function ($arg) {
$ret = null;
if (isset($that->configValueMap[$arg])) {
$ret = $that->configValueMap[$arg];
if (isset($this->configValueMap[$arg])) {
$ret = $this->configValueMap[$arg];
if (is_callable($ret)) {
$ret = $ret();
}
@ -131,10 +130,10 @@ class AutoloadGeneratorTest extends TestCase
->getMock();
$this->im->expects($this->any())
->method('getInstallPath')
->will($this->returnCallback(function ($package) use ($that) {
->will($this->returnCallback(function ($package) {
$targetDir = $package->getTargetDir();
return $that->vendorDir.'/'.$package->getName() . ($targetDir ? '/'.$targetDir : '');
return $this->vendorDir.'/'.$package->getName() . ($targetDir ? '/'.$targetDir : '');
}));
$this->repository = $this->getMockBuilder('Composer\Repository\InstalledRepositoryInterface')->getMock();
$this->repository->expects($this->any())

View File

@ -180,8 +180,6 @@ class FileDownloaderTest extends TestCase
public function testDownloadWithCustomProcessedUrl()
{
$self = $this;
$path = $this->getUniqueTmpDirectory();
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
@ -232,16 +230,16 @@ class FileDownloaderTest extends TestCase
$cacheMock
->expects($this->any())
->method('copyTo')
->will($this->returnCallback(function ($cacheKey) use ($self, $expectedCacheKey) {
$self->assertEquals($expectedCacheKey, $cacheKey, 'Failed assertion on $cacheKey argument of Cache::copyTo method:');
->will($this->returnCallback(function ($cacheKey) use ($expectedCacheKey) {
$this->assertEquals($expectedCacheKey, $cacheKey, 'Failed assertion on $cacheKey argument of Cache::copyTo method:');
return false;
}));
$cacheMock
->expects($this->any())
->method('copyFrom')
->will($this->returnCallback(function ($cacheKey) use ($self, $expectedCacheKey) {
$self->assertEquals($expectedCacheKey, $cacheKey, 'Failed assertion on $cacheKey argument of Cache::copyFrom method:');
->will($this->returnCallback(function ($cacheKey) use ($expectedCacheKey) {
$this->assertEquals($expectedCacheKey, $cacheKey, 'Failed assertion on $cacheKey argument of Cache::copyFrom method:');
return false;
}));
@ -250,8 +248,8 @@ class FileDownloaderTest extends TestCase
$httpDownloaderMock
->expects($this->any())
->method('addCopy')
->will($this->returnCallback(function ($url) use ($self, $expectedUrl) {
$self->assertEquals($expectedUrl, $url, 'Failed assertion on $url argument of HttpDownloader::addCopy method:');
->will($this->returnCallback(function ($url) use ($expectedUrl) {
$this->assertEquals($expectedUrl, $url, 'Failed assertion on $url argument of HttpDownloader::addCopy method:');
return \React\Promise\resolve(
new Response(array('url' => 'http://example.org/'), 200, array(), 'file~')
@ -281,8 +279,6 @@ class FileDownloaderTest extends TestCase
public function testDownloadWithCustomCacheKey()
{
$self = $this;
$path = $this->getUniqueTmpDirectory();
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
@ -334,16 +330,16 @@ class FileDownloaderTest extends TestCase
$cacheMock
->expects($this->any())
->method('copyTo')
->will($this->returnCallback(function ($cacheKey) use ($self, $expectedCacheKey) {
$self->assertEquals($expectedCacheKey, $cacheKey, 'Failed assertion on $cacheKey argument of Cache::copyTo method:');
->will($this->returnCallback(function ($cacheKey) use ($expectedCacheKey) {
$this->assertEquals($expectedCacheKey, $cacheKey, 'Failed assertion on $cacheKey argument of Cache::copyTo method:');
return false;
}));
$cacheMock
->expects($this->any())
->method('copyFrom')
->will($this->returnCallback(function ($cacheKey) use ($self, $expectedCacheKey) {
$self->assertEquals($expectedCacheKey, $cacheKey, 'Failed assertion on $cacheKey argument of Cache::copyFrom method:');
->will($this->returnCallback(function ($cacheKey) use ($expectedCacheKey) {
$this->assertEquals($expectedCacheKey, $cacheKey, 'Failed assertion on $cacheKey argument of Cache::copyFrom method:');
return false;
}));
@ -352,8 +348,8 @@ class FileDownloaderTest extends TestCase
$httpDownloaderMock
->expects($this->any())
->method('addCopy')
->will($this->returnCallback(function ($url) use ($self, $expectedUrl) {
$self->assertEquals($expectedUrl, $url, 'Failed assertion on $url argument of HttpDownloader::addCopy method:');
->will($this->returnCallback(function ($url) use ($expectedUrl) {
$this->assertEquals($expectedUrl, $url, 'Failed assertion on $url argument of HttpDownloader::addCopy method:');
return \React\Promise\resolve(
new Response(array('url' => 'http://example.org/'), 200, array(), 'file~')

View File

@ -79,13 +79,12 @@ class BasePackageTest extends TestCase
),
);
$self = $this;
$createPackage = function ($arr) use ($self) {
$package = $self->getMockForAbstractClass('\Composer\Package\BasePackage', array(), '', false);
$package->expects($self->once())->method('isDev')->will($self->returnValue(true));
$package->expects($self->any())->method('getSourceType')->will($self->returnValue('git'));
$package->expects($self->once())->method('getPrettyVersion')->will($self->returnValue('PrettyVersion'));
$package->expects($self->any())->method('getSourceReference')->will($self->returnValue($arr['sourceReference']));
$createPackage = function ($arr) {
$package = $this->getMockForAbstractClass('\Composer\Package\BasePackage', array(), '', false);
$package->expects($this->once())->method('isDev')->will($this->returnValue(true));
$package->expects($this->any())->method('getSourceType')->will($this->returnValue('git'));
$package->expects($this->once())->method('getPrettyVersion')->will($this->returnValue('PrettyVersion'));
$package->expects($this->any())->method('getSourceReference')->will($this->returnValue($arr['sourceReference']));
return array($package, $arr['truncate'], $arr['expected']);
};

View File

@ -37,7 +37,7 @@ class ComposerRepositoryTest extends TestCase
);
$repository = $this->getMockBuilder('Composer\Repository\ComposerRepository')
->onlyMethods(array('loadRootServerFile', 'createPackages'))
->onlyMethods(array('loadRootServerFile'))
->setConstructorArgs(array(
$repoConfig,
new NullIO,
@ -52,22 +52,15 @@ class ComposerRepositoryTest extends TestCase
->method('loadRootServerFile')
->will($this->returnValue($repoPackages));
$stubs = array();
foreach ($expected as $at => $arg) {
$stubs[] = $this->getPackage('stub/stub', '1.0.0');
}
$repository
->expects($this->once())
->method('createPackages')
->with($this->identicalTo($expected), $this->equalTo('root file (http://example.org/packages.json)'))
->will($this->returnValue($stubs));
// Triggers initialization
$packages = $repository->getPackages();
// Final sanity check, ensure the correct number of packages were added.
$this->assertCount(count($expected), $packages);
foreach ($expected as $index => $pkg) {
self::assertSame($pkg['name'].' '.$pkg['version'], $packages[$index]->getName().' '.$packages[$index]->getPrettyVersion());
}
}
public function loadDataProvider()

View File

@ -49,9 +49,8 @@ class GitTest extends TestCase
*/
public function testRunCommandPublicGitHubRepositoryNotInitialClone($protocol, $expectedUrl)
{
$that = $this;
$commandCallable = function ($url) use ($that, $expectedUrl) {
$that->assertSame($expectedUrl, $url);
$commandCallable = function ($url) use ($expectedUrl) {
$this->assertSame($expectedUrl, $url);
return 'git command';
};
@ -75,9 +74,8 @@ class GitTest extends TestCase
{
self::expectException('RuntimeException');
$that = $this;
$commandCallable = function ($url) use ($that) {
$that->assertSame('https://github.com/acme/repo', $url);
$commandCallable = function ($url) {
$this->assertSame('https://github.com/acme/repo', $url);
return 'git command';
};