Completed
Pull Request — master (#822)
by Greg
02:22
created

Robo::configureApplication()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 2
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 = '1.3.5-dev';
24
25
    /**
26
     * The currently active container object, or NULL if not initialized yet.
27
     *
28
     * @var 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
     *
41
     * @return int
42
     */
43
    public static function run($argv, $commandClasses, $appName = null, $appVersion = null, $output = null, $repository = null)
44
    {
45
        $runner = new \Robo\Runner($commandClasses);
46
        $runner->setSelfUpdateRepository($repository);
47
        $statusCode = $runner->execute($argv, $appName, $appVersion, $output);
48
        return $statusCode;
49
    }
50
51
    /**
52
     * Sets a new global container.
53
     *
54
     * @param ContainerInterface $container
55
     *   A new container instance to replace the current.
56
     */
57
    public static function setContainer(ContainerInterface $container)
58
    {
59
        static::$container = $container;
60
    }
61
62
    /**
63
     * Unsets the global container.
64
     */
65
    public static function unsetContainer()
66
    {
67
        static::$container = null;
68
    }
69
70
    /**
71
     * Returns the currently active global container.
72
     *
73
     * @return \League\Container\ContainerInterface
74
     *
75
     * @throws \RuntimeException
76
     */
77
    public static function getContainer()
78
    {
79
        if (static::$container === null) {
80
            throw new \RuntimeException('container is not initialized yet. \Robo\Robo::setContainer() must be called with a real container.');
81
        }
82
        return static::$container;
83
    }
84
85
    /**
86
     * Returns TRUE if the container has been initialized, FALSE otherwise.
87
     *
88
     * @return bool
89
     */
90
    public static function hasContainer()
91
    {
92
        return static::$container !== null;
93
    }
94
95
    /**
96
     * Create a config object and load it from the provided paths.
97
     */
98
    public static function createConfiguration($paths)
99
    {
100
        $config = new \Robo\Config\Config();
101
        static::loadConfiguration($paths, $config);
102
        return $config;
103
    }
104
105
    /**
106
     * Use a simple config loader to load configuration values from specified paths
107
     */
108
    public static function loadConfiguration($paths, $config = null)
109
    {
110
        if ($config == null) {
111
            $config = static::config();
112
        }
113
        $loader = new YamlConfigLoader();
114
        $processor = new ConfigProcessor();
115
        $processor->add($config->export());
116
        foreach ($paths as $path) {
117
            $processor->extend($loader->load($path));
118
        }
119
        $config->import($processor->export());
120
    }
121
122
    /**
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.
126
     *
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
132
     *
133
     * @return \League\Container\Container|\League\Container\ContainerInterface
134
     */
135
    public static function createDefaultContainer($input = null, $output = null, $app = null, $config = null, $classLoader = null)
136
    {
137
        // Do not allow this function to be called more than once.
138
        if (static::hasContainer()) {
139
            return static::getContainer();
140
        }
141
142
        if (!$app) {
143
            $app = static::createDefaultApplication();
144
        }
145
146
        if (!$config) {
147
            $config = new \Robo\Config\Config();
148
        }
149
150
        // Set up our dependency injection container.
151
        $container = new Container();
152
        static::configureContainer($container, $app, $config, $input, $output, $classLoader);
153
        static::configureApplication($app, $container);
154
155
        return $container;
156
    }
157
158
    public static function configureApplication($app, $container)
159
    {
160
        // Set the application dispatcher
161
        $app->setDispatcher($container->get('eventDispatcher'));
162
163
        if ($app instanceof \Robo\Contract\IOAwareInterface) {
164
            $app->setIOStorage($container->get('ioStorage'));
165
        }
166
    }
167
168
    /**
169
     * Initialize a container with all of the default Robo services.
170
     * IMPORTANT:  after calling this method, clients MUST call:
171
     *
172
     *   \Robo\Robo::configureApplication($app, $container);
173
     *
174
     * Any modification to the container should be done prior to fetching
175
     * objects from it.
176
     *
177
     * It is recommended to use \Robo::createDefaultContainer()
178
     * instead, which does all required setup for the caller, but has
179
     * the limitation that the container it creates can only be
180
     * extended, not modified.
181
     *
182
     * @param \League\Container\ContainerInterface $container
183
     * @param \Symfony\Component\Console\Application $app
184
     * @param ConfigInterface $config
185
     * @param null|\Symfony\Component\Console\Input\InputInterface $input
186
     * @param null|\Symfony\Component\Console\Output\OutputInterface $output
187
     * @param null|\Composer\Autoload\ClassLoader $classLoader
188
     */
189
    public static function configureContainer(ContainerInterface $container, SymfonyApplication $app, ConfigInterface $config, $input = null, $output = null, $classLoader = null)
190
    {
191
        // Self-referential container refernce for the inflector
192
        $container->add('container', $container);
193
        static::setContainer($container);
194
195
        // Create default input and output objects if they were not provided
196
        if (!$input) {
197
            $input = new StringInput('');
198
        }
199
        if (!$output) {
200
            $output = new \Symfony\Component\Console\Output\ConsoleOutput();
201
        }
202
        if (!$classLoader) {
203
            $classLoader = new ClassLoader();
204
        }
205
        $config->set(Config::DECORATED, $output->isDecorated());
206
        $config->set(Config::INTERACTIVE, $input->isInteractive());
207
208
        $container->share('application', $app);
209
        $container->share('config', $config);
210
        $container->share('input', $input);
211
        $container->share('output', $output);
212
        $container->share('outputAdapter', \Robo\Common\OutputAdapter::class);
213
        $container->share('classLoader', $classLoader);
214
215
        // Register logging and related services.
216
        $container->share('logStyler', \Robo\Log\RoboLogStyle::class);
217
        $container->share('logger', \Robo\Log\RoboLogger::class)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface League\Container\Definition\DefinitionInterface as the method withMethodCall() does only exist in the following implementations of said interface: League\Container\Definition\ClassDefinition.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
218
            ->withArgument('output')
219
            ->withMethodCall('setLogOutputStyler', ['logStyler']);
220
        $container->add('progressBar', \Symfony\Component\Console\Helper\ProgressBar::class)
221
            ->withArgument('output');
222
        $container->share('progressIndicator', \Robo\Common\ProgressIndicator::class)
223
            ->withArgument('progressBar')
224
            ->withArgument('output');
225
        $container->share('resultPrinter', \Robo\Log\ResultPrinter::class);
226
        $container->add('simulator', \Robo\Task\Simulator::class);
227
        $container->share('globalOptionsEventListener', \Robo\GlobalOptionsEventListener::class)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface League\Container\Definition\DefinitionInterface as the method withMethodCall() does only exist in the following implementations of said interface: League\Container\Definition\ClassDefinition.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
228
            ->withMethodCall('setApplication', ['application']);
229
        $container->share('injectConfigEventListener', \Consolidation\Config\Inject\ConfigForCommand::class)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface League\Container\Definition\DefinitionInterface as the method withMethodCall() does only exist in the following implementations of said interface: League\Container\Definition\ClassDefinition.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
230
            ->withArgument('config')
231
            ->withMethodCall('setApplication', ['application']);
232
        $container->share('collectionProcessHook', \Robo\Collection\CollectionProcessHook::class);
233
        $container->share('alterOptionsCommandEvent', \Consolidation\AnnotatedCommand\Options\AlterOptionsCommandEvent::class)
234
            ->withArgument('application');
235
        $container->share('hookManager', \Consolidation\AnnotatedCommand\Hooks\HookManager::class)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface League\Container\Definition\DefinitionInterface as the method withMethodCall() does only exist in the following implementations of said interface: League\Container\Definition\ClassDefinition.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
236
            ->withMethodCall('addCommandEvent', ['alterOptionsCommandEvent'])
237
            ->withMethodCall('addCommandEvent', ['injectConfigEventListener'])
238
            ->withMethodCall('addCommandEvent', ['globalOptionsEventListener'])
239
            ->withMethodCall('addResultProcessor', ['collectionProcessHook', '*']);
240
        $container->share('eventDispatcher', \Symfony\Component\EventDispatcher\EventDispatcher::class)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface League\Container\Definition\DefinitionInterface as the method withMethodCall() does only exist in the following implementations of said interface: League\Container\Definition\ClassDefinition.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
241
            ->withMethodCall('addSubscriber', ['hookManager']);
242
        $container->share('formatterManager', \Consolidation\OutputFormatters\FormatterManager::class)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface League\Container\Definition\DefinitionInterface as the method withMethodCall() does only exist in the following implementations of said interface: League\Container\Definition\ClassDefinition.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
243
            ->withMethodCall('addDefaultFormatters', [])
244
            ->withMethodCall('addDefaultSimplifiers', []);
245
        $container->share('prepareTerminalWidthOption', \Consolidation\AnnotatedCommand\Options\PrepareTerminalWidthOption::class)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface League\Container\Definition\DefinitionInterface as the method withMethodCall() does only exist in the following implementations of said interface: League\Container\Definition\ClassDefinition.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
246
            ->withMethodCall('setApplication', ['application']);
247
        $container->share('symfonyStyleInjector', \Robo\Symfony\SymfonyStyleInjector::class);
248
        $container->share('parameterInjection', \Consolidation\AnnotatedCommand\ParameterInjection::class)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface League\Container\Definition\DefinitionInterface as the method withMethodCall() does only exist in the following implementations of said interface: League\Container\Definition\ClassDefinition.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
249
            ->withMethodCall('register', ['Symfony\Component\Console\Style\SymfonyStyle', 'symfonyStyleInjector']);
250
        $container->share('commandProcessor', \Consolidation\AnnotatedCommand\CommandProcessor::class)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface League\Container\Definition\DefinitionInterface as the method withMethodCall() does only exist in the following implementations of said interface: League\Container\Definition\ClassDefinition.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
251
            ->withArgument('hookManager')
252
            ->withMethodCall('setFormatterManager', ['formatterManager'])
253
            ->withMethodCall('addPrepareFormatter', ['prepareTerminalWidthOption'])
254
            ->withMethodCall('setParameterInjection', ['parameterInjection'])
255
            ->withMethodCall(
256
                'setDisplayErrorFunction',
257
                [
258
                    function ($output, $message) use ($container) {
259
                        $logger = $container->get('logger');
260
                        $logger->error($message);
261
                    }
262
                ]
263
            );
264
        $container->share('ioStorage', \Robo\Symfony\IOStorage::class);
265
        $container->share('stdinHandler', \Consolidation\AnnotatedCommand\Input\StdinHandler::class);
266
        $container->share('commandFactory', \Consolidation\AnnotatedCommand\AnnotatedCommandFactory::class)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface League\Container\Definition\DefinitionInterface as the method withMethodCall() does only exist in the following implementations of said interface: League\Container\Definition\ClassDefinition.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
267
            ->withMethodCall('setCommandProcessor', ['commandProcessor']);
268
        $container->share('relativeNamespaceDiscovery', \Robo\ClassDiscovery\RelativeNamespaceDiscovery::class)
269
            ->withArgument('classLoader');
270
271
        // Deprecated: favor using collection builders to direct use of collections.
272
        $container->add('collection', \Robo\Collection\Collection::class);
273
        // Deprecated: use CollectionBuilder::create() instead -- or, better
274
        // yet, BuilderAwareInterface::collectionBuilder() if available.
275
        $container->add('collectionBuilder', \Robo\Collection\CollectionBuilder::class);
276
277
        static::addInflectors($container);
278
279
        // Make sure the application is appropriately initialized.
280
        $app->setAutoExit(false);
281
    }
282
283
    /**
284
     * @param null|string $appName
285
     * @param null|string $appVersion
286
     *
287
     * @return \Robo\Application
288
     */
289
    public static function createDefaultApplication($appName = null, $appVersion = null)
290
    {
291
        $appName = $appName ?: self::APPLICATION_NAME;
292
        $appVersion = $appVersion ?: self::VERSION;
293
294
        $app = new \Robo\Application($appName, $appVersion);
295
        $app->setAutoExit(false);
296
        return $app;
297
    }
298
299
    /**
300
     * Add the Robo League\Container inflectors to the container
301
     *
302
     * @param \League\Container\ContainerInterface $container
303
     */
304
    public static function addInflectors($container)
305
    {
306
        // Register our various inflectors.
307
        $container->inflector(\Robo\Contract\ConfigAwareInterface::class)
308
            ->invokeMethod('setConfig', ['config']);
309
        $container->inflector(\Psr\Log\LoggerAwareInterface::class)
310
            ->invokeMethod('setLogger', ['logger']);
311
        $container->inflector(\League\Container\ContainerAwareInterface::class)
312
            ->invokeMethod('setContainer', ['container']);
313
        $container->inflector(\Robo\Symfony\IOAwareInterface::class)
314
            ->invokeMethod('setIOStorage', ['ioStorage']);
315
        $container->inflector(\Symfony\Component\Console\Input\InputAwareInterface::class)
316
            ->invokeMethod('setInput', ['input']);
317
        $container->inflector(\Robo\Contract\OutputAwareInterface::class)
318
            ->invokeMethod('setOutput', ['output']);
319
        $container->inflector(\Robo\Contract\ProgressIndicatorAwareInterface::class)
320
            ->invokeMethod('setProgressIndicator', ['progressIndicator']);
321
        $container->inflector(\Consolidation\AnnotatedCommand\Events\CustomEventAwareInterface::class)
322
            ->invokeMethod('setHookManager', ['hookManager']);
323
        $container->inflector(\Robo\Contract\VerbosityThresholdInterface::class)
324
            ->invokeMethod('setOutputAdapter', ['outputAdapter']);
325
        $container->inflector(\Consolidation\AnnotatedCommand\Input\StdinAwareInterface::class)
326
            ->invokeMethod('setStdinHandler', ['stdinHandler']);
327
    }
328
329
    /**
330
     * Retrieves a service from the container.
331
     *
332
     * Use this method if the desired service is not one of those with a dedicated
333
     * accessor method below. If it is listed below, those methods are preferred
334
     * as they can return useful type hints.
335
     *
336
     * @param string $id
337
     *   The ID of the service to retrieve.
338
     *
339
     * @return mixed
340
     *   The specified service.
341
     */
342
    public static function service($id)
343
    {
344
        return static::getContainer()->get($id);
345
    }
346
347
    /**
348
     * Indicates if a service is defined in the container.
349
     *
350
     * @param string $id
351
     *   The ID of the service to check.
352
     *
353
     * @return bool
354
     *   TRUE if the specified service exists, FALSE otherwise.
355
     */
356
    public static function hasService($id)
357
    {
358
        // Check hasContainer() first in order to always return a Boolean.
359
        return static::hasContainer() && static::getContainer()->has($id);
360
    }
361
362
    /**
363
     * Return the result printer object.
364
     *
365
     * @return \Robo\Log\ResultPrinter
366
     */
367
    public static function resultPrinter()
368
    {
369
        return static::service('resultPrinter');
370
    }
371
372
    /**
373
     * @return ConfigInterface
374
     */
375
    public static function config()
376
    {
377
        return static::service('config');
378
    }
379
380
    /**
381
     * @return \Consolidation\Log\Logger
382
     */
383
    public static function logger()
384
    {
385
        return static::service('logger');
386
    }
387
388
    /**
389
     * @return \Robo\Application
390
     */
391
    public static function application()
392
    {
393
        return static::service('application');
394
    }
395
396
    /**
397
     * Return the output object.
398
     *
399
     * @return \Symfony\Component\Console\Output\OutputInterface
400
     */
401
    public static function output()
402
    {
403
        return static::service('output');
404
    }
405
406
    /**
407
     * Return the input object.
408
     *
409
     * @return \Symfony\Component\Console\Input\InputInterface
410
     */
411
    public static function input()
412
    {
413
        return static::service('input');
414
    }
415
416
    public static function process(Process $process)
417
    {
418
        return ProcessExecutor::create(static::getContainer(), $process);
419
    }
420
}
421