1
0
Fork 0
pull/151/head
digitalkaoz 2012-02-06 09:53:31 +01:00
commit 7b982a1409
19 changed files with 552 additions and 16 deletions

View File

@ -1,11 +1,10 @@
#!/usr/bin/env php
<?php
if (!@include __DIR__.'/../vendor/.composer/autoload.php') {
die('You must set up the project dependencies, run the following commands:
wget http://getcomposer.org/composer.phar
php composer.phar install
');
if ((!@include __DIR__.'/../../../.composer/autoload.php') && (!@include __DIR__.'/../vendor/.composer/autoload.php')) {
die('You must set up the project dependencies, run the following commands:'.PHP_EOL.
'curl -s http://getcomposer.org/installer | php'.PHP_EOL.
'php composer.phar install'.PHP_EOL);
}
use Composer\Compiler;

View File

@ -1,11 +1,10 @@
#!/usr/bin/env php
<?php
if (!@include __DIR__.'/../vendor/.composer/autoload.php') {
die('You must set up the project dependencies, run the following commands:
wget http://getcomposer.org/composer.phar
php composer.phar install
');
if ((!@include __DIR__.'/../../../.composer/autoload.php') && (!@include __DIR__.'/../vendor/.composer/autoload.php')) {
die('You must set up the project dependencies, run the following commands:'.PHP_EOL.
'curl -s http://getcomposer.org/installer | php'.PHP_EOL.
'php composer.phar install'.PHP_EOL);
}
use Composer\Console\Application;

66
doc/faqs/scripts.md Normal file
View File

@ -0,0 +1,66 @@
# Scripts
## What is a script?
A script is a callback (defined as a static method) that will be called
when the event it listens on is triggered.
**Scripts are only executed on the root package, not on the dependencies
that are installed.**
## Event types
- **pre-install-cmd**: occurs before the install command is executed.
- **post-install-cmd**: occurs after the install command is executed.
- **pre-update-cmd**: occurs before the update command is executed.
- **post-update-cmd**: occurs after the update command is executed.
- **pre-package-install**: occurs before a package is installed.
- **post-package-install**: occurs after a package is installed.
- **pre-package-update**: occurs before a package is updated.
- **post-package-update**: occurs after a package is updated.
- **pre-package-uninstall**: occurs before a package has been uninstalled.
- **post-package-uninstall**: occurs after a package has been uninstalled.
## Defining scripts
Scripts are defined by adding the `scripts` key to a project's `composer.json`.
They are specified as an array of classes and static method names.
The classes used as scripts must be autoloadable via Composer's autoload
functionality.
Script definition example:
```json
{
"scripts": {
"post-update-cmd": "MyVendor\\MyClass::postUpdate",
"post-package-install": ["MyVendor\\MyClass::postPackageInstall"]
}
}
```
Script listener example:
```php
<?php
namespace MyVendor;
class MyClass
{
public static function postUpdate($event)
{
// do stuff
}
public static function postPackageInstall($event)
{
$installedPackage = $event->getOperation()->getPackage();
// do stuff
}
}
```

View File

@ -12,6 +12,8 @@
namespace Composer\Command;
use Composer\Script\ScriptEvents;
use Composer\Script\EventDispatcher;
use Composer\Autoload\AutoloadGenerator;
use Composer\DependencyResolver;
use Composer\DependencyResolver\Pool;
@ -23,6 +25,8 @@ use Composer\Repository\PlatformRepository;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\DependencyResolver\Solver;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
@ -65,6 +69,8 @@ EOT
$dryRun = (Boolean) $input->getOption('dry-run');
$verbose = $dryRun || $input->getOption('verbose');
$composer = $this->getComposer();
$io = $this->getApplication()->getIO();
$dispatcher = new EventDispatcher($this->getComposer(), $io);
if ($preferSource) {
$composer->getDownloadManager()->setPreferSource(true);
@ -82,6 +88,12 @@ EOT
$pool->addRepository($repository);
}
// dispatch pre event
if (!$dryRun) {
$eventName = $update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD;
$dispatcher->dispatchCommandEvent($eventName);
}
// creating requirements request
$request = new Request($pool);
if ($update) {
@ -132,7 +144,10 @@ EOT
// TODO this belongs in the solver, but this will do for now to report top-level deps missing at least
foreach ($request->getJobs() as $job) {
if ('install' === $job['cmd']) {
foreach ($installedRepo->getPackages() as $package) {
foreach ($installedRepo->getPackages() as $package ) {
if ($installedRepo->hasPackage($package) && !$package->isPlatform() && !$installationManager->isPackageInstalled($package)) {
$operations[$job['packageName']] = new InstallOperation($package, Solver::RULE_PACKAGE_NOT_EXIST);
}
if (in_array($job['packageName'], $package->getNames())) {
continue 2;
}
@ -162,7 +177,9 @@ EOT
$output->writeln((string) $operation);
}
if (!$dryRun) {
$dispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType())), $operation);
$installationManager->execute($operation);
$dispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType())), $operation);
}
}
@ -177,6 +194,10 @@ EOT
$output->writeln('<info>Generating autoload files</info>');
$generator = new AutoloadGenerator;
$generator->dump($localRepo, $composer->getPackage(), $installationManager, $installationManager->getVendorPath().'/.composer');
// dispatch post event
$eventName = $update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD;
$dispatcher->dispatchCommandEvent($eventName);
}
}

View File

@ -26,7 +26,7 @@ class SearchCommand extends Command
{
$this
->setName('search')
->setDescription('search for packages')
->setDescription('Search for packages')
->setDefinition(array(
new InputArgument('tokens', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'tokens to search for'),
))

View File

@ -28,7 +28,7 @@ class ShowCommand extends Command
{
$this
->setName('show')
->setDescription('show package details')
->setDescription('Show package details')
->setDefinition(array(
new InputArgument('package', InputArgument::REQUIRED, 'the package to inspect'),
new InputArgument('version', InputArgument::OPTIONAL, 'the version'),

View File

@ -28,7 +28,7 @@ class ValidateCommand extends Command
{
$this
->setName('validate')
->setDescription('validates a composer.json')
->setDescription('Validates a composer.json')
->setDefinition(array(
new InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file', './composer.json')
))

View File

@ -72,7 +72,7 @@ class LibraryInstaller implements InstallerInterface
*/
public function isInstalled(PackageInterface $package)
{
return $this->repository->hasPackage($package);
return $this->repository->hasPackage($package) && is_readable($this->getInstallPath($package));
}
/**
@ -82,9 +82,16 @@ class LibraryInstaller implements InstallerInterface
{
$downloadPath = $this->getInstallPath($package);
// remove the binaries if it appears the package files are missing
if (!is_readable($downloadPath) && $this->repository->hasPackage($package)) {
$this->removeBinaries($package);
}
$this->downloadManager->download($package, $downloadPath);
$this->installBinaries($package);
$this->repository->addPackage(clone $package);
if (!$this->repository->hasPackage($package)) {
$this->repository->addPackage(clone $package);
}
}
/**

View File

@ -15,6 +15,7 @@ namespace Composer\Package;
use Composer\Package\LinkConstraint\LinkConstraintInterface;
use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\Repository\RepositoryInterface;
use Composer\Repository\PlatformRepository;
/**
* Base class for packages providing name storage and default match implementation
@ -134,6 +135,16 @@ abstract class BasePackage implements PackageInterface
$this->repository = $repository;
}
/**
* checks if this package is a platform package
*
* @return boolean
*/
public function isPlatform()
{
return $this->getRepository() instanceof PlatformRepository;
}
/**
* Returns package unique name, constructed from name, version and release type.
*

View File

@ -74,6 +74,13 @@ class ArrayLoader
$package->setBinaries($config['bin']);
}
if (isset($config['scripts']) && is_array($config['scripts'])) {
foreach ($config['scripts'] as $event => $listeners) {
$config['scripts'][$event]= (array) $listeners;
}
$package->setScripts($config['scripts']);
}
if (!empty($config['description']) && is_string($config['description'])) {
$package->setDescription($config['description']);
}

View File

@ -40,6 +40,7 @@ class MemoryPackage extends BasePackage
protected $homepage;
protected $extra = array();
protected $binaries = array();
protected $scripts = array();
protected $requires = array();
protected $conflicts = array();
@ -128,6 +129,22 @@ class MemoryPackage extends BasePackage
return $this->binaries;
}
/**
* @param array $scripts
*/
public function setScripts(array $scripts)
{
$this->scripts = $scripts;
}
/**
* {@inheritDoc}
*/
public function getScripts()
{
return $this->scripts;
}
/**
* {@inheritDoc}
*/

View File

@ -152,6 +152,13 @@ interface PackageInterface
*/
function getDistSha1Checksum();
/**
* Returns the scripts of this package
*
* @return array array('script name' => array('listeners'))
*/
function getScripts();
/**
* Returns the version of this package
*

View File

@ -79,6 +79,7 @@ abstract class VcsDriver
} else if (null !== $this->io->getLastUsername()) {
$authStr = base64_encode($this->io->getLastUsername() . ':' . $this->io->getLastPassword());
$params['http'] = array('header' => "Authorization: Basic $authStr\r\n");
$this->io->setAuthorization($this->url, $this->io->getLastUsername(), $this->io->getLastPassword());
}
$ctx = stream_context_create($params);

View File

@ -0,0 +1,26 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Script;
use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\Package\PackageInterface;
/**
* The Command Event.
*
* @author François Pluchino <francois.pluchino@opendisplay.com>
*/
class CommandEvent extends Event
{
}

View File

@ -0,0 +1,83 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Script;
use Composer\Composer;
use Composer\IO\IOInterface;
/**
* The base event class
*
* @author François Pluchino <francois.pluchino@opendisplay.com>
*/
class Event
{
/**
* @var string This event's name
*/
private $name;
/**
* @var Composer The composer instance
*/
private $composer;
/**
* @var IOInterface The IO instance
*/
private $io;
/**
* Constructor.
*
* @param string $name The event name
* @param Composer $composer The composer objet
* @param IOInterface $io The IOInterface object
*/
public function __construct($name, Composer $composer, IOInterface $io)
{
$this->name = $name;
$this->composer = $composer;
$this->io = $io;
}
/**
* Returns the event's name.
*
* @return string The event name
*/
public function getName()
{
return $this->name;
}
/**
* Returns the composer instance.
*
* @return Composer
*/
public function getComposer()
{
return $this->composer;
}
/**
* Returns the IO instance.
*
* @return IOInterface
*/
public function getIO()
{
return $this->io;
}
}

View File

@ -0,0 +1,120 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Script;
use Composer\Json\JsonFile;
use Composer\Repository\FilesystemRepository;
use Composer\Autoload\ClassLoader;
use Composer\Package\PackageInterface;
use Composer\IO\IOInterface;
use Composer\Composer;
use Composer\DependencyResolver\Operation\OperationInterface;
/**
* The Event Dispatcher.
*
* Example in command:
* $dispatcher = new EventDispatcher($this->getComposer(), $this->getApplication()->getIO());
* // ...
* $dispatcher->dispatch(ScriptEvents::POST_INSTALL_CMD);
* // ...
*
* @author François Pluchino <francois.pluchino@opendisplay.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class EventDispatcher
{
protected $composer;
protected $io;
protected $loader;
/**
* Constructor.
*
* @param Composer $composer The composer instance
* @param IOInterface $io The IOInterface instance
*/
public function __construct(Composer $composer, IOInterface $io)
{
$this->composer = $composer;
$this->io = $io;
$this->loader = new ClassLoader();
$this->loader->register();
}
/**
* Dispatch a package event.
*
* @param string $eventName The constant in ScriptEvents
* @param OperationInterface $operation The package being installed/updated/removed
*/
public function dispatchPackageEvent($eventName, OperationInterface $operation)
{
$this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $operation));
}
/**
* Dispatch a command event.
*
* @param string $eventName The constant in ScriptEvents
*/
public function dispatchCommandEvent($eventName)
{
$this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io));
}
/**
* Triggers the listeners of an event.
*
* @param Event $event The event object to pass to the event handlers/listeners.
*/
protected function doDispatch(Event $event)
{
$listeners = $this->getListeners($event);
foreach ($listeners as $callable) {
$className = substr($callable, 0, strpos($callable, '::'));
$methodName = substr($callable, strpos($callable, '::') + 2);
if (!class_exists($className)) {
throw new \UnexpectedValueException('Class '.$className.' is not autoloadable, can not call '.$event->getName().' script');
}
if (!is_callable($callable)) {
throw new \UnexpectedValueException('Method '.$callable.' is not callable, can not call '.$event->getName().' script');
}
$className::$methodName($event);
}
}
/**
* @param Event $event Event object
* @return array Listeners
*/
protected function getListeners(Event $event)
{
$package = $this->composer->getPackage();
$scripts = $package->getScripts();
$autoload = $package->getAutoload();
// get namespaces in composer.json project
if (!$this->loader->getPrefixes() && isset($autoload['psr-0'])) {
krsort($autoload['psr-0']);
foreach ($autoload['psr-0'] as $ns => $path) {
$this->loader->add($ns, rtrim(getcwd().'/'.$path, '/'));
}
}
return isset($scripts[$event->getName()]) ? $scripts[$event->getName()] : array();
}
}

View File

@ -0,0 +1,54 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Script;
use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\DependencyResolver\Operation\OperationInterface;
/**
* The Package Event.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class PackageEvent extends Event
{
/**
* @var OperationInterface The package instance
*/
private $operation;
/**
* Constructor.
*
* @param string $name The event name
* @param Composer $composer The composer objet
* @param IOInterface $io The IOInterface object
* @param OperationInterface $operation The operation object
*/
public function __construct($name, Composer $composer, IOInterface $io, OperationInterface $operation)
{
parent::__construct($name, $composer, $io);
$this->operation = $operation;
}
/**
* Returns the package instance.
*
* @return OperationInterface
*/
public function getOperation()
{
return $this->operation;
}
}

View File

@ -0,0 +1,112 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Script;
/**
* The Script Events.
*
* @author François Pluchino <francois.pluchino@opendisplay.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class ScriptEvents
{
/**
* The PRE_INSTALL_CMD event occurs before the install command is executed.
*
* The event listener method receives a Composer\Script\CommandEvent instance.
*
* @var string
*/
const PRE_INSTALL_CMD = 'pre-install-cmd';
/**
* The POST_INSTALL_CMD event occurs after the install command is executed.
*
* The event listener method receives a Composer\Script\CommandEvent instance.
*
* @var string
*/
const POST_INSTALL_CMD = 'post-install-cmd';
/**
* The PRE_UPDATE_CMD event occurs before the update command is executed.
*
* The event listener method receives a Composer\Script\CommandEvent instance.
*
* @var string
*/
const PRE_UPDATE_CMD = 'pre-update-cmd';
/**
* The POST_UPDATE_CMD event occurs after the update command is executed.
*
* The event listener method receives a Composer\Script\CommandEvent instance.
*
* @var string
*/
const POST_UPDATE_CMD = 'post-update-cmd';
/**
* The PRE_PACKAGE_INSTALL event occurs before a package is installed.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const PRE_PACKAGE_INSTALL = 'pre-package-install';
/**
* The POST_PACKAGE_INSTALL event occurs after a package is installed.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const POST_PACKAGE_INSTALL = 'post-package-install';
/**
* The PRE_PACKAGE_UPDATE event occurs before a package is updated.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const PRE_PACKAGE_UPDATE = 'pre-package-update';
/**
* The POST_PACKAGE_UPDATE event occurs after a package is updated.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const POST_PACKAGE_UPDATE = 'post-package-update';
/**
* The PRE_PACKAGE_UNINSTALL event occurs before a package has been uninstalled.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const PRE_PACKAGE_UNINSTALL = 'pre-package-uninstall';
/**
* The POST_PACKAGE_UNINSTALL event occurs after a package has been uninstalled.
*
* The event listener method receives a Composer\Script\PackageEvent instance.
*
* @var string
*/
const POST_PACKAGE_UNINSTALL = 'post-package-uninstall';
}

View File

@ -10,6 +10,12 @@
* file that was distributed with this source code.
*/
if ((!$loader = @include __DIR__.'/../../../.composer/autoload.php') && (!$loader = @include __DIR__.'/../vendor/.composer/autoload.php')) {
die('You must set up the project dependencies, run the following commands:'.PHP_EOL.
'curl -s http://getcomposer.org/installer | php'.PHP_EOL.
'php composer.phar install'.PHP_EOL);
}
$loader = require __DIR__.'/../vendor/.composer/autoload.php';
$loader->add('Composer\Test', __DIR__);
$loader->register();