ConsoleIO::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 0
cts 6
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 3
crap 2
1
<?php
2
3
namespace Webcreate\Conveyor\IO;
4
5
use Symfony\Component\Console\Helper\HelperSet;
6
use Symfony\Component\Console\Input\InputInterface;
7
use Symfony\Component\Console\Output\OutputInterface;
8
9
/**
10
 * The Input/Output helper.
11
 *
12
 * @author François Pluchino <[email protected]>
13
 * @author Jordi Boggiano <[email protected]>
14
 * @author Jeroen Fiege <[email protected]>
15
 */
16
class ConsoleIO implements IOInterface
17
{
18
    protected $input;
19
    protected $output;
20
    protected $helperSet;
21
    protected $authorizations = array();
22
    protected $lastMessage;
23
    protected $lastMessageNewline = true;
24
    protected $indention = 0;
25
    protected $prefix;
26
27
    /**
28
     * Constructor.
29
     *
30
     * @param InputInterface  $input     The input instance
31
     * @param OutputInterface $output    The output instance
32
     * @param HelperSet       $helperSet The helperSet instance
33
     */
34
    public function __construct(InputInterface $input, OutputInterface $output, HelperSet $helperSet)
35
    {
36
        $this->input = $input;
37
        $this->output = $output;
38
        $this->helperSet = $helperSet;
39
    }
40
41
    /**
42
     * {@inheritDoc}
43
     */
44
    public function isInteractive()
45
    {
46
        return $this->input->isInteractive();
47
    }
48
49
    /**
50
     * {@inheritDoc}
51
     */
52
    public function isDecorated()
53
    {
54
        return $this->output->isDecorated();
55
    }
56
57
    /**
58
     * {@inheritDoc}
59
     */
60
    public function isVerbose()
61
    {
62
        return (bool) $this->input->getOption('verbose');
63
    }
64
65
    /**
66
     * Set verbose option
67
     *
68
     * @param bool $option
69
     */
70
    public function setVerbose($option)
71
    {
72
        $this->input->setOption('verbose', (bool) $option);
73
    }
74
75
    /**
76
     * @param string|array $messages
77
     * @return string|array
78
     */
79
    protected function applyIndention($messages)
80
    {
81
        if (is_array($messages)) {
82
            foreach ($messages as &$message) {
83
                $message = str_repeat(' ', $this->indention) . $message;
84
            }
85
        } else {
86
            $messages = str_repeat(' ', $this->indention) . $messages;
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $messages. This often makes code more readable.
Loading history...
87
        }
88
89
        return $messages;
90
    }
91
92
    /**
93
     * @param string|array $messages
94
     * @return string|array
95
     */
96
    protected function applyPrefix($messages)
97
    {
98
        if (!$this->prefix || (!$this->lastMessageNewline && trim($messages) == '')) {
99
            return $messages;
100
        }
101
102
        if (is_array($messages)) {
103
            foreach ($messages as &$message) {
104
                $message = $this->prefix . $message;
105
            }
106
        } else {
107
            $messages = $this->prefix . $messages;
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $messages. This often makes code more readable.
Loading history...
108
        }
109
110
        return $messages;
111
    }
112
113
    /**
114
     * {@inheritDoc}
115
     */
116
    public function write($messages, $newline = true)
117
    {
118
        $messages = $this->applyIndention($messages);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $messages. This often makes code more readable.
Loading history...
119
        $messages = $this->applyPrefix($messages);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $messages. This often makes code more readable.
Loading history...
120
121
        $this->_write($messages, $newline);
122
    }
123
124
    /**
125
     * {@inheritDoc}
126
     */
127
    protected function _write($messages, $newline = true)
128
    {
129
        $this->output->write($messages, $newline);
130
        $this->lastMessage = join($newline ? PHP_EOL : '', (array) $messages);
131
        $this->lastMessageNewline = $newline;
132
    }
133
134
    /**
135
     * {@inheritDoc}
136
     */
137
    public function overwrite($messages, $newline = true, $size = null)
138
    {
139
        $messages = $this->applyIndention($messages);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $messages. This often makes code more readable.
Loading history...
140
        $messages = $this->applyPrefix($messages);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $messages. This often makes code more readable.
Loading history...
141
142
        // messages can be an array, let's convert it to string anyway
143
        $messages = join($newline ? PHP_EOL : '', (array) $messages);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $messages. This often makes code more readable.
Loading history...
144
145
        // since overwrite is supposed to overwrite last message...
146
        if (!isset($size)) {
147
            // removing possible formatting of lastMessage with strip_tags
148
            $size = strlen(strip_tags($this->lastMessage));
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $size. This often makes code more readable.
Loading history...
149
            $size+= $this->indention;
150
            if ($this->prefix) {
151
                $size += strlen($this->prefix);
152
            }
153
        }
154
        // ...let's fill its length with backspaces
155
        $this->_write(str_repeat("\x08", $size), false);
156
157
        // write the new message
158
        $this->_write($messages, false);
159
160
        $fill = $size - strlen(strip_tags($messages));
161
        if ($fill > 0) {
162
            // whitespace whatever has left
163
            $this->_write(str_repeat(' ', $fill), false);
164
            // move the cursor back
165
            $this->_write(str_repeat("\x08", $fill), false);
166
        }
167
168
        if ($newline) {
169
            $this->_write('');
170
        }
171
        $this->lastMessage = $messages;
172
        $this->lastMessageNewline = $newline;
173
    }
174
175
    /**
176
     * {@inheritDoc}
177
     */
178 View Code Duplication
    public function select($question, $choices, $default = null, $attempts = false, $errorMessage = 'Value "%s" is invalid')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
179
    {
180
        if ($this->isInteractive()) {
181
            return $this->helperSet->get('dialog')->select($this->output, $question, $choices, $default, $attempts, $errorMessage);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Helper\HelperInterface as the method select() does only exist in the following implementations of said interface: Symfony\Component\Console\Helper\DialogHelper.

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...
182
        } else {
183
            return $default;
0 ignored issues
show
Bug Compatibility introduced by
The expression return $default; of type boolean|null is incompatible with the return type declared by the interface Webcreate\Conveyor\IO\IOInterface::select of type integer|string as it can also be of type boolean which is not included in this return type.
Loading history...
184
        }
185
    }
186
187
    /**
188
     * {@inheritDoc}
189
     */
190 View Code Duplication
    public function ask($question, $default = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
191
    {
192
        if ($this->isInteractive()) {
193
            return $this->helperSet->get('dialog')->ask($this->output, $question, $default);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Helper\HelperInterface as the method ask() does only exist in the following implementations of said interface: Symfony\Component\Console\Helper\DialogHelper, Symfony\Component\Console\Helper\QuestionHelper.

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...
194
        } else {
195
            return $default;
196
        }
197
    }
198
199
    /**
200
     * {@inheritDoc}
201
     */
202 View Code Duplication
    public function askConfirmation($question, $default = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
203
    {
204
        if ($this->isInteractive()) {
205
            return $this->helperSet->get('dialog')->askConfirmation($this->output, $question, $default);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Helper\HelperInterface as the method askConfirmation() does only exist in the following implementations of said interface: Symfony\Component\Console\Helper\DialogHelper.

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...
206
        } else {
207
            return $default;
208
        }
209
    }
210
211
    /**
212
     * {@inheritDoc}
213
     */
214 View Code Duplication
    public function askAndValidate($question, $validator, $attempts = false, $default = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
215
    {
216
        if ($this->isInteractive()) {
217
            return $this->helperSet->get('dialog')->askAndValidate($this->output, $question, $validator, $attempts, $default);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Helper\HelperInterface as the method askAndValidate() does only exist in the following implementations of said interface: Symfony\Component\Console\Helper\DialogHelper.

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
        } else {
219
            return $default;
220
        }
221
    }
222
223
    /**
224
     * {@inheritDoc}
225
     */
226
    public function askAndHideAnswer($question)
227
    {
228
        // handle windows
229
        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
230
            $exe = __DIR__.'\\hiddeninput.exe';
231
232
            // handle code running from a phar
233
            if ('phar:' === substr(__FILE__, 0, 5)) {
234
                $tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
235
                copy($exe, $tmpExe);
236
                $exe = $tmpExe;
237
            }
238
239
            $this->write($question, false);
240
            $value = rtrim(shell_exec($exe));
241
            $this->write('');
242
243
            // clean up
244
            if (isset($tmpExe)) {
245
                unlink($tmpExe);
246
            }
247
248
            return $value;
249
        }
250
251
        if (file_exists('/usr/bin/env')) {
252
            // handle other OSs with bash/zsh/ksh/csh if available to hide the answer
253
            $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
254
            foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) {
255
                if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
256
                    $shell = $sh;
257
                    break;
258
                }
259
            }
260
            if (isset($shell)) {
261
                $this->write($question, false);
262
                $readCmd = ($shell === 'csh') ? 'set mypassword = $<' : 'read -r mypassword';
263
                $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
264
                $value = rtrim(shell_exec($command));
265
                $this->write('');
266
267
                return $value;
268
            }
269
        }
270
271
        // not able to hide the answer, proceed with normal question handling
272
        return $this->ask($question);
273
    }
274
275
    /**
276
     * (non-PHPdoc)
277
     * @see Webcreate\Conveyor\IO.IOInterface::setIndention()
278
     */
279
    public function setIndention($indent)
280
    {
281
        $this->indention = $indent;
282
    }
283
284
    /**
285
     * (non-PHPdoc)
286
     * @see Webcreate\Conveyor\IO.IOInterface::increaseIndention()
287
     */
288
    public function increaseIndention($indent)
289
    {
290
        $this->indention += $indent;
291
    }
292
293
    /**
294
     * (non-PHPdoc)
295
     * @see Webcreate\Conveyor\IO.IOInterface::decreaseIndention()
296
     */
297
    public function decreaseIndention($indent)
298
    {
299
        if ($this->indention - $indent < 0) {
300
            throw new \InvalidArgumentException('Resulting indenting should be greater or equal to 0.');
301
        }
302
303
        $this->indention -= $indent;
304
    }
305
306
    /**
307
     * (non-PHPdoc)
308
     * @see Webcreate\Conveyor\IO.IOInterface::getIndention()
309
     */
310
    public function getIndention()
311
    {
312
        return $this->indention;
313
    }
314
315
    public function renderException($e)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $e. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
316
    {
317
        $indention = $this->getIndention();
318
319
        $this->setIndention(0);
320
321
        $app = new \Symfony\Component\Console\Application();
322
        $app->renderException($e, $this->output);
323
324
        // restore indention
325
        $this->setIndention($indention);
326
    }
327
328
    /**
329
     * @param string $prefix
330
     * @return mixed
331
     */
332
    public function setPrefix($prefix)
333
    {
334
        $this->prefix = $prefix;
335
    }
336
}
337