X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs-website;a=blobdiff_plain;f=vendor%2Fconsolidation%2Frobo%2Fsrc%2FRunner.php;fp=vendor%2Fconsolidation%2Frobo%2Fsrc%2FRunner.php;h=e1a779c35c8d8a4ffae75df4ad774794daf8b2c6;hp=0000000000000000000000000000000000000000;hb=af6d1fb995500ae68849458ee10d66abbdcfb252;hpb=680c79a86e3ed402f263faeac92e89fb6d9edcc0 diff --git a/vendor/consolidation/robo/src/Runner.php b/vendor/consolidation/robo/src/Runner.php new file mode 100644 index 000000000..e1a779c35 --- /dev/null +++ b/vendor/consolidation/robo/src/Runner.php @@ -0,0 +1,522 @@ +roboClass = $roboClass ? $roboClass : self::ROBOCLASS ; + $this->roboFile = $roboFile ? $roboFile : self::ROBOFILE; + $this->dir = getcwd(); + } + + protected function errorCondition($msg, $errorType) + { + $this->errorConditions[$msg] = $errorType; + } + + /** + * @param \Symfony\Component\Console\Output\OutputInterface $output + * + * @return bool + */ + protected function loadRoboFile($output) + { + // If we have not been provided an output object, make a temporary one. + if (!$output) { + $output = new \Symfony\Component\Console\Output\ConsoleOutput(); + } + + // If $this->roboClass is a single class that has not already + // been loaded, then we will try to obtain it from $this->roboFile. + // If $this->roboClass is an array, we presume all classes requested + // are available via the autoloader. + if (is_array($this->roboClass) || class_exists($this->roboClass)) { + return true; + } + if (!file_exists($this->dir)) { + $this->errorCondition("Path `{$this->dir}` is invalid; please provide a valid absolute path to the Robofile to load.", 'red'); + return false; + } + + $realDir = realpath($this->dir); + + $roboFilePath = $realDir . DIRECTORY_SEPARATOR . $this->roboFile; + if (!file_exists($roboFilePath)) { + $requestedRoboFilePath = $this->dir . DIRECTORY_SEPARATOR . $this->roboFile; + $this->errorCondition("Requested RoboFile `$requestedRoboFilePath` is invalid, please provide valid absolute path to load Robofile.", 'red'); + return false; + } + require_once $roboFilePath; + + if (!class_exists($this->roboClass)) { + $this->errorCondition("Class {$this->roboClass} was not loaded.", 'red'); + return false; + } + return true; + } + + /** + * @param array $argv + * @param null|string $appName + * @param null|string $appVersion + * @param null|\Symfony\Component\Console\Output\OutputInterface $output + * + * @return int + */ + public function execute($argv, $appName = null, $appVersion = null, $output = null) + { + $argv = $this->shebang($argv); + $argv = $this->processRoboOptions($argv); + $app = null; + if ($appName && $appVersion) { + $app = Robo::createDefaultApplication($appName, $appVersion); + } + $commandFiles = $this->getRoboFileCommands($output); + return $this->run($argv, $output, $app, $commandFiles, $this->classLoader); + } + + /** + * @param null|\Symfony\Component\Console\Input\InputInterface $input + * @param null|\Symfony\Component\Console\Output\OutputInterface $output + * @param null|\Robo\Application $app + * @param array[] $commandFiles + * @param null|ClassLoader $classLoader + * + * @return int + */ + public function run($input = null, $output = null, $app = null, $commandFiles = [], $classLoader = null) + { + // Create default input and output objects if they were not provided + if (!$input) { + $input = new StringInput(''); + } + if (is_array($input)) { + $input = new ArgvInput($input); + } + if (!$output) { + $output = new \Symfony\Component\Console\Output\ConsoleOutput(); + } + $this->setInput($input); + $this->setOutput($output); + + // If we were not provided a container, then create one + if (!$this->getContainer()) { + $userConfig = 'robo.yml'; + $roboAppConfig = dirname(__DIR__) . '/robo.yml'; + $config = Robo::createConfiguration([$userConfig, $roboAppConfig]); + $container = Robo::createDefaultContainer($input, $output, $app, $config, $classLoader); + $this->setContainer($container); + // Automatically register a shutdown function and + // an error handler when we provide the container. + $this->installRoboHandlers(); + } + + if (!$app) { + $app = Robo::application(); + } + if ($app instanceof \Robo\Application) { + $app->addSelfUpdateCommand($this->getSelfUpdateRepository()); + if (!isset($commandFiles)) { + $this->errorCondition("Robo is not initialized here. Please run `robo init` to create a new RoboFile.", 'yellow'); + $app->addInitRoboFileCommand($this->roboFile, $this->roboClass); + $commandFiles = []; + } + } + + if (!empty($this->relativePluginNamespace)) { + $commandClasses = $this->discoverCommandClasses($this->relativePluginNamespace); + $commandFiles = array_merge((array)$commandFiles, $commandClasses); + } + + $this->registerCommandClasses($app, $commandFiles); + + try { + $statusCode = $app->run($input, $output); + } catch (TaskExitException $e) { + $statusCode = $e->getCode() ?: 1; + } + + // If there were any error conditions in bootstrapping Robo, + // print them only if the requested command did not complete + // successfully. + if ($statusCode) { + foreach ($this->errorConditions as $msg => $color) { + $this->yell($msg, 40, $color); + } + } + return $statusCode; + } + + /** + * @param \Symfony\Component\Console\Output\OutputInterface $output + * + * @return null|string + */ + protected function getRoboFileCommands($output) + { + if (!$this->loadRoboFile($output)) { + return; + } + return $this->roboClass; + } + + /** + * @param \Robo\Application $app + * @param array $commandClasses + */ + public function registerCommandClasses($app, $commandClasses) + { + foreach ((array)$commandClasses as $commandClass) { + $this->registerCommandClass($app, $commandClass); + } + } + + /** + * @param $relativeNamespace + * + * @return array|string[] + */ + protected function discoverCommandClasses($relativeNamespace) + { + /** @var \Robo\ClassDiscovery\RelativeNamespaceDiscovery $discovery */ + $discovery = Robo::service('relativeNamespaceDiscovery'); + $discovery->setRelativeNamespace($relativeNamespace.'\Commands') + ->setSearchPattern('*Commands.php'); + return $discovery->getClasses(); + } + + /** + * @param \Robo\Application $app + * @param string|BuilderAwareInterface|ContainerAwareInterface $commandClass + * + * @return mixed|void + */ + public function registerCommandClass($app, $commandClass) + { + $container = Robo::getContainer(); + $roboCommandFileInstance = $this->instantiateCommandClass($commandClass); + if (!$roboCommandFileInstance) { + return; + } + + // Register commands for all of the public methods in the RoboFile. + $commandFactory = $container->get('commandFactory'); + $commandList = $commandFactory->createCommandsFromClass($roboCommandFileInstance); + foreach ($commandList as $command) { + $app->add($command); + } + return $roboCommandFileInstance; + } + + /** + * @param string|BuilderAwareInterface|ContainerAwareInterface $commandClass + * + * @return null|object + */ + protected function instantiateCommandClass($commandClass) + { + $container = Robo::getContainer(); + + // Register the RoboFile with the container and then immediately + // fetch it; this ensures that all of the inflectors will run. + // If the command class is already an instantiated object, then + // just use it exactly as it was provided to us. + if (is_string($commandClass)) { + if (!class_exists($commandClass)) { + return; + } + $reflectionClass = new \ReflectionClass($commandClass); + if ($reflectionClass->isAbstract()) { + return; + } + + $commandFileName = "{$commandClass}Commands"; + $container->share($commandFileName, $commandClass); + $commandClass = $container->get($commandFileName); + } + // If the command class is a Builder Aware Interface, then + // ensure that it has a builder. Every command class needs + // its own collection builder, as they have references to each other. + if ($commandClass instanceof BuilderAwareInterface) { + $builder = CollectionBuilder::create($container, $commandClass); + $commandClass->setBuilder($builder); + } + if ($commandClass instanceof ContainerAwareInterface) { + $commandClass->setContainer($container); + } + return $commandClass; + } + + public function installRoboHandlers() + { + register_shutdown_function(array($this, 'shutdown')); + set_error_handler(array($this, 'handleError')); + } + + /** + * Process a shebang script, if one was used to launch this Runner. + * + * @param array $args + * + * @return array $args with shebang script removed + */ + protected function shebang($args) + { + // Option 1: Shebang line names Robo, but includes no parameters. + // #!/bin/env robo + // The robo class may contain multiple commands; the user may + // select which one to run, or even get a list of commands or + // run 'help' on any of the available commands as usual. + if ((count($args) > 1) && $this->isShebangFile($args[1])) { + return array_merge([$args[0]], array_slice($args, 2)); + } + // Option 2: Shebang line stipulates which command to run. + // #!/bin/env robo mycommand + // The robo class must contain a public method named 'mycommand'. + // This command will be executed every time. Arguments and options + // may be provided on the commandline as usual. + if ((count($args) > 2) && $this->isShebangFile($args[2])) { + return array_merge([$args[0]], explode(' ', $args[1]), array_slice($args, 3)); + } + return $args; + } + + /** + * Determine if the specified argument is a path to a shebang script. + * If so, load it. + * + * @param string $filepath file to check + * + * @return bool Returns TRUE if shebang script was processed + */ + protected function isShebangFile($filepath) + { + if (!is_file($filepath)) { + return false; + } + $fp = fopen($filepath, "r"); + if ($fp === false) { + return false; + } + $line = fgets($fp); + $result = $this->isShebangLine($line); + if ($result) { + while ($line = fgets($fp)) { + $line = trim($line); + if ($line == 'roboClass = $matches[1]; + eval($script); + $result = true; + } + } + } + } + fclose($fp); + + return $result; + } + + /** + * Test to see if the provided line is a robo 'shebang' line. + * + * @param string $line + * + * @return bool + */ + protected function isShebangLine($line) + { + return ((substr($line, 0, 2) == '#!') && (strstr($line, 'robo') !== false)); + } + + /** + * Check for Robo-specific arguments such as --load-from, process them, + * and remove them from the array. We have to process --load-from before + * we set up Symfony Console. + * + * @param array $argv + * + * @return array + */ + protected function processRoboOptions($argv) + { + // loading from other directory + $pos = $this->arraySearchBeginsWith('--load-from', $argv) ?: array_search('-f', $argv); + if ($pos === false) { + return $argv; + } + + $passThru = array_search('--', $argv); + if (($passThru !== false) && ($passThru < $pos)) { + return $argv; + } + + if (substr($argv[$pos], 0, 12) == '--load-from=') { + $this->dir = substr($argv[$pos], 12); + } elseif (isset($argv[$pos +1])) { + $this->dir = $argv[$pos +1]; + unset($argv[$pos +1]); + } + unset($argv[$pos]); + // Make adjustments if '--load-from' points at a file. + if (is_file($this->dir) || (substr($this->dir, -4) == '.php')) { + $this->roboFile = basename($this->dir); + $this->dir = dirname($this->dir); + $className = basename($this->roboFile, '.php'); + if ($className != $this->roboFile) { + $this->roboClass = $className; + } + } + // Convert directory to a real path, but only if the + // path exists. We do not want to lose the original + // directory if the user supplied a bad value. + $realDir = realpath($this->dir); + if ($realDir) { + chdir($realDir); + $this->dir = $realDir; + } + + return $argv; + } + + /** + * @param string $needle + * @param string[] $haystack + * + * @return bool|int + */ + protected function arraySearchBeginsWith($needle, $haystack) + { + for ($i = 0; $i < count($haystack); ++$i) { + if (substr($haystack[$i], 0, strlen($needle)) == $needle) { + return $i; + } + } + return false; + } + + public function shutdown() + { + $error = error_get_last(); + if (!is_array($error)) { + return; + } + $this->writeln(sprintf("ERROR: %s \nin %s:%d\n", $error['message'], $error['file'], $error['line'])); + } + + /** + * This is just a proxy error handler that checks the current error_reporting level. + * In case error_reporting is disabled the error is marked as handled, otherwise + * the normal internal error handling resumes. + * + * @return bool + */ + public function handleError() + { + if (error_reporting() === 0) { + return true; + } + return false; + } + + /** + * @return string + */ + public function getSelfUpdateRepository() + { + return $this->selfUpdateRepository; + } + + /** + * @param $selfUpdateRepository + * + * @return $this + */ + public function setSelfUpdateRepository($selfUpdateRepository) + { + $this->selfUpdateRepository = $selfUpdateRepository; + return $this; + } + + /** + * @param \Composer\Autoload\ClassLoader $classLoader + * + * @return $this + */ + public function setClassLoader(ClassLoader $classLoader) + { + $this->classLoader = $classLoader; + return $this; + } + + /** + * @param string $relativeNamespace + * + * @return $this + */ + public function setRelativePluginNamespace($relativeNamespace) + { + $this->relativePluginNamespace = $relativeNamespace; + return $this; + } +}