Completed
Pull Request — master (#893)
by Greg
05:57
created

src/Robo.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
namespace Robo;
3
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;
14
15
/**
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.
19
 */
20
class Robo
21
{
22
    const APPLICATION_NAME = 'Robo';
23
    const VERSION = '2.0.0-dev';
24
25
    /**
26
     * The currently active container object, or NULL if not initialized yet.
27
     *
28
     * @var \League\Container\ContainerInterface|null
29
     */
30
    protected static $container;
31
32
    /**
33
     * Entrypoint for standalone Robo-based tools.  See docs/framework.md.
34
     *
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
40
     * @param null|string $repository
41
     *
42
     * @return int
43
     */
44
    public static function run($argv, $commandClasses, $appName = null, $appVersion = null, $output = null, $repository = null)
45
    {
46
        $runner = new \Robo\Runner($commandClasses);
47
        $runner->setSelfUpdateRepository($repository);
48
        $statusCode = $runner->execute($argv, $appName, $appVersion, $output);
49
        return $statusCode;
50
    }
51
52
    /**
53
     * Sets a new global container.
54
     *
55
     * @param \League\Container\ContainerInterface $container
56
     *   A new container instance to replace the current.
57
     */
58
    public static function setContainer(ContainerInterface $container)
59
    {
60
        static::$container = $container;
61
    }
62
63
    /**
64
     * Unsets the global container.
65
     */
66
    public static function unsetContainer()
67
    {
68
        static::$container = null;
69
    }
70
71
    /**
72
     * Returns the currently active global container.
73
     *
74
     * @return \League\Container\ContainerInterface
75
     *
76
     * @throws \RuntimeException
77
     */
78
    public static function getContainer()
79
    {
80
        if (static::$container === null) {
81
            throw new \RuntimeException('container is not initialized yet. \Robo\Robo::setContainer() must be called with a real container.');
82
        }
83
        return static::$container;
84
    }
85
86
    /**
87
     * Returns TRUE if the container has been initialized, FALSE otherwise.
88
     *
89
     * @return bool
90
     */
91
    public static function hasContainer()
92
    {
93
        return static::$container !== null;
94
    }
95
96
    /**
97
     * Create a config object and load it from the provided paths.
98
     *
99
     * @param string[] $paths
100
     *
101
     * @return \Consolidation\Config\ConfigInterface
102
     */
103
    public static function createConfiguration($paths)
104
    {
105
        $config = new \Robo\Config\Config();
106
        static::loadConfiguration($paths, $config);
107
        return $config;
108
    }
109
110
    /**
111
     * Use a simple config loader to load configuration values from specified paths
112
     *
113
     * @param string[] $paths
114
     * @param null|\Consolidation\Config\ConfigInterface $config
115
     */
116
    public static function loadConfiguration($paths, $config = null)
117
    {
118
        if ($config == null) {
119
            $config = static::config();
120
        }
121
        $loader = new YamlConfigLoader();
122
        $processor = new ConfigProcessor();
123
        $processor->add($config->export());
124
        foreach ($paths as $path) {
125
            $processor->extend($loader->load($path));
126
        }
127
        $config->import($processor->export());
0 ignored issues
show
Deprecated Code introduced by
The method Consolidation\Config\ConfigInterface::import() has been deprecated with message: Use 'replace'. Dflydev\DotAccessData\Data::import() merges, which is confusing, since this method replaces.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
128
    }
129
130
    /**
131
     * Create a container and initiailze it.  If you wish to *change*
132
     * anything defined in the container, then you should call
133
     * \Robo::configureContainer() instead of this function.
134
     *
135
     * @param null|\Symfony\Component\Console\Input\InputInterface $input
136
     * @param null|\Symfony\Component\Console\Output\OutputInterface $output
137
     * @param null|\Robo\Application $app
138
     * @param null|\Consolidation\Config\ConfigInterface $config
139
     * @param null|\Composer\Autoload\ClassLoader $classLoader
140
     *
141
     * @return \League\Container\Container|\League\Container\ContainerInterface
142
     */
143
    public static function createDefaultContainer($input = null, $output = null, $app = null, $config = null, $classLoader = null)
144
    {
145
        // Do not allow this function to be called more than once.
146
        if (static::hasContainer()) {
147
            return static::getContainer();
148
        }
149
150
        if (!$app) {
151
            $app = static::createDefaultApplication();
152
        }
153
154
        if (!$config) {
155
            $config = new \Robo\Config\Config();
156
        }
157
158
        // Set up our dependency injection container.
159
        $container = new Container();
160
        static::configureContainer($container, $app, $config, $input, $output, $classLoader);
161
        static::configureApplication($app, $container);
162
163
        return $container;
164
    }
165
166
    public static function configureApplication($app, $container)
167
    {
168
        // Set the application dispatcher
169
        $app->setDispatcher($container->get('eventDispatcher'));
170
171
        if ($app instanceof \Robo\Contract\IOAwareInterface) {
172
            $app->setIOStorage($container->get('ioStorage'));
173
        }
174
        if ($app instanceof \Psr\Log\LoggerAwareInterface) {
175
            $app->setLogger($container->get('logger'));
176
        }
177
    }
178
179
    /**
180
     * Initialize a container with all of the default Robo services.
181
     * IMPORTANT:  after calling this method, clients MUST call:
182
     *
183
     *   \Robo\Robo::configureApplication($app, $container);
184
     *
185
     * Any modification to the container should be done prior to fetching
186
     * objects from it.
187
     *
188
     * It is recommended to use \Robo::createDefaultContainer()
189
     * instead, which does all required setup for the caller, but has
190
     * the limitation that the container it creates can only be
191
     * extended, not modified.
192
     *
193
     * @param \League\Container\ContainerInterface $container
194
     * @param \Symfony\Component\Console\Application $app
195
     * @param \Consolidation\Config\ConfigInterface $config
196
     * @param null|\Symfony\Component\Console\Input\InputInterface $input
197
     * @param null|\Symfony\Component\Console\Output\OutputInterface $output
198
     * @param null|\Composer\Autoload\ClassLoader $classLoader
199
     */
200
    public static function configureContainer(ContainerInterface $container, SymfonyApplication $app, ConfigInterface $config, $input = null, $output = null, $classLoader = null)
201
    {
202
        // Self-referential container refernce for the inflector
203
        $container->add('container', $container);
204
        static::setContainer($container);
205
206
        // Create default input and output objects if they were not provided
207
        if (!$input) {
208
            $input = new StringInput('');
209
        }
210
        if (!$output) {
211
            $output = new \Symfony\Component\Console\Output\ConsoleOutput();
212
        }
213
        if (!$classLoader) {
214
            $classLoader = new ClassLoader();
215
        }
216
        $config->set(Config::DECORATED, $output->isDecorated());
217
        $config->set(Config::INTERACTIVE, $input->isInteractive());
218
219
        $container->share('application', $app);
220
        $container->share('config', $config);
221
        $container->share('input', $input);
222
        $container->share('output', $output);
223
        $container->share('outputAdapter', \Robo\Common\OutputAdapter::class);
224
        $container->share('classLoader', $classLoader);
225
226
        // Register logging and related services.
227
        $container->share('logStyler', \Robo\Log\RoboLogStyle::class);
228
        $container->share('roboLogger', \Robo\Log\RoboLogger::class)
229
            ->withMethodCall('setLogOutputStyler', ['logStyler'])
230
            ->withArgument('output');
231
        $container->share('logger', \Consolidation\Log\LoggerManager::class)
232
            ->withMethodCall('setLogOutputStyler', ['logStyler'])
233
            ->withMethodCall('fallbackLogger', ['roboLogger']);
234
        $container->add('progressBar', \Symfony\Component\Console\Helper\ProgressBar::class)
235
            ->withArgument('output');
236
        $container->share('progressIndicator', \Robo\Common\ProgressIndicator::class)
237
            ->withArgument('progressBar')
238
            ->withArgument('output');
239
        $container->share('resultPrinter', \Robo\Log\ResultPrinter::class);
240
        $container->add('simulator', \Robo\Task\Simulator::class);
241
        $container->share('globalOptionsEventListener', \Robo\GlobalOptionsEventListener::class)
242
            ->withMethodCall('setApplication', ['application']);
243
        $container->share('injectConfigEventListener', \Consolidation\Config\Inject\ConfigForCommand::class)
244
            ->withArgument('config')
245
            ->withMethodCall('setApplication', ['application']);
246
        $container->share('collectionProcessHook', \Robo\Collection\CollectionProcessHook::class);
247
        $container->share('alterOptionsCommandEvent', \Consolidation\AnnotatedCommand\Options\AlterOptionsCommandEvent::class)
248
            ->withArgument('application');
249
        $container->share('hookManager', \Consolidation\AnnotatedCommand\Hooks\HookManager::class)
250
            ->withMethodCall('addCommandEvent', ['alterOptionsCommandEvent'])
251
            ->withMethodCall('addCommandEvent', ['injectConfigEventListener'])
252
            ->withMethodCall('addCommandEvent', ['globalOptionsEventListener'])
253
            ->withMethodCall('addResultProcessor', ['collectionProcessHook', '*']);
254
        $container->share('eventDispatcher', \Symfony\Component\EventDispatcher\EventDispatcher::class)
255
            ->withMethodCall('addSubscriber', ['hookManager']);
256
        $container->share('formatterManager', \Consolidation\OutputFormatters\FormatterManager::class)
257
            ->withMethodCall('addDefaultFormatters', [])
258
            ->withMethodCall('addDefaultSimplifiers', []);
259
        $container->share('prepareTerminalWidthOption', \Consolidation\AnnotatedCommand\Options\PrepareTerminalWidthOption::class)
260
            ->withMethodCall('setApplication', ['application']);
261
        $container->share('symfonyStyleInjector', \Robo\Symfony\SymfonyStyleInjector::class);
262
        $container->share('parameterInjection', \Consolidation\AnnotatedCommand\ParameterInjection::class)
263
            ->withMethodCall('register', ['Symfony\Component\Console\Style\SymfonyStyle', 'symfonyStyleInjector']);
264
        $container->share('commandProcessor', \Consolidation\AnnotatedCommand\CommandProcessor::class)
265
            ->withArgument('hookManager')
266
            ->withMethodCall('setFormatterManager', ['formatterManager'])
267
            ->withMethodCall('addPrepareFormatter', ['prepareTerminalWidthOption'])
268
            ->withMethodCall('setParameterInjection', ['parameterInjection'])
269
            ->withMethodCall(
270
                'setDisplayErrorFunction',
271
                [
272
                    function ($output, $message) use ($container) {
273
                        $logger = $container->get('logger');
274
                        $logger->error($message);
275
                    }
276
                ]
277
            );
278
        $container->share('ioStorage', \Robo\Symfony\IOStorage::class);
279
        $container->share('stdinHandler', \Consolidation\AnnotatedCommand\Input\StdinHandler::class);
280
        $container->share('commandFactory', \Consolidation\AnnotatedCommand\AnnotatedCommandFactory::class)
281
            ->withMethodCall('setCommandProcessor', ['commandProcessor']);
282
        $container->share('relativeNamespaceDiscovery', \Robo\ClassDiscovery\RelativeNamespaceDiscovery::class)
283
            ->withArgument('classLoader');
284
285
        // Deprecated: favor using collection builders to direct use of collections.
286
        $container->add('collection', \Robo\Collection\Collection::class);
287
        // Deprecated: use CollectionBuilder::create() instead -- or, better
288
        // yet, BuilderAwareInterface::collectionBuilder() if available.
289
        $container->add('collectionBuilder', \Robo\Collection\CollectionBuilder::class);
290
291
        static::addInflectors($container);
292
293
        // Make sure the application is appropriately initialized.
294
        $app->setAutoExit(false);
295
    }
296
297
    /**
298
     * @param null|string $appName
299
     * @param null|string $appVersion
300
     *
301
     * @return \Robo\Application
302
     */
303
    public static function createDefaultApplication($appName = null, $appVersion = null)
304
    {
305
        $appName = $appName ?: self::APPLICATION_NAME;
306
        $appVersion = $appVersion ?: self::VERSION;
307
308
        $app = new \Robo\Application($appName, $appVersion);
309
        $app->setAutoExit(false);
310
        return $app;
311
    }
312
313
    /**
314
     * Add the Robo League\Container inflectors to the container
315
     *
316
     * @param \League\Container\ContainerInterface $container
317
     */
318
    public static function addInflectors($container)
319
    {
320
        // Register our various inflectors.
321
        $container->inflector(\Robo\Contract\ConfigAwareInterface::class)
322
            ->invokeMethod('setConfig', ['config']);
323
        $container->inflector(\Psr\Log\LoggerAwareInterface::class)
324
            ->invokeMethod('setLogger', ['logger']);
325
        $container->inflector(\League\Container\ContainerAwareInterface::class)
326
            ->invokeMethod('setContainer', ['container']);
327
        $container->inflector(\Robo\Symfony\IOAwareInterface::class)
328
            ->invokeMethod('setIOStorage', ['ioStorage']);
329
        $container->inflector(\Symfony\Component\Console\Input\InputAwareInterface::class)
330
            ->invokeMethod('setInput', ['input']);
331
        $container->inflector(\Robo\Contract\OutputAwareInterface::class)
332
            ->invokeMethod('setOutput', ['output']);
333
        $container->inflector(\Robo\Contract\ProgressIndicatorAwareInterface::class)
334
            ->invokeMethod('setProgressIndicator', ['progressIndicator']);
335
        $container->inflector(\Consolidation\AnnotatedCommand\Events\CustomEventAwareInterface::class)
336
            ->invokeMethod('setHookManager', ['hookManager']);
337
        $container->inflector(\Robo\Contract\VerbosityThresholdInterface::class)
338
            ->invokeMethod('setOutputAdapter', ['outputAdapter']);
339
        $container->inflector(\Consolidation\AnnotatedCommand\Input\StdinAwareInterface::class)
340
            ->invokeMethod('setStdinHandler', ['stdinHandler']);
341
    }
342
343
    /**
344
     * Retrieves a service from the container.
345
     *
346
     * Use this method if the desired service is not one of those with a dedicated
347
     * accessor method below. If it is listed below, those methods are preferred
348
     * as they can return useful type hints.
349
     *
350
     * @param string $id
351
     *   The ID of the service to retrieve.
352
     *
353
     * @return mixed
354
     *   The specified service.
355
     */
356
    public static function service($id)
357
    {
358
        return static::getContainer()->get($id);
359
    }
360
361
    /**
362
     * Indicates if a service is defined in the container.
363
     *
364
     * @param string $id
365
     *   The ID of the service to check.
366
     *
367
     * @return bool
368
     *   TRUE if the specified service exists, FALSE otherwise.
369
     */
370
    public static function hasService($id)
371
    {
372
        // Check hasContainer() first in order to always return a Boolean.
373
        return static::hasContainer() && static::getContainer()->has($id);
374
    }
375
376
    /**
377
     * Return the result printer object.
378
     *
379
     * @return \Robo\Log\ResultPrinter
380
     */
381
    public static function resultPrinter()
382
    {
383
        return static::service('resultPrinter');
384
    }
385
386
    /**
387
     * @return \Consolidation\Config\ConfigInterface
388
     */
389
    public static function config()
390
    {
391
        return static::service('config');
392
    }
393
394
    /**
395
     * @return \Consolidation\Log\Logger
396
     */
397
    public static function logger()
398
    {
399
        return static::service('logger');
400
    }
401
402
    /**
403
     * @return \Robo\Application
404
     */
405
    public static function application()
406
    {
407
        return static::service('application');
408
    }
409
410
    /**
411
     * Return the output object.
412
     *
413
     * @return \Symfony\Component\Console\Output\OutputInterface
414
     */
415
    public static function output()
416
    {
417
        return static::service('output');
418
    }
419
420
    /**
421
     * Return the input object.
422
     *
423
     * @return \Symfony\Component\Console\Input\InputInterface
424
     */
425
    public static function input()
426
    {
427
        return static::service('input');
428
    }
429
430
    /**
431
     * @return \Robo\Common\ProcessExecutor
432
     */
433
    public static function process(Process $process)
434
    {
435
        return ProcessExecutor::create(static::getContainer(), $process);
436
    }
437
}
438