diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index ff4ecdebb..88d4ff65f 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -182,15 +182,22 @@ class Installer $this->eventDispatcher->dispatchCommandEvent($eventName, $this->devMode); } - $this->suggestedPackages = array(); - if (!$this->doInstall($this->repositoryManager->getLocalRepository(), $installedRepo, $aliases)) { - return false; - } - if ($this->devMode) { - if (!$this->doInstall($this->repositoryManager->getLocalDevRepository(), $installedRepo, $aliases, true)) { + try { + $this->suggestedPackages = array(); + if (!$this->doInstall($this->repositoryManager->getLocalRepository(), $installedRepo, $aliases)) { return false; } + if ($this->devMode) { + if (!$this->doInstall($this->repositoryManager->getLocalDevRepository(), $installedRepo, $aliases, true)) { + return false; + } + } + } catch (\Exception $e) { + $this->installationManager->notifyInstalls(); + + throw $e; } + $this->installationManager->notifyInstalls(); // output suggestions foreach ($this->suggestedPackages as $suggestion) { diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index 82604d283..2a696ec21 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -34,6 +34,12 @@ class InstallationManager { private $installers = array(); private $cache = array(); + private $notifiablePackages = array(); + + public function reset() + { + $this->notifiablePackages = array(); + } /** * Adds installer @@ -130,7 +136,7 @@ class InstallationManager $package = $operation->getPackage(); $installer = $this->getInstaller($package->getType()); $installer->install($repo, $package); - $this->notifyInstall($package); + $this->markForNotification($package); } /** @@ -150,7 +156,7 @@ class InstallationManager if ($initialType === $targetType) { $installer = $this->getInstaller($initialType); $installer->update($repo, $initial, $target); - $this->notifyInstall($target); + $this->markForNotification($target); } else { $this->getInstaller($initialType)->uninstall($repo, $initial); $this->getInstaller($targetType)->install($repo, $target); @@ -211,10 +217,18 @@ class InstallationManager return $installer->getInstallPath($package); } - private function notifyInstall(PackageInterface $package) + public function notifyInstalls() + { + foreach ($this->notifiablePackages as $packages) { + $repo = reset($packages)->getRepository(); + $repo->notifyInstalls($packages); + } + } + + private function markForNotification(PackageInterface $package) { if ($package->getRepository() instanceof NotifiableRepositoryInterface) { - $package->getRepository()->notifyInstall($package); + $this->notifiablePackages[spl_object_hash($package->getRepository())][$package->getName()] = $package; } } } diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 557c45393..249dfd89e 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -22,6 +22,7 @@ use Composer\Cache; use Composer\Config; use Composer\IO\IOInterface; use Composer\Util\RemoteFilesystem; +use Composer\Util\StreamContextFactory; /** * @author Jordi Boggiano @@ -79,30 +80,56 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository /** * {@inheritDoc} */ - public function notifyInstall(PackageInterface $package) + public function notifyInstalls(array $packages) { if (!$this->notifyUrl || !$this->config->get('notify-on-install')) { return; } - // TODO use an optional curl_multi pool for all the notifications - $url = str_replace('%package%', $package->getPrettyName(), $this->notifyUrl); + // non-batch API, deprecated + if (strpos($this->notifyUrl, '%package%')) { + foreach ($packages as $package) { + $url = str_replace('%package%', $package->getPrettyName(), $this->notifyUrl); + + $params = array( + 'version' => $package->getPrettyVersion(), + 'version_normalized' => $package->getVersion(), + ); + $opts = array('http' => + array( + 'method' => 'POST', + 'header' => 'Content-type: application/x-www-form-urlencoded', + 'content' => http_build_query($params, '', '&'), + 'timeout' => 3, + ) + ); + + $context = StreamContextFactory::getContext($opts); + @file_get_contents($url, false, $context); + } + + return; + } + + $postData = array('downloads' => array()); + foreach ($packages as $package) { + $postData['downloads'][] = array( + 'name' => $package->getPrettyName(), + 'version' => $package->getVersion(), + ); + } - $params = array( - 'version' => $package->getPrettyVersion(), - 'version_normalized' => $package->getVersion(), - ); $opts = array('http' => array( 'method' => 'POST', - 'header' => 'Content-type: application/x-www-form-urlencoded', - 'content' => http_build_query($params, '', '&'), - 'timeout' => 3, + 'header' => 'Content-Type: application/json', + 'content' => json_encode($postData), + 'timeout' => 6, ) ); - $context = stream_context_create($opts); - @file_get_contents($url, false, $context); + $context = StreamContextFactory::getContext($opts); + @file_get_contents($this->notifyUrl, false, $context); } public function setRootAliases(array $rootAliases) @@ -340,7 +367,15 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository $data = $this->fetchFile($jsonUrl, 'packages.json'); - if (!empty($data['notify'])) { + if (!empty($data['notify_batch'])) { + if ('/' === $data['notify_batch'][0]) { + $this->notifyUrl = preg_replace('{(https?://[^/]+).*}i', '$1' . $data['notify_batch'], $this->url); + } else { + $this->notifyUrl = $data['notify_batch']; + } + } + + if (!$this->notifyUrl && !empty($data['notify'])) { if ('/' === $data['notify'][0]) { $this->notifyUrl = preg_replace('{(https?://[^/]+).*}i', '$1' . $data['notify'], $this->url); } else { diff --git a/src/Composer/Repository/NotifiableRepositoryInterface.php b/src/Composer/Repository/NotifiableRepositoryInterface.php index ed72a30a9..e3a252ea0 100644 --- a/src/Composer/Repository/NotifiableRepositoryInterface.php +++ b/src/Composer/Repository/NotifiableRepositoryInterface.php @@ -22,7 +22,7 @@ interface NotifiableRepositoryInterface extends RepositoryInterface /** * Notify this repository about the installation of a package * - * @param PackageInterface $package Package that is installed + * @param PackageInterface[] $packages Packages that were installed */ - public function notifyInstall(PackageInterface $package); + public function notifyInstalls(array $packages); }