From 7add1001ae59c5ffa20402afb616d14330e21eec Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 5 Feb 2012 16:14:25 +0100 Subject: [PATCH] Refactor scripts --- src/Composer/Command/InstallCommand.php | 16 +- src/Composer/Package/MemoryPackage.php | 17 ++ src/Composer/Package/PackageInterface.php | 7 + src/Composer/Script/CommandEvent.php | 26 +++ .../TriggerEvent.php => Script/Event.php} | 166 +++++++++--------- src/Composer/Script/EventDispatcher.php | 120 +++++++++++++ src/Composer/Script/PackageEvent.php | 54 ++++++ src/Composer/Script/ScriptEvents.php | 112 ++++++++++++ src/Composer/Trigger/TriggerDispatcher.php | 147 ---------------- src/Composer/Trigger/TriggerEvents.php | 89 ---------- 10 files changed, 428 insertions(+), 326 deletions(-) create mode 100644 src/Composer/Script/CommandEvent.php rename src/Composer/{Trigger/TriggerEvent.php => Script/Event.php} (90%) create mode 100644 src/Composer/Script/EventDispatcher.php create mode 100644 src/Composer/Script/PackageEvent.php create mode 100644 src/Composer/Script/ScriptEvents.php delete mode 100644 src/Composer/Trigger/TriggerDispatcher.php delete mode 100644 src/Composer/Trigger/TriggerEvents.php diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index f6f2594b9..5c4beba90 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -12,8 +12,8 @@ namespace Composer\Command; -use Composer\Trigger\TriggerEvents; -use Composer\Trigger\TriggerDispatcher; +use Composer\Script\ScriptEvents; +use Composer\Script\EventDispatcher; use Composer\Autoload\AutoloadGenerator; use Composer\DependencyResolver; use Composer\DependencyResolver\Pool; @@ -68,7 +68,7 @@ EOT $verbose = $dryRun || $input->getOption('verbose'); $composer = $this->getComposer(); $io = $this->getApplication()->getIO(); - $dispatcher = new TriggerDispatcher($this->getComposer(), $io); + $dispatcher = new EventDispatcher($this->getComposer(), $io); if ($preferSource) { $composer->getDownloadManager()->setPreferSource(true); @@ -88,8 +88,8 @@ EOT // dispatch pre event if (!$dryRun) { - $eventName = $update ? TriggerEvents::PRE_UPDATE : TriggerEvents::PRE_INSTALL; - $dispatcher->dispatch($eventName); + $eventName = $update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD; + $dispatcher->dispatchCommandEvent($eventName); } // creating requirements request @@ -172,7 +172,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); } } @@ -189,8 +191,8 @@ EOT $generator->dump($localRepo, $composer->getPackage(), $installationManager, $installationManager->getVendorPath().'/.composer'); // dispatch post event - $eventName = $update ? TriggerEvents::POST_UPDATE : TriggerEvents::POST_INSTALL; - $dispatcher->dispatch($eventName); + $eventName = $update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; + $dispatcher->dispatchCommandEvent($eventName); } } diff --git a/src/Composer/Package/MemoryPackage.php b/src/Composer/Package/MemoryPackage.php index 9167fde90..02222e43b 100644 --- a/src/Composer/Package/MemoryPackage.php +++ b/src/Composer/Package/MemoryPackage.php @@ -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} */ diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index 4616143b5..c8f92b581 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -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 * diff --git a/src/Composer/Script/CommandEvent.php b/src/Composer/Script/CommandEvent.php new file mode 100644 index 000000000..2526a99d7 --- /dev/null +++ b/src/Composer/Script/CommandEvent.php @@ -0,0 +1,26 @@ + + * Jordi Boggiano + * + * 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 + */ +class CommandEvent extends Event +{ +} diff --git a/src/Composer/Trigger/TriggerEvent.php b/src/Composer/Script/Event.php similarity index 90% rename from src/Composer/Trigger/TriggerEvent.php rename to src/Composer/Script/Event.php index dce3fa549..239d494c8 100644 --- a/src/Composer/Trigger/TriggerEvent.php +++ b/src/Composer/Script/Event.php @@ -1,83 +1,83 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Trigger; - -use Composer\Composer; -use Composer\IO\IOInterface; - -/** - * The Trigger Event. - * - * @author François Pluchino - */ -class TriggerEvent -{ - /** - * @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; - } -} + + * Jordi Boggiano + * + * 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 + */ +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; + } +} diff --git a/src/Composer/Script/EventDispatcher.php b/src/Composer/Script/EventDispatcher.php new file mode 100644 index 000000000..049762f7c --- /dev/null +++ b/src/Composer/Script/EventDispatcher.php @@ -0,0 +1,120 @@ + + * Jordi Boggiano + * + * 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 + * @author Jordi Boggiano + */ +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(); + } +} diff --git a/src/Composer/Script/PackageEvent.php b/src/Composer/Script/PackageEvent.php new file mode 100644 index 000000000..7d0e39407 --- /dev/null +++ b/src/Composer/Script/PackageEvent.php @@ -0,0 +1,54 @@ + + * Jordi Boggiano + * + * 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 + */ +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; + } +} diff --git a/src/Composer/Script/ScriptEvents.php b/src/Composer/Script/ScriptEvents.php new file mode 100644 index 000000000..9f5131345 --- /dev/null +++ b/src/Composer/Script/ScriptEvents.php @@ -0,0 +1,112 @@ + + * Jordi Boggiano + * + * 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 + * @author Jordi Boggiano + */ +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'; +} diff --git a/src/Composer/Trigger/TriggerDispatcher.php b/src/Composer/Trigger/TriggerDispatcher.php deleted file mode 100644 index e63410da5..000000000 --- a/src/Composer/Trigger/TriggerDispatcher.php +++ /dev/null @@ -1,147 +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\Trigger; - -use Composer\Json\JsonFile; -use Composer\Repository\FilesystemRepository; -use Composer\Autoload\ClassLoader; -use Composer\Package\PackageInterface; -use Composer\IO\IOInterface; -use Composer\Composer; - -/** - * The Trigger Dispatcher. - * - * Example in command: - * $dispatcher = new TriggerDispatcher($this->getComposer(), $this->getApplication()->getIO()); - * // ... - * $dispatcher->dispatch(TriggerEvents::POST_INSTALL); - * // ... - * - * @author François Pluchino - */ -class TriggerDispatcher -{ - 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(); - } - - /** - * Dispatch the event. - * - * @param string $eventName The constant in TriggerEvents - */ - public function dispatch($eventName) - { - $event = new TriggerEvent($eventName, $this->composer, $this->io); - - $this->doDispatch($event); - } - - /** - * Triggers the listeners of an event. - * - * @param TriggerEvent $event The event object to pass to the event handlers/listeners. - */ - protected function doDispatch(TriggerEvent $event) - { - $listeners = $this->getListeners($event); - - foreach ($listeners as $method) { - $className = substr($method, 0, strpos($method, '::')); - $methodName = substr($method, strpos($method, '::') + 2); - - try { - $refMethod = new \ReflectionMethod($className, $methodName); - - // execute only if all conditions are validates - if ($refMethod->isPublic() - && $refMethod->isStatic() - && !$refMethod->isAbstract() - && 1 === $refMethod->getNumberOfParameters()) { - $className::$methodName($event); - } - - } catch (\ReflectionException $ex) {}//silent execpetion - } - } - - /** - * Register namespaces in ClassLoader. - * - * @param TriggerEvent $event The event object - * - * @return array The listener classes with event type - */ - protected function getListeners(TriggerEvent $event) - { - $package = $this->composer->getPackage(); - $vendorDir = $this->composer->getInstallationManager()->getVendorPath(true); - $autoloadFile = $vendorDir . '/.composer/autoload.php'; - $ex = $package->getExtra(); - $al = $package->getAutoload(); - $searchListeners = array(); - $listeners = array(); - $namespaces = array(); - - // get classes - if (isset($ex['triggers'][$event->getName()])) { - foreach ($ex['triggers'][$event->getName()] as $method) { - $searchListeners[] = $method; - } - } - - // get autoload namespaces - if (file_exists($autoloadFile)) { - $this->loader = require $autoloadFile; - } - - $namespaces = $this->loader->getPrefixes(); - - // get namespaces in composer.json project - if (isset($al['psr-0'])) { - foreach ($al['psr-0'] as $ns => $path) { - if (!isset($namespaces[str_replace('\\', '\\\\', $ns)])) { - $this->loader->add($ns, trim(realpath('.').'/'.$path, '/')); - } - } - - $this->loader->register(); - $namespaces = $this->loader->getPrefixes(); - } - - // filter class::method have not a namespace registered - foreach ($namespaces as $ns => $path) { - foreach ($searchListeners as $method) { - if (0 === strpos($method, $ns)) { - $listeners[] = $method; - } - } - } - - return $listeners; - } -} diff --git a/src/Composer/Trigger/TriggerEvents.php b/src/Composer/Trigger/TriggerEvents.php deleted file mode 100644 index 61392405f..000000000 --- a/src/Composer/Trigger/TriggerEvents.php +++ /dev/null @@ -1,89 +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\Trigger; - -/** - * The Trigger Events. - * - * @author François Pluchino - */ -class TriggerEvents -{ - /** - * The PRE_INSTALL event occurs at begging installation packages. - * - * This event allows you to execute a trigger before any other code in the - * composer is executed. The event listener method receives a - * Composer\Trigger\GetTriggerEvent instance. - * - * @var string - */ - const PRE_INSTALL = 'pre_install'; - - /** - * The POST_INSTALL event occurs at end installation packages. - * - * This event allows you to execute a trigger after any other code in the - * composer is executed. The event listener method receives a - * Composer\Trigger\GetTriggerEvent instance. - * - * @var string - */ - const POST_INSTALL = 'post_install'; - - /** - * The PRE_UPDATE event occurs at begging update packages. - * - * This event allows you to execute a trigger before any other code in the - * composer is executed. The event listener method receives a - * Composer\Trigger\GetTriggerEvent instance. - * - * @var string - */ - const PRE_UPDATE = 'pre_update'; - - /** - * The POST_UPDATE event occurs at end update packages. - * - * This event allows you to execute a trigger after any other code in the - * composer is executed. The event listener method receives a - * Composer\Trigger\GetTriggerEvent instance. - * - * @var string - */ - const POST_UPDATE = 'post_update'; - - /** - * The PRE_UNINSTALL event occurs at begging uninstallation packages. - * - * This event allows you to execute a trigger after any other code in the - * composer is executed. The event listener method receives a - * Composer\Trigger\TriggerEvent instance. - * - * @var string - */ - const PRE_UNINSTALL = 'pre_uninstall'; - //TODO add the dispatcher when the uninstall command will be doing - - /** - * The PRE_UNINSTALL event occurs at end uninstallation packages. - * - * This event allows you to execute a trigger after any other code in the - * composer is executed. The event listener method receives a - * Composer\Trigger\TriggerEvent instance. - * - * @var string - */ - const POST_UNINSTALL = 'post_uninstall'; - //TODO add the dispatcher when the uninstall command will be doing -}