Completed
Push — master ( db3cbe...3a00c0 )
by Greg
02:10
created

Robo::configureApplication()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
cc 3
nc 4
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 = '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
162
        // Set the application dispatcher
163
        $app->setDispatcher($container->get('eventDispatcher'));
164
165
        return $container;
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
     * $dispatcher = $container->get('eventDispatcher');
173
     * $app->setDispatcher($dispatcher);
174
     *
175
     * Any modification to the container should be done prior to fetching
176
     * objects from it.
177
     *
178
     * It is recommended to use \Robo::createDefaultContainer()
179
     * instead, which does all required setup for the caller, but has
180
     * the limitation that the container it creates can only be
181
     * extended, not modified.
182
     *
183
     * @param \League\Container\ContainerInterface $container
184
     * @param \Symfony\Component\Console\Application $app
185
     * @param \Consolidation\Config\ConfigInterface $config
186
     * @param null|\Symfony\Component\Console\Input\InputInterface $input
187
     * @param null|\Symfony\Component\Console\Output\OutputInterface $output
188
     * @param null|\Composer\Autoload\ClassLoader $classLoader
189
     */
190
    public static function configureContainer(ContainerInterface $container, SymfonyApplication $app, ConfigInterface $config, $input = null, $output = null, $classLoader = null)
191
    {
192
        // Self-referential container refernce for the inflector
193
        $container->add('container', $container);
194
        static::setContainer($container);
195
196
        // Create default input and output objects if they were not provided
197
        if (!$input) {
198
            $input = new StringInput('');
199
        }
200
        if (!$output) {
201
            $output = new \Symfony\Component\Console\Output\ConsoleOutput();
202
        }
203
        if (!$classLoader) {
204
            $classLoader = new ClassLoader();
205
        }
206
        $config->set(Config::DECORATED, $output->isDecorated());
207
        $config->set(Config::INTERACTIVE, $input->isInteractive());
208
209
        $container->share('application', $app);
210
        $container->share('config', $config);
211
        $container->share('input', $input);
212
        $container->share('output', $output);
213
        $container->share('outputAdapter', \Robo\Common\OutputAdapter::class);
214
        $container->share('classLoader', $classLoader);
215
216
        // Register logging and related services.
217
        $container->share('logStyler', \Robo\Log\RoboLogStyle::class);
218
        $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...
219
            ->withArgument('output')
220
            ->withMethodCall('setLogOutputStyler', ['logStyler']);
221
        $container->add('progressBar', \Symfony\Component\Console\Helper\ProgressBar::class)
222
            ->withArgument('output');
223
        $container->share('progressIndicator', \Robo\Common\ProgressIndicator::class)
224
            ->withArgument('progressBar')
225
            ->withArgument('output');
226
        $container->share('resultPrinter', \Robo\Log\ResultPrinter::class);
227
        $container->add('simulator', \Robo\Task\Simulator::class);
228
        $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...
229
            ->withMethodCall('setApplication', ['application']);
230
        $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...
231
            ->withArgument('config')
232
            ->withMethodCall('setApplication', ['application']);
233
        $container->share('collectionProcessHook', \Robo\Collection\CollectionProcessHook::class);
234
        $container->share('alterOptionsCommandEvent', \Consolidation\AnnotatedCommand\Options\AlterOptionsCommandEvent::class)
235
            ->withArgument('application');
236
        $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...
237
            ->withMethodCall('addCommandEvent', ['alterOptionsCommandEvent'])
238
            ->withMethodCall('addCommandEvent', ['injectConfigEventListener'])
239
            ->withMethodCall('addCommandEvent', ['globalOptionsEventListener'])
240
            ->withMethodCall('addResultProcessor', ['collectionProcessHook', '*']);
241
        $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...
242
            ->withMethodCall('addSubscriber', ['hookManager']);
243
        $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...
244
            ->withMethodCall('addDefaultFormatters', [])
245
            ->withMethodCall('addDefaultSimplifiers', []);
246
        $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...
247
            ->withMethodCall('setApplication', ['application']);
248
        $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...
249
            ->withArgument('hookManager')
250
            ->withMethodCall('setFormatterManager', ['formatterManager'])
251
            ->withMethodCall('addPrepareFormatter', ['prepareTerminalWidthOption'])
252
            ->withMethodCall(
253
                'setDisplayErrorFunction',
254
                [
255
                    function ($output, $message) use ($container) {
256
                        $logger = $container->get('logger');
257
                        $logger->error($message);
258
                    }
259
                ]
260
            );
261
        $container->share('stdinHandler', \Consolidation\AnnotatedCommand\Input\StdinHandler::class);
262
        $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...
263
            ->withMethodCall('setCommandProcessor', ['commandProcessor']);
264
        $container->share('relativeNamespaceDiscovery', \Robo\ClassDiscovery\RelativeNamespaceDiscovery::class)
265
            ->withArgument('classLoader');
266
267
        // Deprecated: favor using collection builders to direct use of collections.
268
        $container->add('collection', \Robo\Collection\Collection::class);
269
        // Deprecated: use CollectionBuilder::create() instead -- or, better
270
        // yet, BuilderAwareInterface::collectionBuilder() if available.
271
        $container->add('collectionBuilder', \Robo\Collection\CollectionBuilder::class);
272
273
        static::addInflectors($container);
274
275
        // Make sure the application is appropriately initialized.
276
        $app->setAutoExit(false);
277
    }
278
279
    /**
280
     * @param null|string $appName
281
     * @param null|string $appVersion
282
     *
283
     * @return \Robo\Application
284
     */
285
    public static function createDefaultApplication($appName = null, $appVersion = null)
286
    {
287
        $appName = $appName ?: self::APPLICATION_NAME;
288
        $appVersion = $appVersion ?: self::VERSION;
289
290
        $app = new \Robo\Application($appName, $appVersion);
291
        $app->setAutoExit(false);
292
        return $app;
293
    }
294
295
    /**
296
     * Add the Robo League\Container inflectors to the container
297
     *
298
     * @param \League\Container\ContainerInterface $container
299
     */
300
    public static function addInflectors($container)
301
    {
302
        // Register our various inflectors.
303
        $container->inflector(\Robo\Contract\ConfigAwareInterface::class)
304
            ->invokeMethod('setConfig', ['config']);
305
        $container->inflector(\Psr\Log\LoggerAwareInterface::class)
306
            ->invokeMethod('setLogger', ['logger']);
307
        $container->inflector(\League\Container\ContainerAwareInterface::class)
308
            ->invokeMethod('setContainer', ['container']);
309
        $container->inflector(\Symfony\Component\Console\Input\InputAwareInterface::class)
310
            ->invokeMethod('setInput', ['input']);
311
        $container->inflector(\Robo\Contract\OutputAwareInterface::class)
312
            ->invokeMethod('setOutput', ['output']);
313
        $container->inflector(\Robo\Contract\ProgressIndicatorAwareInterface::class)
314
            ->invokeMethod('setProgressIndicator', ['progressIndicator']);
315
        $container->inflector(\Consolidation\AnnotatedCommand\Events\CustomEventAwareInterface::class)
316
            ->invokeMethod('setHookManager', ['hookManager']);
317
        $container->inflector(\Robo\Contract\VerbosityThresholdInterface::class)
318
            ->invokeMethod('setOutputAdapter', ['outputAdapter']);
319
        $container->inflector(\Consolidation\AnnotatedCommand\Input\StdinAwareInterface::class)
320
            ->invokeMethod('setStdinHandler', ['stdinHandler']);
321
    }
322
323
    /**
324
     * Retrieves a service from the container.
325
     *
326
     * Use this method if the desired service is not one of those with a dedicated
327
     * accessor method below. If it is listed below, those methods are preferred
328
     * as they can return useful type hints.
329
     *
330
     * @param string $id
331
     *   The ID of the service to retrieve.
332
     *
333
     * @return mixed
334
     *   The specified service.
335
     */
336
    public static function service($id)
337
    {
338
        return static::getContainer()->get($id);
339
    }
340
341
    /**
342
     * Indicates if a service is defined in the container.
343
     *
344
     * @param string $id
345
     *   The ID of the service to check.
346
     *
347
     * @return bool
348
     *   TRUE if the specified service exists, FALSE otherwise.
349
     */
350
    public static function hasService($id)
351
    {
352
        // Check hasContainer() first in order to always return a Boolean.
353
        return static::hasContainer() && static::getContainer()->has($id);
354
    }
355
356
    /**
357
     * Return the result printer object.
358
     *
359
     * @return \Robo\Log\ResultPrinter
360
     */
361
    public static function resultPrinter()
362
    {
363
        return static::service('resultPrinter');
364
    }
365
366
    /**
367
     * @return \Consolidation\Config\ConfigInterface
368
     */
369
    public static function config()
370
    {
371
        return static::service('config');
372
    }
373
374
    /**
375
     * @return \Consolidation\Log\Logger
376
     */
377
    public static function logger()
378
    {
379
        return static::service('logger');
380
    }
381
382
    /**
383
     * @return \Robo\Application
384
     */
385
    public static function application()
386
    {
387
        return static::service('application');
388
    }
389
390
    /**
391
     * Return the output object.
392
     *
393
     * @return \Symfony\Component\Console\Output\OutputInterface
394
     */
395
    public static function output()
396
    {
397
        return static::service('output');
398
    }
399
400
    /**
401
     * Return the input object.
402
     *
403
     * @return \Symfony\Component\Console\Input\InputInterface
404
     */
405
    public static function input()
406
    {
407
        return static::service('input');
408
    }
409
410
    /**
411
     * @return \Robo\Common\ProcessExecutor
412
     */
413
    public static function process(Process $process)
414
    {
415
        return ProcessExecutor::create(static::getContainer(), $process);
416
    }
417
}
418