You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

241 lines
7.8 KiB
PHTML

13 years ago
<?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\EventDispatcher;
13 years ago
use Composer\IO\IOInterface;
use Composer\Composer;
use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\Script;
use Composer\Script\CommandEvent;
use Composer\Script\PackageEvent;
use Composer\Util\ProcessExecutor;
13 years ago
/**
* 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>
* @author Nils Adermann <naderman@naderman.de>
13 years ago
*/
class EventDispatcher
{
protected $composer;
protected $io;
protected $loader;
protected $process;
13 years ago
/**
* Constructor.
*
12 years ago
* @param Composer $composer The composer instance
* @param IOInterface $io The IOInterface instance
* @param ProcessExecutor $process
13 years ago
*/
public function __construct(Composer $composer, IOInterface $io, ProcessExecutor $process = null)
13 years ago
{
$this->composer = $composer;
$this->io = $io;
$this->process = $process ?: new ProcessExecutor($io);
13 years ago
}
/**
* Dispatch an event
*
* @param string $eventName An event name
* @param Event $event
*/
public function dispatch($eventName, Event $event = null)
{
if (null == $event) {
$event = new Event($eventName);
}
$this->doDispatch($event);
}
/**
* Dispatch a script event.
*
11 years ago
* @param string $eventName The constant in ScriptEvents
* @param Event $event
*/
public function dispatchScript($eventName, Script\Event $event = null)
{
if (null == $event) {
$event = new Script\Event($eventName, $this->composer, $this->io);
}
$this->doDispatch($event);
}
13 years ago
/**
* Dispatch a package event.
*
* @param string $eventName The constant in ScriptEvents
* @param boolean $devMode Whether or not we are in dev mode
13 years ago
* @param OperationInterface $operation The package being installed/updated/removed
*/
public function dispatchPackageEvent($eventName, $devMode, OperationInterface $operation)
13 years ago
{
$this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $operation));
13 years ago
}
/**
* Dispatch a command event.
*
* @param string $eventName The constant in ScriptEvents
* @param boolean $devMode Whether or not we are in dev mode
13 years ago
*/
public function dispatchCommandEvent($eventName, $devMode)
13 years ago
{
$this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode));
13 years ago
}
/**
* Triggers the listeners of an event.
*
11 years ago
* @param Event $event The event object to pass to the event handlers/listeners.
* @throws \RuntimeException
* @throws \Exception
13 years ago
*/
protected function doDispatch(Event $event)
{
$listeners = $this->getListeners($event);
foreach ($listeners as $callable) {
if ((is_array($callable) && $is_callable($callable)) || $callable instanceof Closure) {
$callable($event);
} elseif ($this->isPhpScript($callable)) {
$className = substr($callable, 0, strpos($callable, '::'));
$methodName = substr($callable, strpos($callable, '::') + 2);
if (!class_exists($className)) {
$this->io->write('<warning>Class '.$className.' is not autoloadable, can not call '.$event->getName().' script</warning>');
continue;
}
if (!is_callable($callable)) {
$this->io->write('<warning>Method '.$callable.' is not callable, can not call '.$event->getName().' script</warning>');
continue;
}
try {
$this->executeEventPhpScript($className, $methodName, $event);
} catch (\Exception $e) {
$message = "Script %s handling the %s event terminated with an exception";
$this->io->write('<error>'.sprintf($message, $callable, $event->getName()).'</error>');
throw $e;
}
} else {
if (0 !== ($exitCode = $this->process->execute($callable))) {
$event->getIO()->write(sprintf('<error>Script %s handling the %s event returned with an error</error>', $callable, $event->getName()));
throw new \RuntimeException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
}
}
if ($event->isPropagationStopped()) {
break;
}
13 years ago
}
}
/**
* @param string $className
* @param string $methodName
12 years ago
* @param Event $event Event invoking the PHP callable
*/
protected function executeEventPhpScript($className, $methodName, Event $event)
{
$className::$methodName($event);
}
protected function addListener($eventName, $listener, $priority = 0)
{
$this->listeners[$eventName][$priority][] = $listener;
}
protected function addSubscriber($subscriber)
{
foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
if (is_string($params)) {
$this->addListener($eventName, array($subscriber, $params));
} elseif (is_string($params[0])) {
$this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0);
} else {
foreach ($params as $listener) {
$this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0);
}
}
}
}
protected function getListeners(Event $event)
{
$scriptListeners = $this->getScriptListeners($event);
if (!isset($this->listeners[$event->getName()][0])) {
$this->listeners[$event->getName()][0] = array();
}
krsort($this->listeners[$event->getName()]);
$listeners = $this->listeners;
$listeners[$event->getName()][0] = array_merge($listeners[$event->getName()][0], $scriptListeners);
return call_user_func_array('array_merge', $listeners[$event->getName()]);
}
13 years ago
/**
* @param Event $event Event object
13 years ago
* @return array Listeners
*/
protected function getScriptListeners(Event $event)
13 years ago
{
$package = $this->composer->getPackage();
$scripts = $package->getScripts();
if (empty($scripts[$event->getName()])) {
return array();
}
if ($this->loader) {
$this->loader->unregister();
13 years ago
}
$generator = $this->composer->getAutoloadGenerator();
$packages = $this->composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages();
$packageMap = $generator->buildPackageMap($this->composer->getInstallationManager(), $package, $packages);
$map = $generator->parseAutoloads($packageMap, $package);
$this->loader = $generator->createLoader($map);
$this->loader->register();
return $scripts[$event->getName()];
13 years ago
}
/**
* Checks if string given references a class path and method
*
12 years ago
* @param string $callable
* @return boolean
*/
protected function isPhpScript($callable)
{
return false === strpos($callable, ' ') && false !== strpos($callable, '::');
}
13 years ago
}