4 use Composer\Autoload\ClassLoader;
5 use League\Container\Container;
6 use League\Container\ContainerInterface;
7 use Robo\Common\ProcessExecutor;
8 use Consolidation\Config\ConfigInterface;
9 use Consolidation\Config\Loader\ConfigProcessor;
10 use Consolidation\Config\Loader\YamlConfigLoader;
11 use Symfony\Component\Console\Input\StringInput;
12 use Symfony\Component\Console\Application as SymfonyApplication;
13 use Symfony\Component\Process\Process;
16 * Manages the container reference and other static data. Favor
17 * using dependency injection wherever possible. Avoid using
18 * this class directly, unless setting up a custom DI container.
22 const APPLICATION_NAME = 'Robo';
23 const VERSION = '1.2.3';
26 * The currently active container object, or NULL if not initialized yet.
28 * @var ContainerInterface|null
30 protected static $container;
33 * Entrypoint for standalone Robo-based tools. See docs/framework.md.
35 * @param string[] $argv
36 * @param string $commandClasses
37 * @param null|string $appName
38 * @param null|string $appVersion
39 * @param null|\Symfony\Component\Console\Output\OutputInterface $output
43 public static function run($argv, $commandClasses, $appName = null, $appVersion = null, $output = null, $repository = null)
45 $runner = new \Robo\Runner($commandClasses);
46 $runner->setSelfUpdateRepository($repository);
47 $statusCode = $runner->execute($argv, $appName, $appVersion, $output);
52 * Sets a new global container.
54 * @param ContainerInterface $container
55 * A new container instance to replace the current.
57 public static function setContainer(ContainerInterface $container)
59 static::$container = $container;
63 * Unsets the global container.
65 public static function unsetContainer()
67 static::$container = null;
71 * Returns the currently active global container.
73 * @return \League\Container\ContainerInterface
75 * @throws \RuntimeException
77 public static function getContainer()
79 if (static::$container === null) {
80 throw new \RuntimeException('container is not initialized yet. \Robo\Robo::setContainer() must be called with a real container.');
82 return static::$container;
86 * Returns TRUE if the container has been initialized, FALSE otherwise.
90 public static function hasContainer()
92 return static::$container !== null;
96 * Create a config object and load it from the provided paths.
98 public static function createConfiguration($paths)
100 $config = new \Robo\Config\Config();
101 static::loadConfiguration($paths, $config);
106 * Use a simple config loader to load configuration values from specified paths
108 public static function loadConfiguration($paths, $config = null)
110 if ($config == null) {
111 $config = static::config();
113 $loader = new YamlConfigLoader();
114 $processor = new ConfigProcessor();
115 $processor->add($config->export());
116 foreach ($paths as $path) {
117 $processor->extend($loader->load($path));
119 $config->import($processor->export());
123 * Create a container and initiailze it. If you wish to *change*
124 * anything defined in the container, then you should call
125 * \Robo::configureContainer() instead of this function.
127 * @param null|\Symfony\Component\Console\Input\InputInterface $input
128 * @param null|\Symfony\Component\Console\Output\OutputInterface $output
129 * @param null|\Robo\Application $app
130 * @param null|ConfigInterface $config
131 * @param null|\Composer\Autoload\ClassLoader $classLoader
133 * @return \League\Container\Container|\League\Container\ContainerInterface
135 public static function createDefaultContainer($input = null, $output = null, $app = null, $config = null, $classLoader = null)
137 // Do not allow this function to be called more than once.
138 if (static::hasContainer()) {
139 return static::getContainer();
143 $app = static::createDefaultApplication();
147 $config = new \Robo\Config\Config();
150 // Set up our dependency injection container.
151 $container = new Container();
152 static::configureContainer($container, $app, $config, $input, $output, $classLoader);
154 // Set the application dispatcher
155 $app->setDispatcher($container->get('eventDispatcher'));
161 * Initialize a container with all of the default Robo services.
162 * IMPORTANT: after calling this method, clients MUST call:
164 * $dispatcher = $container->get('eventDispatcher');
165 * $app->setDispatcher($dispatcher);
167 * Any modification to the container should be done prior to fetching
170 * It is recommended to use \Robo::createDefaultContainer()
171 * instead, which does all required setup for the caller, but has
172 * the limitation that the container it creates can only be
173 * extended, not modified.
175 * @param \League\Container\ContainerInterface $container
176 * @param \Symfony\Component\Console\Application $app
177 * @param ConfigInterface $config
178 * @param null|\Symfony\Component\Console\Input\InputInterface $input
179 * @param null|\Symfony\Component\Console\Output\OutputInterface $output
180 * @param null|\Composer\Autoload\ClassLoader $classLoader
182 public static function configureContainer(ContainerInterface $container, SymfonyApplication $app, ConfigInterface $config, $input = null, $output = null, $classLoader = null)
184 // Self-referential container refernce for the inflector
185 $container->add('container', $container);
186 static::setContainer($container);
188 // Create default input and output objects if they were not provided
190 $input = new StringInput('');
193 $output = new \Symfony\Component\Console\Output\ConsoleOutput();
196 $classLoader = new ClassLoader();
198 $config->set(Config::DECORATED, $output->isDecorated());
199 $config->set(Config::INTERACTIVE, $input->isInteractive());
201 $container->share('application', $app);
202 $container->share('config', $config);
203 $container->share('input', $input);
204 $container->share('output', $output);
205 $container->share('outputAdapter', \Robo\Common\OutputAdapter::class);
206 $container->share('classLoader', $classLoader);
208 // Register logging and related services.
209 $container->share('logStyler', \Robo\Log\RoboLogStyle::class);
210 $container->share('logger', \Robo\Log\RoboLogger::class)
211 ->withArgument('output')
212 ->withMethodCall('setLogOutputStyler', ['logStyler']);
213 $container->add('progressBar', \Symfony\Component\Console\Helper\ProgressBar::class)
214 ->withArgument('output');
215 $container->share('progressIndicator', \Robo\Common\ProgressIndicator::class)
216 ->withArgument('progressBar')
217 ->withArgument('output');
218 $container->share('resultPrinter', \Robo\Log\ResultPrinter::class);
219 $container->add('simulator', \Robo\Task\Simulator::class);
220 $container->share('globalOptionsEventListener', \Robo\GlobalOptionsEventListener::class)
221 ->withMethodCall('setApplication', ['application']);
222 $container->share('injectConfigEventListener', \Consolidation\Config\Inject\ConfigForCommand::class)
223 ->withArgument('config')
224 ->withMethodCall('setApplication', ['application']);
225 $container->share('collectionProcessHook', \Robo\Collection\CollectionProcessHook::class);
226 $container->share('alterOptionsCommandEvent', \Consolidation\AnnotatedCommand\Options\AlterOptionsCommandEvent::class)
227 ->withArgument('application');
228 $container->share('hookManager', \Consolidation\AnnotatedCommand\Hooks\HookManager::class)
229 ->withMethodCall('addCommandEvent', ['alterOptionsCommandEvent'])
230 ->withMethodCall('addCommandEvent', ['injectConfigEventListener'])
231 ->withMethodCall('addCommandEvent', ['globalOptionsEventListener'])
232 ->withMethodCall('addResultProcessor', ['collectionProcessHook', '*']);
233 $container->share('eventDispatcher', \Symfony\Component\EventDispatcher\EventDispatcher::class)
234 ->withMethodCall('addSubscriber', ['hookManager']);
235 $container->share('formatterManager', \Consolidation\OutputFormatters\FormatterManager::class)
236 ->withMethodCall('addDefaultFormatters', [])
237 ->withMethodCall('addDefaultSimplifiers', []);
238 $container->share('prepareTerminalWidthOption', \Consolidation\AnnotatedCommand\Options\PrepareTerminalWidthOption::class)
239 ->withMethodCall('setApplication', ['application']);
240 $container->share('commandProcessor', \Consolidation\AnnotatedCommand\CommandProcessor::class)
241 ->withArgument('hookManager')
242 ->withMethodCall('setFormatterManager', ['formatterManager'])
243 ->withMethodCall('addPrepareFormatter', ['prepareTerminalWidthOption'])
245 'setDisplayErrorFunction',
247 function ($output, $message) use ($container) {
248 $logger = $container->get('logger');
249 $logger->error($message);
253 $container->share('commandFactory', \Consolidation\AnnotatedCommand\AnnotatedCommandFactory::class)
254 ->withMethodCall('setCommandProcessor', ['commandProcessor']);
255 $container->share('relativeNamespaceDiscovery', \Robo\ClassDiscovery\RelativeNamespaceDiscovery::class)
256 ->withArgument('classLoader');
258 // Deprecated: favor using collection builders to direct use of collections.
259 $container->add('collection', \Robo\Collection\Collection::class);
260 // Deprecated: use CollectionBuilder::create() instead -- or, better
261 // yet, BuilderAwareInterface::collectionBuilder() if available.
262 $container->add('collectionBuilder', \Robo\Collection\CollectionBuilder::class);
264 static::addInflectors($container);
266 // Make sure the application is appropriately initialized.
267 $app->setAutoExit(false);
271 * @param null|string $appName
272 * @param null|string $appVersion
274 * @return \Robo\Application
276 public static function createDefaultApplication($appName = null, $appVersion = null)
278 $appName = $appName ?: self::APPLICATION_NAME;
279 $appVersion = $appVersion ?: self::VERSION;
281 $app = new \Robo\Application($appName, $appVersion);
282 $app->setAutoExit(false);
287 * Add the Robo League\Container inflectors to the container
289 * @param \League\Container\ContainerInterface $container
291 public static function addInflectors($container)
293 // Register our various inflectors.
294 $container->inflector(\Robo\Contract\ConfigAwareInterface::class)
295 ->invokeMethod('setConfig', ['config']);
296 $container->inflector(\Psr\Log\LoggerAwareInterface::class)
297 ->invokeMethod('setLogger', ['logger']);
298 $container->inflector(\League\Container\ContainerAwareInterface::class)
299 ->invokeMethod('setContainer', ['container']);
300 $container->inflector(\Symfony\Component\Console\Input\InputAwareInterface::class)
301 ->invokeMethod('setInput', ['input']);
302 $container->inflector(\Robo\Contract\OutputAwareInterface::class)
303 ->invokeMethod('setOutput', ['output']);
304 $container->inflector(\Robo\Contract\ProgressIndicatorAwareInterface::class)
305 ->invokeMethod('setProgressIndicator', ['progressIndicator']);
306 $container->inflector(\Consolidation\AnnotatedCommand\Events\CustomEventAwareInterface::class)
307 ->invokeMethod('setHookManager', ['hookManager']);
308 $container->inflector(\Robo\Contract\VerbosityThresholdInterface::class)
309 ->invokeMethod('setOutputAdapter', ['outputAdapter']);
313 * Retrieves a service from the container.
315 * Use this method if the desired service is not one of those with a dedicated
316 * accessor method below. If it is listed below, those methods are preferred
317 * as they can return useful type hints.
320 * The ID of the service to retrieve.
323 * The specified service.
325 public static function service($id)
327 return static::getContainer()->get($id);
331 * Indicates if a service is defined in the container.
334 * The ID of the service to check.
337 * TRUE if the specified service exists, FALSE otherwise.
339 public static function hasService($id)
341 // Check hasContainer() first in order to always return a Boolean.
342 return static::hasContainer() && static::getContainer()->has($id);
346 * Return the result printer object.
348 * @return \Robo\Log\ResultPrinter
350 public static function resultPrinter()
352 return static::service('resultPrinter');
356 * @return ConfigInterface
358 public static function config()
360 return static::service('config');
364 * @return \Consolidation\Log\Logger
366 public static function logger()
368 return static::service('logger');
372 * @return \Robo\Application
374 public static function application()
376 return static::service('application');
380 * Return the output object.
382 * @return \Symfony\Component\Console\Output\OutputInterface
384 public static function output()
386 return static::service('output');
390 * Return the input object.
392 * @return \Symfony\Component\Console\Input\InputInterface
394 public static function input()
396 return static::service('input');
399 public static function process(Process $process)
401 return ProcessExecutor::create(static::getContainer(), $process);