diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php
index 5cc8d6fb7..1a3086ed8 100644
--- a/src/Composer/Command/CreateProjectCommand.php
+++ b/src/Composer/Command/CreateProjectCommand.php
@@ -16,14 +16,15 @@ use Composer\Config;
use Composer\Factory;
use Composer\Installer;
use Composer\Installer\ProjectInstaller;
+use Composer\Installer\InstallationManager;
use Composer\IO\IOInterface;
use Composer\Package\BasePackage;
use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\DependencyResolver\Pool;
+use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\Repository\ComposerRepository;
use Composer\Repository\CompositeRepository;
use Composer\Repository\FilesystemRepository;
-use Composer\Repository\NotifiableRepositoryInterface;
use Composer\Repository\InstalledFilesystemRepository;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -117,11 +118,6 @@ EOT
throw new \InvalidArgumentException('Invalid stability provided ('.$stability.'), must be one of: '.implode(', ', array_keys(BasePackage::$stabilities)));
}
- $dm = $this->createDownloadManager($io, $config);
- if ($preferSource) {
- $dm->setPreferSource(true);
- }
-
if (null === $repositoryUrl) {
$sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config));
} elseif ("json" === pathinfo($repositoryUrl, PATHINFO_EXTENSION)) {
@@ -179,13 +175,16 @@ EOT
$package->setSourceReference(substr($package->getPrettyVersion(), 4));
}
+ $dm = $this->createDownloadManager($io, $config);
$dm->setPreferSource($preferSource)
->setPreferDist($preferDist);
+
$projectInstaller = new ProjectInstaller($directory, $dm);
- $projectInstaller->install(new InstalledFilesystemRepository(new JsonFile('php://memory')), $package);
- if ($package->getRepository() instanceof NotifiableRepositoryInterface) {
- $package->getRepository()->notifyInstall($package);
- }
+ $im = $this->createInstallationManager();
+ $im->addInstaller($projectInstaller);
+ $im->install(new InstalledFilesystemRepository(new JsonFile('php://memory')), new InstallOperation($package));
+ $im->notifyInstalls();
+
$installedFromVcs = 'source' === $package->getInstallationSource();
$io->write('Created project in ' . $directory . '');
@@ -194,7 +193,7 @@ EOT
putenv('COMPOSER_ROOT_VERSION='.$package->getPrettyVersion());
// clean up memory
- unset($dm, $config, $projectInstaller, $sourceRepo, $package);
+ unset($dm, $im, $config, $projectInstaller, $sourceRepo, $package);
// install dependencies of the created project
$composer = Factory::create($io);
@@ -248,4 +247,9 @@ EOT
return $factory->createDownloadManager($io, $config);
}
+
+ protected function createInstallationManager()
+ {
+ return new InstallationManager();
+ }
}
diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php
index 911805dce..65f2dd526 100644
--- a/src/Composer/Factory.php
+++ b/src/Composer/Factory.php
@@ -223,7 +223,7 @@ class Factory
$dm = $this->createDownloadManager($io, $config);
// initialize installation manager
- $im = $this->createInstallationManager($config);
+ $im = $this->createInstallationManager();
// initialize composer
$composer = new Composer();
@@ -305,12 +305,11 @@ class Factory
}
/**
- * @param Config $config
* @return Installer\InstallationManager
*/
- protected function createInstallationManager(Config $config)
+ protected function createInstallationManager()
{
- return new Installer\InstallationManager($config->get('vendor-dir'));
+ return new Installer\InstallationManager();
}
/**
diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php
index 2a696ec21..5c239d0c6 100644
--- a/src/Composer/Installer/InstallationManager.php
+++ b/src/Composer/Installer/InstallationManager.php
@@ -15,7 +15,6 @@ namespace Composer\Installer;
use Composer\Package\PackageInterface;
use Composer\Package\AliasPackage;
use Composer\Repository\RepositoryInterface;
-use Composer\Repository\NotifiableRepositoryInterface;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\DependencyResolver\Operation\InstallOperation;
@@ -23,6 +22,7 @@ use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\DependencyResolver\Operation\UninstallOperation;
use Composer\DependencyResolver\Operation\MarkAliasInstalledOperation;
use Composer\DependencyResolver\Operation\MarkAliasUninstalledOperation;
+use Composer\Util\StreamContextFactory;
/**
* Package operation manager.
@@ -52,6 +52,19 @@ class InstallationManager
$this->cache = array();
}
+ /**
+ * Removes installer
+ *
+ * @param InstallerInterface $installer installer instance
+ */
+ public function removeInstaller(InstallerInterface $installer)
+ {
+ if (false !== ($key = array_search($installer, $this->installers, true))) {
+ array_splice($this->installers, $key, 1);
+ $this->cache = array();
+ }
+ }
+
/**
* Disables custom installers.
*
@@ -219,16 +232,60 @@ class InstallationManager
public function notifyInstalls()
{
- foreach ($this->notifiablePackages as $packages) {
- $repo = reset($packages)->getRepository();
- $repo->notifyInstalls($packages);
+ foreach ($this->notifiablePackages as $repoUrl => $packages) {
+ // non-batch API, deprecated
+ if (strpos($repoUrl, '%package%')) {
+ foreach ($packages as $package) {
+ $url = str_replace('%package%', $package->getPrettyName(), $repoUrl);
+
+ $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(),
+ );
+ }
+
+ $opts = array('http' =>
+ array(
+ 'method' => 'POST',
+ 'header' => 'Content-Type: application/json',
+ 'content' => json_encode($postData),
+ 'timeout' => 6,
+ )
+ );
+
+ $context = StreamContextFactory::getContext($opts);
+ @file_get_contents($repoUrl, false, $context);
}
+
+ $this->reset();
}
private function markForNotification(PackageInterface $package)
{
- if ($package->getRepository() instanceof NotifiableRepositoryInterface) {
- $this->notifiablePackages[spl_object_hash($package->getRepository())][$package->getName()] = $package;
+ if ($package->getNotificationUrl()) {
+ $this->notifiablePackages[$package->getNotificationUrl()][$package->getName()] = $package;
}
}
}
diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php
index 965570807..e2f748092 100644
--- a/src/Composer/Package/AliasPackage.php
+++ b/src/Composer/Package/AliasPackage.php
@@ -307,6 +307,10 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
{
return $this->aliasOf->getSupport();
}
+ public function getNotificationUrl()
+ {
+ return $this->aliasOf->getNotificationUrl();
+ }
public function __toString()
{
return parent::__toString().' (alias of '.$this->aliasOf->getVersion().')';
diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php
index bff9a0888..47167dd23 100644
--- a/src/Composer/Package/Dumper/ArrayDumper.php
+++ b/src/Composer/Package/Dumper/ArrayDumper.php
@@ -31,6 +31,7 @@ class ArrayDumper
'extra',
'installationSource' => 'installation-source',
'autoload',
+ 'notificationUrl' => 'notification-url',
'includePaths' => 'include-path',
);
diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php
index a5464bd00..661013691 100644
--- a/src/Composer/Package/Loader/ArrayLoader.php
+++ b/src/Composer/Package/Loader/ArrayLoader.php
@@ -142,6 +142,10 @@ class ArrayLoader implements LoaderInterface
}
}
+ if (!empty($config['notification-url'])) {
+ $package->setNotificationUrl($config['notification-url']);
+ }
+
if ($package instanceof Package\CompletePackageInterface) {
if (isset($config['scripts']) && is_array($config['scripts'])) {
foreach ($config['scripts'] as $event => $listeners) {
diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php
index cb985d2f4..ddd4b4ec5 100644
--- a/src/Composer/Package/Package.php
+++ b/src/Composer/Package/Package.php
@@ -41,6 +41,7 @@ class Package extends BasePackage
protected $prettyAlias;
protected $dev;
protected $stability;
+ protected $notificationUrl;
protected $requires = array();
protected $conflicts = array();
@@ -506,4 +507,22 @@ class Package extends BasePackage
{
return $this->includePaths;
}
+
+ /**
+ * Sets the notification URL
+ *
+ * @param string $notificationUrl
+ */
+ public function setNotificationUrl($notificationUrl)
+ {
+ $this->notificationUrl = $notificationUrl;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getNotificationUrl()
+ {
+ return $this->notificationUrl;
+ }
}
diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php
index e034acba6..6c2b48b4f 100644
--- a/src/Composer/Package/PackageInterface.php
+++ b/src/Composer/Package/PackageInterface.php
@@ -288,6 +288,13 @@ interface PackageInterface
*/
public function getUniqueName();
+ /**
+ * Returns the package notification url
+ *
+ * @return string
+ */
+ public function getNotificationUrl();
+
/**
* Converts the package into a readable and unique string
*
diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php
index 249dfd89e..fdb7fa5cb 100644
--- a/src/Composer/Repository/ComposerRepository.php
+++ b/src/Composer/Repository/ComposerRepository.php
@@ -22,12 +22,11 @@ use Composer\Cache;
use Composer\Config;
use Composer\IO\IOInterface;
use Composer\Util\RemoteFilesystem;
-use Composer\Util\StreamContextFactory;
/**
* @author Jordi Boggiano
*/
-class ComposerRepository extends ArrayRepository implements NotifiableRepositoryInterface, StreamableRepositoryInterface
+class ComposerRepository extends ArrayRepository implements StreamableRepositoryInterface
{
protected $config;
protected $options;
@@ -77,61 +76,6 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository
$this->loader = new ArrayLoader();
}
- /**
- * {@inheritDoc}
- */
- public function notifyInstalls(array $packages)
- {
- if (!$this->notifyUrl || !$this->config->get('notify-on-install')) {
- return;
- }
-
- // 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(),
- );
- }
-
- $opts = array('http' =>
- array(
- 'method' => 'POST',
- 'header' => 'Content-Type: application/json',
- 'content' => json_encode($postData),
- 'timeout' => 6,
- )
- );
-
- $context = StreamContextFactory::getContext($opts);
- @file_get_contents($this->notifyUrl, false, $context);
- }
-
public function setRootAliases(array $rootAliases)
{
$this->rootAliases = $rootAliases;
@@ -459,6 +403,8 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository
protected function createPackage(array $data, $class)
{
try {
+ $data['notification-url'] = $this->notifyUrl;
+
return $this->loader->load($data, 'Composer\Package\CompletePackage');
} catch (\Exception $e) {
throw new \RuntimeException('Could not load package '.(isset($data['name']) ? $data['name'] : json_encode($data)).' in '.$this->url.': ['.get_class($e).'] '.$e->getMessage(), 0, $e);
diff --git a/src/Composer/Repository/NotifiableRepositoryInterface.php b/src/Composer/Repository/NotifiableRepositoryInterface.php
deleted file mode 100644
index e3a252ea0..000000000
--- a/src/Composer/Repository/NotifiableRepositoryInterface.php
+++ /dev/null
@@ -1,28 +0,0 @@
-
- * Jordi Boggiano
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Composer\Repository;
-
-use Composer\Package\PackageInterface;
-
-/**
- * @author Jordi Boggiano
- */
-interface NotifiableRepositoryInterface extends RepositoryInterface
-{
- /**
- * Notify this repository about the installation of a package
- *
- * @param PackageInterface[] $packages Packages that were installed
- */
- public function notifyInstalls(array $packages);
-}
diff --git a/tests/Composer/Test/Installer/InstallationManagerTest.php b/tests/Composer/Test/Installer/InstallationManagerTest.php
index 553a3e129..9f31d776c 100644
--- a/tests/Composer/Test/Installer/InstallationManagerTest.php
+++ b/tests/Composer/Test/Installer/InstallationManagerTest.php
@@ -35,7 +35,7 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase
return $arg === 'vendor';
}));
- $manager = new InstallationManager('vendor');
+ $manager = new InstallationManager();
$manager->addInstaller($installer);
$this->assertSame($installer, $manager->getInstaller('vendor'));
@@ -44,6 +44,36 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase
$manager->getInstaller('unregistered');
}
+ public function testAddRemoveInstaller()
+ {
+ $installer = $this->createInstallerMock();
+
+ $installer
+ ->expects($this->exactly(2))
+ ->method('supports')
+ ->will($this->returnCallback(function ($arg) {
+ return $arg === 'vendor';
+ }));
+
+ $installer2 = $this->createInstallerMock();
+
+ $installer2
+ ->expects($this->exactly(1))
+ ->method('supports')
+ ->will($this->returnCallback(function ($arg) {
+ return $arg === 'vendor';
+ }));
+
+ $manager = new InstallationManager();
+
+ $manager->addInstaller($installer);
+ $this->assertSame($installer, $manager->getInstaller('vendor'));
+ $manager->addInstaller($installer2);
+ $this->assertSame($installer2, $manager->getInstaller('vendor'));
+ $manager->removeInstaller($installer2);
+ $this->assertSame($installer, $manager->getInstaller('vendor'));
+ }
+
public function testExecute()
{
$manager = $this->getMockBuilder('Composer\Installer\InstallationManager')
@@ -77,7 +107,7 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase
public function testInstall()
{
$installer = $this->createInstallerMock();
- $manager = new InstallationManager('vendor');
+ $manager = new InstallationManager();
$manager->addInstaller($installer);
$package = $this->createPackageMock();
@@ -105,7 +135,7 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase
public function testUpdateWithEqualTypes()
{
$installer = $this->createInstallerMock();
- $manager = new InstallationManager('vendor');
+ $manager = new InstallationManager();
$manager->addInstaller($installer);
$initial = $this->createPackageMock();
@@ -139,7 +169,7 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase
{
$libInstaller = $this->createInstallerMock();
$bundleInstaller = $this->createInstallerMock();
- $manager = new InstallationManager('vendor');
+ $manager = new InstallationManager();
$manager->addInstaller($libInstaller);
$manager->addInstaller($bundleInstaller);
@@ -185,7 +215,7 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase
public function testUninstall()
{
$installer = $this->createInstallerMock();
- $manager = new InstallationManager('vendor');
+ $manager = new InstallationManager();
$manager->addInstaller($installer);
$package = $this->createPackageMock();