class LibraryInstaller
Package installation manager.
@author Jordi Boggiano <j.boggiano@seld.be> @author Konstantin Kudryashov <ever.zet@gmail.com>
Hierarchy
- class \Composer\Installer\LibraryInstaller implements \Composer\Installer\InstallerInterface, \Composer\Installer\BinaryPresenceInterface
Expanded class hierarchy of LibraryInstaller
1 file declares its use of LibraryInstaller
- Installer.php in vendor/
composer/ installers/ src/ Composer/ Installers/ Installer.php
File
-
vendor/
composer/ composer/ src/ Composer/ Installer/ LibraryInstaller.php, line 33
Namespace
Composer\InstallerView source
class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface {
/** @var PartialComposer */
protected $composer;
/** @var string */
protected $vendorDir;
/** @var DownloadManager|null */
protected $downloadManager;
/** @var IOInterface */
protected $io;
/** @var string */
protected $type;
/** @var Filesystem */
protected $filesystem;
/** @var BinaryInstaller */
protected $binaryInstaller;
/**
* Initializes library installer.
*
* @param Filesystem $filesystem
* @param BinaryInstaller $binaryInstaller
*/
public function __construct(IOInterface $io, PartialComposer $composer, ?string $type = 'library', ?Filesystem $filesystem = null, ?BinaryInstaller $binaryInstaller = null) {
$this->composer = $composer;
$this->downloadManager = $composer instanceof Composer ? $composer->getDownloadManager() : null;
$this->io = $io;
$this->type = $type;
$this->filesystem = $filesystem ?: new Filesystem();
$this->vendorDir = rtrim($composer->getConfig()
->get('vendor-dir'), '/');
$this->binaryInstaller = $binaryInstaller ?: new BinaryInstaller($this->io, rtrim($composer->getConfig()
->get('bin-dir'), '/'), $composer->getConfig()
->get('bin-compat'), $this->filesystem, $this->vendorDir);
}
/**
* @inheritDoc
*/
public function supports(string $packageType) {
return $packageType === $this->type || null === $this->type;
}
/**
* @inheritDoc
*/
public function isInstalled(InstalledRepositoryInterface $repo, PackageInterface $package) {
if (!$repo->hasPackage($package)) {
return false;
}
$installPath = $this->getInstallPath($package);
if (Filesystem::isReadable($installPath)) {
return true;
}
if (Platform::isWindows() && $this->filesystem
->isJunction($installPath)) {
return true;
}
if (is_link($installPath)) {
if (realpath($installPath) === false) {
return false;
}
return true;
}
return false;
}
/**
* @inheritDoc
*/
public function download(PackageInterface $package, ?PackageInterface $prevPackage = null) {
$this->initializeVendorDir();
$downloadPath = $this->getInstallPath($package);
return $this->getDownloadManager()
->download($package, $downloadPath, $prevPackage);
}
/**
* @inheritDoc
*/
public function prepare($type, PackageInterface $package, ?PackageInterface $prevPackage = null) {
$this->initializeVendorDir();
$downloadPath = $this->getInstallPath($package);
return $this->getDownloadManager()
->prepare($type, $package, $downloadPath, $prevPackage);
}
/**
* @inheritDoc
*/
public function cleanup($type, PackageInterface $package, ?PackageInterface $prevPackage = null) {
$this->initializeVendorDir();
$downloadPath = $this->getInstallPath($package);
return $this->getDownloadManager()
->cleanup($type, $package, $downloadPath, $prevPackage);
}
/**
* @inheritDoc
*/
public function install(InstalledRepositoryInterface $repo, PackageInterface $package) {
$this->initializeVendorDir();
$downloadPath = $this->getInstallPath($package);
// remove the binaries if it appears the package files are missing
if (!Filesystem::isReadable($downloadPath) && $repo->hasPackage($package)) {
$this->binaryInstaller
->removeBinaries($package);
}
$promise = $this->installCode($package);
if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve(null);
}
$binaryInstaller = $this->binaryInstaller;
$installPath = $this->getInstallPath($package);
return $promise->then(static function () use ($binaryInstaller, $installPath, $package, $repo) : void {
$binaryInstaller->installBinaries($package, $installPath);
if (!$repo->hasPackage($package)) {
$repo->addPackage(clone $package);
}
});
}
/**
* @inheritDoc
*/
public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) {
if (!$repo->hasPackage($initial)) {
throw new \InvalidArgumentException('Package is not installed: ' . $initial);
}
$this->initializeVendorDir();
$this->binaryInstaller
->removeBinaries($initial);
$promise = $this->updateCode($initial, $target);
if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve(null);
}
$binaryInstaller = $this->binaryInstaller;
$installPath = $this->getInstallPath($target);
return $promise->then(static function () use ($binaryInstaller, $installPath, $target, $initial, $repo) : void {
$binaryInstaller->installBinaries($target, $installPath);
$repo->removePackage($initial);
if (!$repo->hasPackage($target)) {
$repo->addPackage(clone $target);
}
});
}
/**
* @inheritDoc
*/
public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package) {
if (!$repo->hasPackage($package)) {
throw new \InvalidArgumentException('Package is not installed: ' . $package);
}
$promise = $this->removeCode($package);
if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve(null);
}
$binaryInstaller = $this->binaryInstaller;
$downloadPath = $this->getPackageBasePath($package);
$filesystem = $this->filesystem;
return $promise->then(static function () use ($binaryInstaller, $filesystem, $downloadPath, $package, $repo) : void {
$binaryInstaller->removeBinaries($package);
$repo->removePackage($package);
if (strpos($package->getName(), '/')) {
$packageVendorDir = dirname($downloadPath);
if (is_dir($packageVendorDir) && $filesystem->isDirEmpty($packageVendorDir)) {
Silencer::call('rmdir', $packageVendorDir);
}
}
});
}
/**
* @inheritDoc
*
* @return string
*/
public function getInstallPath(PackageInterface $package) {
$this->initializeVendorDir();
$basePath = ($this->vendorDir ? $this->vendorDir . '/' : '') . $package->getPrettyName();
$targetDir = $package->getTargetDir();
return $basePath . ($targetDir ? '/' . $targetDir : '');
}
/**
* Make sure binaries are installed for a given package.
*
* @param PackageInterface $package Package instance
*/
public function ensureBinariesPresence(PackageInterface $package) {
$this->binaryInstaller
->installBinaries($package, $this->getInstallPath($package), false);
}
/**
* Returns the base path of the package without target-dir path
*
* It is used for BC as getInstallPath tends to be overridden by
* installer plugins but not getPackageBasePath
*
* @return string
*/
protected function getPackageBasePath(PackageInterface $package) {
$installPath = $this->getInstallPath($package);
$targetDir = $package->getTargetDir();
if ($targetDir) {
return Preg::replace('{/*' . str_replace('/', '/+', preg_quote($targetDir)) . '/?$}', '', $installPath);
}
return $installPath;
}
/**
* @return PromiseInterface|null
* @phpstan-return PromiseInterface<void|null>|null
*/
protected function installCode(PackageInterface $package) {
$downloadPath = $this->getInstallPath($package);
return $this->getDownloadManager()
->install($package, $downloadPath);
}
/**
* @return PromiseInterface|null
* @phpstan-return PromiseInterface<void|null>|null
*/
protected function updateCode(PackageInterface $initial, PackageInterface $target) {
$initialDownloadPath = $this->getInstallPath($initial);
$targetDownloadPath = $this->getInstallPath($target);
if ($targetDownloadPath !== $initialDownloadPath) {
// if the target and initial dirs intersect, we force a remove + install
// to avoid the rename wiping the target dir as part of the initial dir cleanup
if (strpos($initialDownloadPath, $targetDownloadPath) === 0 || strpos($targetDownloadPath, $initialDownloadPath) === 0) {
$promise = $this->removeCode($initial);
if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve(null);
}
return $promise->then(function () use ($target) : PromiseInterface {
$promise = $this->installCode($target);
if ($promise instanceof PromiseInterface) {
return $promise;
}
return \React\Promise\resolve(null);
});
}
$this->filesystem
->rename($initialDownloadPath, $targetDownloadPath);
}
return $this->getDownloadManager()
->update($initial, $target, $targetDownloadPath);
}
/**
* @return PromiseInterface|null
* @phpstan-return PromiseInterface<void|null>|null
*/
protected function removeCode(PackageInterface $package) {
$downloadPath = $this->getPackageBasePath($package);
return $this->getDownloadManager()
->remove($package, $downloadPath);
}
/**
* @return void
*/
protected function initializeVendorDir() {
$this->filesystem
->ensureDirectoryExists($this->vendorDir);
$this->vendorDir = realpath($this->vendorDir);
}
protected function getDownloadManager() : DownloadManager {
assert($this->downloadManager instanceof DownloadManager, new \LogicException(self::class . ' should be initialized with a fully loaded Composer instance to be able to install/... packages'));
return $this->downloadManager;
}
}
Members
Title Sort descending | Modifiers | Object type | Summary | Overriden Title | Overrides |
---|---|---|---|---|---|
LibraryInstaller::$binaryInstaller | protected | property | @var BinaryInstaller | ||
LibraryInstaller::$composer | protected | property | @var PartialComposer | ||
LibraryInstaller::$downloadManager | protected | property | @var DownloadManager|null | ||
LibraryInstaller::$filesystem | protected | property | @var Filesystem | ||
LibraryInstaller::$io | protected | property | @var IOInterface | ||
LibraryInstaller::$type | protected | property | @var string | ||
LibraryInstaller::$vendorDir | protected | property | @var string | ||
LibraryInstaller::cleanup | public | function | @inheritDoc | Overrides InstallerInterface::cleanup | |
LibraryInstaller::download | public | function | @inheritDoc | Overrides InstallerInterface::download | 1 |
LibraryInstaller::ensureBinariesPresence | public | function | Make sure binaries are installed for a given package. | Overrides BinaryPresenceInterface::ensureBinariesPresence | |
LibraryInstaller::getDownloadManager | protected | function | |||
LibraryInstaller::getInstallPath | public | function | @inheritDoc | Overrides InstallerInterface::getInstallPath | 1 |
LibraryInstaller::getPackageBasePath | protected | function | Returns the base path of the package without target-dir path | ||
LibraryInstaller::initializeVendorDir | protected | function | |||
LibraryInstaller::install | public | function | @inheritDoc | Overrides InstallerInterface::install | 1 |
LibraryInstaller::installCode | protected | function | @phpstan-return PromiseInterface<void|null>|null | ||
LibraryInstaller::isInstalled | public | function | @inheritDoc | Overrides InstallerInterface::isInstalled | |
LibraryInstaller::prepare | public | function | @inheritDoc | Overrides InstallerInterface::prepare | 1 |
LibraryInstaller::removeCode | protected | function | @phpstan-return PromiseInterface<void|null>|null | ||
LibraryInstaller::supports | public | function | @inheritDoc | Overrides InstallerInterface::supports | 2 |
LibraryInstaller::uninstall | public | function | @inheritDoc | Overrides InstallerInterface::uninstall | 2 |
LibraryInstaller::update | public | function | @inheritDoc | Overrides InstallerInterface::update | 1 |
LibraryInstaller::updateCode | protected | function | @phpstan-return PromiseInterface<void|null>|null | ||
LibraryInstaller::__construct | public | function | Initializes library installer. | 2 |