protected static function detectDownloadConflicts($packages, $reg) { // check conflicts with packages already installed $checked = $conflicts = array(); foreach ($packages as $package) { $fullPackageName = $package->channel . '/' . $package->name; if (isset($checked[$fullPackageName])) { continue; } $checked[$fullPackageName] = 1; $conflict = $reg->detectFileConflicts($package); if (!count($conflict)) { continue; } $conflicts[$fullPackageName] = $conflict; } // check conflicts with other downloaded packages $dupes = $checked = $filelist = array(); foreach ($packages as $package) { $fullPackageName = $package->channel . '/' . $package->name; if (isset($checked[$fullPackageName])) { continue; } $checked[$fullPackageName] = 1; foreach ($package->installcontents as $file) { // TODO: Ignore file conflicts when force is applied? // TODO: Add more error checking i think $role = Installer\Role::factory($package->getPackageType(), $file->role); list(, $path) = $role->getRelativeLocation($package, $file, true); if (isset($filelist[$file->role][$path])) { $dupes[$path] = $file->role; } $filelist[$file->role][$path][] = $fullPackageName; } } foreach ($dupes as $path => $role) { $conflicted = array_shift($filelist[$role][$path]); foreach ($filelist[$role][$path] as $package) { $conflicts[$conflicted][] = array($path => $package); } } if (count($conflicts)) { $message = "File conflicts detected:\n"; foreach ($conflicts as $package => $files) { $message .= " Package {$package}:\n"; foreach ($files as $info) { foreach ($info as $path => $conflict) { $message .= ' ' . $path . ' (conflicts with package ' . $conflict . ")\n"; } } } AtomicFileTransaction::rollback(); throw new Installer\Exception($message); } }
/** * Install packages slated for installation during transaction */ static function commit() { if (self::$inTransaction === false) { return false; } $installer = new Uninstaller; // validate dependencies $errs = new \PEAR2\MultiErrors; $reg = Config::current()->registry; try { foreach (self::$uninstallPackages as $package) { $package->validateUninstallDependencies(self::$uninstallPackages, $errs); } if (count($errs->E_ERROR)) { throw new Installer\Exception('Dependency validation failed ' . 'for some installed packages, installation aborted', $errs); } // create dependency connections and load them into the directed graph $graph = new DirectedGraph; foreach (self::$uninstallPackages as $package) { $package->makeUninstallConnections($graph, self::$uninstallPackages); } // topologically sort packages and install them via iterating over the graph $actual = array(); foreach ($graph as $package) { $actual[] = $package; } // easy reverse topological sort array_reverse($actual); AtomicFileTransaction::begin(); $reg->begin(); try { foreach ($actual as $package) { $installer->uninstall($package, $reg); self::$uninstalledPackages[] = $package; } $dirtrees = array(); foreach (self::$uninstalledPackages as $package) { $dirtrees[] = $reg->info($package->name, $package->channel, 'dirtree'); $previous = $reg->toPackageFile($package->name, $package->channel, true); self::$registeredPackages[] = array($package, $previous); $reg->uninstall($package->name, $package->channel); } AtomicFileTransaction::rmEmptyDirs($dirtrees); AtomicFileTransaction::commit(); $reg->commit(); AtomicFileTransaction::removeBackups(); } catch (\Exception $e) { if (AtomicFileTransaction::inTransaction()) { AtomicFileTransaction::rollback(); } $reg->rollback(); throw $e; } self::$uninstallPackages = array(); Config::current()->saveConfig(); if (isset(Main::$options['install-plugins'])) { Config::setCurrent(self::$lastCurrent->path); } } catch (\Exception $e) { self::rollback(); throw $e; } }