Merge branch 'master' into 2.0
commit
8584d15e8c
27
CHANGELOG.md
27
CHANGELOG.md
|
@ -1,3 +1,28 @@
|
||||||
|
### [1.10.0] 2020-01-XX
|
||||||
|
|
||||||
|
* Breaking: `composer global exec ...` now executes the process in the current working directory instead of executing it in the global directory.
|
||||||
|
* Warning: Added a warning when class names are being loaded by a PSR-4 or PSR-0 rule only due to classmap optimization, but would not otherwise be autoloadable. The next minor version will stop autoloading these classes so make sure you fix your autoload configs.
|
||||||
|
* Added support for configuring suggestions using config command, e.g. `composer config suggest.foo/bar some text`
|
||||||
|
* Added support for configuring fine-grained preferred-install using config command, e.g. `composer config preferred-install.foo/* dist`
|
||||||
|
* Added `@putenv` script handler to set environment variables from composer.json for following scripts
|
||||||
|
* Added `lock` option that can be set to false, in which case no composer.lock file will be generated
|
||||||
|
* Added support for IPv6 addresses in NO_PROXY
|
||||||
|
* Added package homepage display in the show command
|
||||||
|
* Added debug info about HTTP authentications
|
||||||
|
* Added Symfony 5 compatibility
|
||||||
|
* Added --fixed flag to require command to make it use a fixed constraint instead of a ^x.y constraint when adding the requirement
|
||||||
|
* Fixed GitHub deprecation of access_token query parameter, now using Authorization header
|
||||||
|
* Fixed archive command to persist file permissions inside the zip files
|
||||||
|
* Fixed init/require command to avoid suggesting packages which are already selected in the search results
|
||||||
|
* Fixed create-project UX issues
|
||||||
|
|
||||||
|
### [1.9.2] 2020-01-14
|
||||||
|
|
||||||
|
* Fixed minor git driver bugs
|
||||||
|
* Fixed schema validation for version field to allow dev-* versions too
|
||||||
|
* Fixed external processes' output being formatted even though it should not
|
||||||
|
* Fixed issue with path repositories when trying to install feature branches
|
||||||
|
|
||||||
### [1.9.1] 2019-11-01
|
### [1.9.1] 2019-11-01
|
||||||
|
|
||||||
* Fixed various credential handling issues with gitlab and github
|
* Fixed various credential handling issues with gitlab and github
|
||||||
|
@ -776,6 +801,8 @@
|
||||||
|
|
||||||
* Initial release
|
* Initial release
|
||||||
|
|
||||||
|
[1.10.0]: https://github.com/composer/composer/compare/1.9.2...1.10.0
|
||||||
|
[1.9.2]: https://github.com/composer/composer/compare/1.9.1...1.9.2
|
||||||
[1.9.1]: https://github.com/composer/composer/compare/1.9.0...1.9.1
|
[1.9.1]: https://github.com/composer/composer/compare/1.9.0...1.9.1
|
||||||
[1.9.0]: https://github.com/composer/composer/compare/1.8.6...1.9.0
|
[1.9.0]: https://github.com/composer/composer/compare/1.8.6...1.9.0
|
||||||
[1.8.6]: https://github.com/composer/composer/compare/1.8.5...1.8.6
|
[1.8.6]: https://github.com/composer/composer/compare/1.8.5...1.8.6
|
||||||
|
|
|
@ -285,24 +285,24 @@ EOF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile);
|
$this->filePutContentsIfModified($targetDir.'/autoload_namespaces.php', $namespacesFile);
|
||||||
file_put_contents($targetDir.'/autoload_psr4.php', $psr4File);
|
$this->filePutContentsIfModified($targetDir.'/autoload_psr4.php', $psr4File);
|
||||||
file_put_contents($targetDir.'/autoload_classmap.php', $classmapFile);
|
$this->filePutContentsIfModified($targetDir.'/autoload_classmap.php', $classmapFile);
|
||||||
$includePathFilePath = $targetDir.'/include_paths.php';
|
$includePathFilePath = $targetDir.'/include_paths.php';
|
||||||
if ($includePathFileContents = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
|
if ($includePathFileContents = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
|
||||||
file_put_contents($includePathFilePath, $includePathFileContents);
|
$this->filePutContentsIfModified($includePathFilePath, $includePathFileContents);
|
||||||
} elseif (file_exists($includePathFilePath)) {
|
} elseif (file_exists($includePathFilePath)) {
|
||||||
unlink($includePathFilePath);
|
unlink($includePathFilePath);
|
||||||
}
|
}
|
||||||
$includeFilesFilePath = $targetDir.'/autoload_files.php';
|
$includeFilesFilePath = $targetDir.'/autoload_files.php';
|
||||||
if ($includeFilesFileContents = $this->getIncludeFilesFile($autoloads['files'], $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
|
if ($includeFilesFileContents = $this->getIncludeFilesFile($autoloads['files'], $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
|
||||||
file_put_contents($includeFilesFilePath, $includeFilesFileContents);
|
$this->filePutContentsIfModified($includeFilesFilePath, $includeFilesFileContents);
|
||||||
} elseif (file_exists($includeFilesFilePath)) {
|
} elseif (file_exists($includeFilesFilePath)) {
|
||||||
unlink($includeFilesFilePath);
|
unlink($includeFilesFilePath);
|
||||||
}
|
}
|
||||||
file_put_contents($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion));
|
$this->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion));
|
||||||
file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix));
|
$this->filePutContentsIfModified($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix));
|
||||||
file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion));
|
$this->filePutContentsIfModified($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion));
|
||||||
|
|
||||||
$this->safeCopy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php');
|
$this->safeCopy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php');
|
||||||
$this->safeCopy(__DIR__.'/../../../LICENSE', $targetDir.'/LICENSE');
|
$this->safeCopy(__DIR__.'/../../../LICENSE', $targetDir.'/LICENSE');
|
||||||
|
@ -316,6 +316,16 @@ EOF;
|
||||||
return count($classMap);
|
return count($classMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function filePutContentsIfModified($path, $content)
|
||||||
|
{
|
||||||
|
$currentContent = @file_get_contents($path);
|
||||||
|
if (!$currentContent || ($currentContent != $content)) {
|
||||||
|
return file_put_contents($path, $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist = null, $namespaceFilter = null, $autoloadType = null, array $classMap = array())
|
private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist = null, $namespaceFilter = null, $autoloadType = null, array $classMap = array())
|
||||||
{
|
{
|
||||||
foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType) as $class => $path) {
|
foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType) as $class => $path) {
|
||||||
|
@ -986,7 +996,6 @@ INITIALIZER;
|
||||||
|
|
||||||
$sortedPackages = PackageSorter::sortPackages($packages);
|
$sortedPackages = PackageSorter::sortPackages($packages);
|
||||||
|
|
||||||
|
|
||||||
$sortedPackageMap = array();
|
$sortedPackageMap = array();
|
||||||
|
|
||||||
foreach ($sortedPackages as $package) {
|
foreach ($sortedPackages as $package) {
|
||||||
|
@ -1005,6 +1014,7 @@ INITIALIZER;
|
||||||
*/
|
*/
|
||||||
protected function safeCopy($source, $target)
|
protected function safeCopy($source, $target)
|
||||||
{
|
{
|
||||||
|
if (!file_exists($target) || !file_exists($source) || !$this->filesAreEqual($source, $target)) {
|
||||||
$source = fopen($source, 'r');
|
$source = fopen($source, 'r');
|
||||||
$target = fopen($target, 'w+');
|
$target = fopen($target, 'w+');
|
||||||
|
|
||||||
|
@ -1012,4 +1022,34 @@ INITIALIZER;
|
||||||
fclose($source);
|
fclose($source);
|
||||||
fclose($target);
|
fclose($target);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compare 2 files
|
||||||
|
* https://stackoverflow.com/questions/3060125/can-i-use-file-get-contents-to-compare-two-files
|
||||||
|
*/
|
||||||
|
private function filesAreEqual($a, $b)
|
||||||
|
{
|
||||||
|
// Check if filesize is different
|
||||||
|
if (filesize($a) !== filesize($b)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if content is different
|
||||||
|
$ah = fopen($a, 'rb');
|
||||||
|
$bh = fopen($b, 'rb');
|
||||||
|
|
||||||
|
$result = true;
|
||||||
|
while (!feof($ah)) {
|
||||||
|
if (fread($ah, 8192) != fread($bh, 8192)) {
|
||||||
|
$result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($ah);
|
||||||
|
fclose($bh);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace Composer\Command;
|
||||||
use Composer\Factory;
|
use Composer\Factory;
|
||||||
use Composer\Json\JsonFile;
|
use Composer\Json\JsonFile;
|
||||||
use Composer\Package\BasePackage;
|
use Composer\Package\BasePackage;
|
||||||
|
use Composer\Package\Package;
|
||||||
use Composer\Package\Version\VersionParser;
|
use Composer\Package\Version\VersionParser;
|
||||||
use Composer\Package\Version\VersionSelector;
|
use Composer\Package\Version\VersionSelector;
|
||||||
use Composer\Repository\CompositeRepository;
|
use Composer\Repository\CompositeRepository;
|
||||||
|
@ -702,13 +703,13 @@ EOT
|
||||||
private function getMinimumStability(InputInterface $input)
|
private function getMinimumStability(InputInterface $input)
|
||||||
{
|
{
|
||||||
if ($input->hasOption('stability')) {
|
if ($input->hasOption('stability')) {
|
||||||
return $input->getOption('stability') ?: 'stable';
|
return VersionParser::normalizeStability($input->getOption('stability') ?: 'stable');
|
||||||
}
|
}
|
||||||
|
|
||||||
$file = Factory::getComposerFile();
|
$file = Factory::getComposerFile();
|
||||||
if (is_file($file) && is_readable($file) && is_array($composer = json_decode(file_get_contents($file), true))) {
|
if (is_file($file) && is_readable($file) && is_array($composer = json_decode(file_get_contents($file), true))) {
|
||||||
if (!empty($composer['minimum-stability'])) {
|
if (!empty($composer['minimum-stability'])) {
|
||||||
return $composer['minimum-stability'];
|
return VersionParser::normalizeStability($composer['minimum-stability']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -201,6 +201,9 @@ EOT
|
||||||
if (empty($package)) {
|
if (empty($package)) {
|
||||||
$options = $input->getOptions();
|
$options = $input->getOptions();
|
||||||
if (!isset($options['working-dir']) || !file_exists('composer.json')) {
|
if (!isset($options['working-dir']) || !file_exists('composer.json')) {
|
||||||
|
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $input->getArgument('package')) && !$input->getOption('platform')) {
|
||||||
|
throw new \InvalidArgumentException('Package ' . $packageFilter . ' not found, try using --platform (-p) to show platform packages.');
|
||||||
|
}
|
||||||
throw new \InvalidArgumentException('Package ' . $packageFilter . ' not found');
|
throw new \InvalidArgumentException('Package ' . $packageFilter . ' not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,19 +238,13 @@ EOT
|
||||||
|
|
||||||
return $exitCode;
|
return $exitCode;
|
||||||
}
|
}
|
||||||
$this->printMeta($package, $versions, $installedRepo, $latestPackage ?: null);
|
|
||||||
$this->printLinks($package, 'requires');
|
if ('json' === $format) {
|
||||||
$this->printLinks($package, 'devRequires', 'requires (dev)');
|
$this->printPackageInfoAsJson($package, $versions, $installedRepo, $latestPackage ?: null);
|
||||||
if ($package->getSuggests()) {
|
} else {
|
||||||
$io->write("\n<info>suggests</info>");
|
$this->printPackageInfo($package, $versions, $installedRepo, $latestPackage ?: null);
|
||||||
foreach ($package->getSuggests() as $suggested => $reason) {
|
|
||||||
$io->write($suggested . ' <comment>' . $reason . '</comment>');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->printLinks($package, 'provides');
|
|
||||||
$this->printLinks($package, 'conflicts');
|
|
||||||
$this->printLinks($package, 'replaces');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $exitCode;
|
return $exitCode;
|
||||||
}
|
}
|
||||||
|
@ -570,12 +567,41 @@ EOT
|
||||||
return array($matchedPackage, $versions);
|
return array($matchedPackage, $versions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints package info.
|
||||||
|
*
|
||||||
|
* @param CompletePackageInterface $package
|
||||||
|
* @param array $versions
|
||||||
|
* @param RepositoryInterface $installedRepo
|
||||||
|
* @param PackageInterface|null $latestPackage
|
||||||
|
*/
|
||||||
|
protected function printPackageInfo(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, PackageInterface $latestPackage = null)
|
||||||
|
{
|
||||||
|
$io = $this->getIO();
|
||||||
|
|
||||||
|
$this->printMeta($package, $versions, $installedRepo, $latestPackage ?: null);
|
||||||
|
$this->printLinks($package, 'requires');
|
||||||
|
$this->printLinks($package, 'devRequires', 'requires (dev)');
|
||||||
|
|
||||||
|
if ($package->getSuggests()) {
|
||||||
|
$io->write("\n<info>suggests</info>");
|
||||||
|
foreach ($package->getSuggests() as $suggested => $reason) {
|
||||||
|
$io->write($suggested . ' <comment>' . $reason . '</comment>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->printLinks($package, 'provides');
|
||||||
|
$this->printLinks($package, 'conflicts');
|
||||||
|
$this->printLinks($package, 'replaces');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints package metadata.
|
* Prints package metadata.
|
||||||
*
|
*
|
||||||
* @param CompletePackageInterface $package
|
* @param CompletePackageInterface $package
|
||||||
* @param array $versions
|
* @param array $versions
|
||||||
* @param RepositoryInterface $installedRepo
|
* @param RepositoryInterface $installedRepo
|
||||||
|
* @param PackageInterface|null $latestPackage
|
||||||
*/
|
*/
|
||||||
protected function printMeta(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, PackageInterface $latestPackage = null)
|
protected function printMeta(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, PackageInterface $latestPackage = null)
|
||||||
{
|
{
|
||||||
|
@ -717,6 +743,165 @@ EOT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints package info in JSON format.
|
||||||
|
*
|
||||||
|
* @param CompletePackageInterface $package
|
||||||
|
* @param array $versions
|
||||||
|
* @param RepositoryInterface $installedRepo
|
||||||
|
* @param PackageInterface|null $latestPackage
|
||||||
|
*/
|
||||||
|
protected function printPackageInfoAsJson(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, PackageInterface $latestPackage = null)
|
||||||
|
{
|
||||||
|
$json = array(
|
||||||
|
'name' => $package->getPrettyName(),
|
||||||
|
'description' => $package->getDescription(),
|
||||||
|
'keywords' => $package->getKeywords() ?: array(),
|
||||||
|
'type' => $package->getType(),
|
||||||
|
'homepage' => $package->getHomepage(),
|
||||||
|
'names' => $package->getNames()
|
||||||
|
);
|
||||||
|
|
||||||
|
$json = $this->appendVersions($json, $versions);
|
||||||
|
$json = $this->appendLicenses($json, $package);
|
||||||
|
|
||||||
|
if ($latestPackage) {
|
||||||
|
$json['latest'] = $latestPackage->getPrettyVersion();
|
||||||
|
} else {
|
||||||
|
$latestPackage = $package;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($package->getSourceType()) {
|
||||||
|
$json['source'] = array(
|
||||||
|
'type' => $package->getSourceType(),
|
||||||
|
'url' => $package->getSourceUrl(),
|
||||||
|
'reference' => $package->getSourceReference()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($package->getDistType()) {
|
||||||
|
$json['dist'] = array(
|
||||||
|
'type' => $package->getDistType(),
|
||||||
|
'url' => $package->getDistUrl(),
|
||||||
|
'reference' => $package->getDistReference()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($installedRepo->hasPackage($package)) {
|
||||||
|
$json['path'] = realpath($this->getComposer()->getInstallationManager()->getInstallPath($package));
|
||||||
|
if ($json['path'] === false) {
|
||||||
|
unset($json['path']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($latestPackage->isAbandoned()) {
|
||||||
|
$json['replacement'] = $latestPackage->getReplacementPackage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($package->getSuggests()) {
|
||||||
|
$json['suggests'] = $package->getSuggests();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($package->getSupport()) {
|
||||||
|
$json['support'] = $package->getSupport();
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = $this->appendAutoload($json, $package);
|
||||||
|
|
||||||
|
if ($package->getIncludePaths()) {
|
||||||
|
$json['include_path'] = $package->getIncludePaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = $this->appendLinks($json, $package);
|
||||||
|
|
||||||
|
$this->getIO()->write(JsonFile::encode($json));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function appendVersions($json, array $versions)
|
||||||
|
{
|
||||||
|
uasort($versions, 'version_compare');
|
||||||
|
$versions = array_keys(array_reverse($versions));
|
||||||
|
$json['versions'] = $versions;
|
||||||
|
|
||||||
|
return $json;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function appendLicenses($json, CompletePackageInterface $package)
|
||||||
|
{
|
||||||
|
if ($licenses = $package->getLicense()) {
|
||||||
|
$spdxLicenses = new SpdxLicenses();
|
||||||
|
|
||||||
|
$json['licenses'] = array_map(function ($licenseId) use ($spdxLicenses) {
|
||||||
|
$license = $spdxLicenses->getLicenseByIdentifier($licenseId); // keys: 0 fullname, 1 osi, 2 url
|
||||||
|
|
||||||
|
if (!$license) {
|
||||||
|
return $licenseId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'name' => $license[0],
|
||||||
|
'osi' => $licenseId,
|
||||||
|
'url' => $license[2]
|
||||||
|
);
|
||||||
|
}, $licenses);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $json;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function appendAutoload($json, CompletePackageInterface $package)
|
||||||
|
{
|
||||||
|
if ($package->getAutoload()) {
|
||||||
|
$autoload = array();
|
||||||
|
|
||||||
|
foreach ($package->getAutoload() as $type => $autoloads) {
|
||||||
|
if ($type === 'psr-0' || $type === 'psr-4') {
|
||||||
|
$psr = array();
|
||||||
|
|
||||||
|
foreach ($autoloads as $name => $path) {
|
||||||
|
if (!$path) {
|
||||||
|
$path = '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$psr[$name ?: '*'] = $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
$autoload[$type] = $psr;
|
||||||
|
} elseif ($type === 'classmap') {
|
||||||
|
$autoload['classmap'] = $autoloads;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$json['autoload'] = $autoload;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $json;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function appendLinks($json, CompletePackageInterface $package)
|
||||||
|
{
|
||||||
|
foreach (array('requires', 'devRequires', 'provides', 'conflicts', 'replaces') as $linkType) {
|
||||||
|
$json = $this->appendLink($json, $package, $linkType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $json;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function appendLink($json, CompletePackageInterface $package, $linkType)
|
||||||
|
{
|
||||||
|
$links = $package->{'get' . ucfirst($linkType)}();
|
||||||
|
|
||||||
|
if ($links) {
|
||||||
|
$json[$linkType] = array();
|
||||||
|
|
||||||
|
foreach ($links as $link) {
|
||||||
|
$json[$linkType][$link->getTarget()] = $link->getPrettyConstraint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $json;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init styles for tree
|
* Init styles for tree
|
||||||
*
|
*
|
||||||
|
|
|
@ -143,27 +143,15 @@ class EventDispatcher
|
||||||
*/
|
*/
|
||||||
protected function doDispatch(Event $event)
|
protected function doDispatch(Event $event)
|
||||||
{
|
{
|
||||||
$pathStr = 'PATH';
|
|
||||||
if (!isset($_SERVER[$pathStr]) && isset($_SERVER['Path'])) {
|
|
||||||
$pathStr = 'Path';
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the bin dir to the PATH to make local binaries of deps usable in scripts
|
|
||||||
$binDir = $this->composer->getConfig()->get('bin-dir');
|
|
||||||
if (is_dir($binDir)) {
|
|
||||||
$binDir = realpath($binDir);
|
|
||||||
if (isset($_SERVER[$pathStr]) && !preg_match('{(^|'.PATH_SEPARATOR.')'.preg_quote($binDir).'($|'.PATH_SEPARATOR.')}', $_SERVER[$pathStr])) {
|
|
||||||
$_SERVER[$pathStr] = $binDir.PATH_SEPARATOR.getenv($pathStr);
|
|
||||||
putenv($pathStr.'='.$_SERVER[$pathStr]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$listeners = $this->getListeners($event);
|
$listeners = $this->getListeners($event);
|
||||||
|
|
||||||
$this->pushEvent($event);
|
$this->pushEvent($event);
|
||||||
|
|
||||||
$return = 0;
|
$return = 0;
|
||||||
foreach ($listeners as $callable) {
|
foreach ($listeners as $callable) {
|
||||||
|
|
||||||
|
$this->ensureBinDirIsInPath();
|
||||||
|
|
||||||
if (!is_string($callable)) {
|
if (!is_string($callable)) {
|
||||||
if (!is_callable($callable)) {
|
if (!is_callable($callable)) {
|
||||||
$className = is_object($callable[0]) ? get_class($callable[0]) : $callable[0];
|
$className = is_object($callable[0]) ? get_class($callable[0]) : $callable[0];
|
||||||
|
@ -525,4 +513,22 @@ class EventDispatcher
|
||||||
{
|
{
|
||||||
return array_pop($this->eventStack);
|
return array_pop($this->eventStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function ensureBinDirIsInPath()
|
||||||
|
{
|
||||||
|
$pathStr = 'PATH';
|
||||||
|
if (!isset($_SERVER[$pathStr]) && isset($_SERVER['Path'])) {
|
||||||
|
$pathStr = 'Path';
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the bin dir to the PATH to make local binaries of deps usable in scripts
|
||||||
|
$binDir = $this->composer->getConfig()->get('bin-dir');
|
||||||
|
if (is_dir($binDir)) {
|
||||||
|
$binDir = realpath($binDir);
|
||||||
|
if (isset($_SERVER[$pathStr]) && !preg_match('{(^|'.PATH_SEPARATOR.')'.preg_quote($binDir).'($|'.PATH_SEPARATOR.')}', $_SERVER[$pathStr])) {
|
||||||
|
$_SERVER[$pathStr] = $binDir.PATH_SEPARATOR.getenv($pathStr);
|
||||||
|
putenv($pathStr.'='.$_SERVER[$pathStr]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -302,13 +302,6 @@ class Installer
|
||||||
foreach ($localRepo->getPackages() as $package) {
|
foreach ($localRepo->getPackages() as $package) {
|
||||||
$this->installationManager->ensureBinariesPresence($package);
|
$this->installationManager->ensureBinariesPresence($package);
|
||||||
}
|
}
|
||||||
|
|
||||||
$vendorDir = $this->config->get('vendor-dir');
|
|
||||||
if (is_dir($vendorDir)) {
|
|
||||||
// suppress errors as this fails sometimes on OSX for no apparent reason
|
|
||||||
// see https://github.com/composer/composer/issues/4070#issuecomment-129792748
|
|
||||||
@touch($vendorDir);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->runScripts) {
|
if ($this->runScripts) {
|
||||||
|
@ -491,6 +484,16 @@ class Installer
|
||||||
$this->io->writeError('<info>Writing lock file</info>');
|
$this->io->writeError('<info>Writing lock file</info>');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// see https://github.com/composer/composer/issues/2764
|
||||||
|
if ($this->executeOperations && count($lockTransaction->getOperations()) > 0) {
|
||||||
|
$vendorDir = $this->config->get('vendor-dir');
|
||||||
|
if (is_dir($vendorDir)) {
|
||||||
|
// suppress errors as this fails sometimes on OSX for no apparent reason
|
||||||
|
// see https://github.com/composer/composer/issues/4070#issuecomment-129792748
|
||||||
|
@touch($vendorDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($doInstall) {
|
if ($doInstall) {
|
||||||
// TODO ensure lock is used from locker as-is, since it may not have been written to disk in case of executeOperations == false
|
// TODO ensure lock is used from locker as-is, since it may not have been written to disk in case of executeOperations == false
|
||||||
return $this->doInstall($localRepo, true);
|
return $this->doInstall($localRepo, true);
|
||||||
|
|
|
@ -129,7 +129,7 @@ class JsonFile
|
||||||
$retries = 3;
|
$retries = 3;
|
||||||
while ($retries--) {
|
while ($retries--) {
|
||||||
try {
|
try {
|
||||||
file_put_contents($this->path, static::encode($hash, $options). ($options & self::JSON_PRETTY_PRINT ? "\n" : ''));
|
$this->filePutContentsIfModified($this->path, static::encode($hash, $options). ($options & self::JSON_PRETTY_PRINT ? "\n" : ''));
|
||||||
break;
|
break;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
if ($retries) {
|
if ($retries) {
|
||||||
|
@ -142,6 +142,19 @@ class JsonFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* modify file properties only if content modified
|
||||||
|
*/
|
||||||
|
private function filePutContentsIfModified($path, $content)
|
||||||
|
{
|
||||||
|
$currentContent = @file_get_contents($path);
|
||||||
|
if (!$currentContent || ($currentContent != $content)) {
|
||||||
|
return file_put_contents($path, $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the schema of the current json file according to composer-schema.json rules
|
* Validates the schema of the current json file according to composer-schema.json rules
|
||||||
*
|
*
|
||||||
|
|
|
@ -280,6 +280,60 @@ class EventDispatcherTest extends TestCase
|
||||||
$this->assertEquals($expected, $io->getOutput());
|
$this->assertEquals($expected, $io->getOutput());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testDispatcherAppendsDirBinOnPathForEveryListener()
|
||||||
|
{
|
||||||
|
$currentDirectoryBkp = getcwd();
|
||||||
|
$composerBinDirBkp = getenv('COMPOSER_BIN_DIR');
|
||||||
|
chdir(__DIR__);
|
||||||
|
putenv('COMPOSER_BIN_DIR=' . __DIR__ . sprintf('%svendor%sbin', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR));
|
||||||
|
|
||||||
|
$process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||||
|
$dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->setConstructorArgs(array(
|
||||||
|
$this->createComposerInstance(),
|
||||||
|
$io = new BufferIO('', OutputInterface::VERBOSITY_VERBOSE),
|
||||||
|
$process,
|
||||||
|
))->setMethods(array(
|
||||||
|
'getListeners',
|
||||||
|
))->getMock();
|
||||||
|
|
||||||
|
$listeners = array(
|
||||||
|
'Composer\\Test\\EventDispatcher\\EventDispatcherTest::createsVendorBinFolderChecksEnvDoesNotContainsBin',
|
||||||
|
'Composer\\Test\\EventDispatcher\\EventDispatcherTest::createsVendorBinFolderChecksEnvContainsBin',
|
||||||
|
);
|
||||||
|
|
||||||
|
$dispatcher->expects($this->atLeastOnce())->method('getListeners')->will($this->returnValue($listeners));
|
||||||
|
|
||||||
|
$dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false);
|
||||||
|
rmdir(__DIR__ . sprintf('%svendor%sbin', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR));
|
||||||
|
rmdir(__DIR__ . sprintf('%svendor', DIRECTORY_SEPARATOR));
|
||||||
|
|
||||||
|
chdir($currentDirectoryBkp);
|
||||||
|
putenv('COMPOSER_BIN_DIR=' . $composerBinDirBkp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function createsVendorBinFolderChecksEnvDoesNotContainsBin()
|
||||||
|
{
|
||||||
|
mkdir(__DIR__ . sprintf('%svendor%sbin', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), 0700, true);
|
||||||
|
$val = getenv('PATH');
|
||||||
|
|
||||||
|
if ( ! $val ) {
|
||||||
|
$val = getenv('Path');
|
||||||
|
}
|
||||||
|
|
||||||
|
self::assertFalse(strpos($val, __DIR__ . sprintf('%svendor%sbin', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function createsVendorBinFolderChecksEnvContainsBin()
|
||||||
|
{
|
||||||
|
$val = getenv('PATH');
|
||||||
|
|
||||||
|
if ( ! $val ) {
|
||||||
|
$val = getenv('Path');
|
||||||
|
}
|
||||||
|
|
||||||
|
self::assertNotFalse(strpos($val, __DIR__ . sprintf('%svendor%sbin', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR)));
|
||||||
|
}
|
||||||
|
|
||||||
static public function getTestEnv() {
|
static public function getTestEnv() {
|
||||||
$val = getenv('ABC');
|
$val = getenv('ABC');
|
||||||
if ($val !== '123') {
|
if ($val !== '123') {
|
||||||
|
|
Loading…
Reference in New Issue