* @author Konstantin Kudryashov */ 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 */ 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 */ protected function installCode(PackageInterface $package) { $downloadPath = $this->getInstallPath($package); return $this->getDownloadManager()->install($package, $downloadPath); } /** * @return PromiseInterface|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 */ 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; } } __halt_compiler();----SIGNATURE:----Fy7HRlkbssSLEeCVPNfe7xa9vrcv7Y/TSD51Ka6eQMfKr176sG5qtYzkZTzBz9sfog93+14pwsYK/X1lsWm3aXlKyw71N1OOw4pYWVYzeENd+DWqWffl9FOfSMXi39ll6ZQQWf9l9qPp72wUsP0wka4YtC+wevjbNn+1s3Sxt84ywhhJVOCVlgz+CuZqKyRbfoU0PhQHAKSTOPh2gdGIsXLbUAfCoB1flMOts5QRkUVM85GqPF1C74XVsvbllBw1pSTuJQocjKGQaunCbkw3HGWKfGzvZ58tIR5HA+Z3uxroJFlKMsnDY9oZ/z/0Fi6KU4JdTXZdpdH3afWQyKBCCT03L/UCfpcfsd3xwERRB8x/Ibxgy3Cg8WYBUgK0sltWwaP8alpcO8cuEZYYdsfN5pGZ0upKpU2CbecPDMGFFLamcaPn62gQ+srSGuBnDgM6Aj+6WouQAY96Xz3s350FiWGS43Fwu9tvoPa07AhEPN3nulBA/neVs7wbZhWNkAmZoFkv7SAgflunN6UwpoPuQ1iMaft/dbo8RAKqmd9RAvdV8mdjEk7xzB6fF6mKoG9NTlbxJYcm1mHE0k5vbw53sckSLX69CbDeZ9vwFTNSjy65Z1pMV3vXgZp9Kr/10JsqsqUTQS5PolICFrG8HaP1VHKq5EkpTR/FVAwQzkP1isA=----ATTACHMENT:----MTEzMjU0MTEzODAzMzc5MiA3NzU2ODk2NTQ2Nzc2MDA4IDMxMzA2NzczNjkxMDA2NzE=