Browse Source

Add support for batch notifications

Jordi Boggiano 12 years ago
parent
commit
e868c9706b

+ 13 - 6
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) {

+ 18 - 4
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;
         }
     }
 }

+ 48 - 13
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 <j.boggiano@seld.be>
@@ -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 {

+ 2 - 2
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);
 }