CredentialsGatherer::askQuestions()   C
last analyzed

Complexity

Conditions 8
Paths 12

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 8.0747

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 29
rs 5.3846
ccs 17
cts 19
cp 0.8947
cc 8
eloc 17
nc 12
nop 2
crap 8.0747
1
<?php
2
3
/*
4
 * This file is part of Rocketeer
5
 *
6
 * (c) Maxime Fabre <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 */
12
13
namespace Rocketeer\Services\Connections\Credentials;
14
15
use Closure;
16
use Rocketeer\Services\Connections\Credentials\Keys\ConnectionKey;
17
use Rocketeer\Services\Connections\Credentials\Keys\RepositoryKey;
18
use Rocketeer\Traits\ContainerAwareTrait;
19
20
/**
21
 * Collects the needed credentials from the user.
22
 */
23
class CredentialsGatherer
24
{
25
    use ContainerAwareTrait;
26
27
    /**
28
     * @var array
29
     */
30
    protected $connections = [];
31
32
    /**
33
     * @return array
34
     */
35 3
    public function getCredentials()
36
    {
37 3
        $credentials = $this->getRepositoryCredentials();
38 3
        $connections = $this->getConnectionsCredentials();
39 3
        foreach ($connections as $connection) {
40 3
            $credentials = array_merge($credentials, $connection);
41 3
        }
42
43 3
        return $credentials;
44
    }
45
46
    /**
47
     * Get the Repository's credentials.
48
     *
49
     * @return array
50
     */
51 5
    public function getRepositoryCredentials()
52
    {
53 5
        $endpoint = $this->vcs->runLocally('currentEndpoint');
0 ignored issues
show
Bug introduced by Maxime Fabre
The method runLocally() does not seem to exist on object<Rocketeer\Binaries\Vcs\VcsInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
54 5
        $user = $this->bash->runLocally('whoami');
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method runLocally does not exist on object<Rocketeer\Services\Connections\Shell\Bash>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
55
56 5
        return $this->askQuestions('vcs', [
57 5
            'repository' => ['Where is your code located?', $endpoint],
58 5
            'username' => ['What is the username for it?', $user],
59 5
            'password' => 'And the password?',
60 5
        ]);
61
    }
62
63
    /**
64
     * Get the credentials of all connections.
65
     *
66
     * @return array
67
     */
68 5
    public function getConnectionsCredentials()
69
    {
70 5
        $connectionName = null;
71 5
        if ($this->connections) {
72 5
            $this->presentConnections($this->connections);
73 5
            if ($this->command->confirm('Do you want to add a connection to this?', false)) {
0 ignored issues
show
Documentation Bug introduced by Maxime FABRE
The method confirm does not exist on object<Rocketeer\Console...mmands\AbstractCommand>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
74
                $connectionName = $this->command->ask('What do you want to name it?');
0 ignored issues
show
Documentation Bug introduced by Maxime FABRE
The method ask does not exist on object<Rocketeer\Console...mmands\AbstractCommand>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
75
            }
76 5
        } else {
77 5
            $connectionName = $this->command->ask('No connections have been set, let\'s create one, what do you want to name it?', 'production');
0 ignored issues
show
Documentation Bug introduced by Maxime FABRE
The method ask does not exist on object<Rocketeer\Console...mmands\AbstractCommand>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
78
        }
79
80
        // If the user does not want to add any more connection
81
        // then we can quit
82 5
        if (!$connectionName) {
83 5
            return $this->connections;
84
        }
85
86 5
        $this->getConnectionCredentials($connectionName);
87
88 5
        return $this->getConnectionsCredentials();
89
    }
90
91
    /**
92
     * Get the credentials of a connection.
93
     *
94
     * @param string $connectionName
95
     */
96 5
    public function getConnectionCredentials($connectionName)
97
    {
98 5
        $user = $this->bash->runLocally('whoami');
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method runLocally does not exist on object<Rocketeer\Services\Connections\Shell\Bash>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
99
100 5
        $usesPrivateKey = $this->command->confirm('Do you use an SSH key to connect to it?');
0 ignored issues
show
Documentation Bug introduced by Maxime FABRE
The method confirm does not exist on object<Rocketeer\Console...mmands\AbstractCommand>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
101 5
        $questions = $usesPrivateKey ? [
102 4
            'key' => ['Where can I find your key?', $this->paths->getDefaultKeyPath()],
0 ignored issues
show
Documentation Bug introduced by Maxime FABRE
The method getDefaultKeyPath does not exist on object<Rocketeer\Services\Environment\Pathfinder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
103 4
            'keyphrase' => 'If it needs a passphrase enter it',
104 4
            'host' => 'Where is your server located? <comment>(eg. foobar.com)</comment>',
105 4
            'username' => ['What is the username for it?', $user],
106 4
            'root_directory' => ['Where do you want your application deployed?', '/home/www/'],
107 4
        ] : [
108 1
            'host' => 'Where is your server located?',
109 1
            'username' => ['What is the username for it?', $user],
110 1
            'password' => 'And password?',
111 1
            'root_directory' => ['Where do you want your application deployed?', '/home/www/'],
112 5
        ];
113
114 5
        $this->connections[$connectionName] = $this->askQuestions($connectionName, $questions);
115 5
    }
116
117
    ////////////////////////////////////////////////////////////////////////////////
118
    /////////////////////////////////// HELPERS ////////////////////////////////////
119
    ////////////////////////////////////////////////////////////////////////////////
120
121
    /**
122
     * @param string $for
123
     * @param array  $questions
124
     *
125
     * @return array
126
     */
127 7
    protected function askQuestions($for, array $questions)
128
    {
129 7
        $key = $for === 'vcs' ? new RepositoryKey() : new ConnectionKey(['server' => 0]);
130 7
        $credentials = [];
131 7
        $config = [];
132
133 7
        foreach ($questions as $credential => $question) {
134 7
            $answer = $this->askQuestion($for, $credential, $question);
135 7
            $key->$credential = $answer;
136
137
            // Store credential
138 7
            $constant = $this->getCredentialConstant($for, $credential);
139 7
            $credentials[$constant] = $answer;
140 7
            $config[$credential] = '%%'.$constant.'%%';
141
142
            // Special cases
143 7
            if ($credential === 'repository' && !$key->needsCredentials()) {
0 ignored issues
show
Bug introduced by Maxime Fabre
The method needsCredentials does only exist in Rocketeer\Services\Conne...ials\Keys\RepositoryKey, but not in Rocketeer\Services\Conne...ials\Keys\ConnectionKey.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
144 4
                break;
145 6
            } elseif ($credential === 'host' && $key->isFtp()) {
0 ignored issues
show
Bug introduced by Maxime Fabre
The method isFtp does only exist in Rocketeer\Services\Conne...ials\Keys\ConnectionKey, but not in Rocketeer\Services\Conne...ials\Keys\RepositoryKey.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
146
                $this->command->writeln(' Oh damn is that an FTP host? Good luck buddy 👌');
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method writeln does not exist on object<Rocketeer\Console...mmands\AbstractCommand>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
147
            }
148 7
        }
149
150
        // Set in current configuration
151 7
        $configKey = $for === 'vcs' ? 'vcs' : 'connections.'.$for;
152 7
        $this->config->set($configKey, $config);
153
154 7
        return $credentials;
155
    }
156
157
    /**
158
     * @param string $for
159
     * @param string $credential
160
     * @param string $question
161
     *
162
     * @return string
163
     */
164 7
    protected function askQuestion($for, $credential, $question)
165
    {
166 7
        $isPassword = in_array($credential, ['keyphrase', 'password'], true);
167
168
        // Prepend who the question is for to name to question
169 7
        $question = (array) $question;
170 7
        $question[0] = '<fg=magenta>['.$for.']</fg=magenta> '.$question[0];
171 7
        if ($isPassword) {
172 6
            $question[] = $this->getCredentialsValidator();
173 6
        }
174
175
        // Get the credential, either through options or prompt
176 7
        if (($for !== 'vcs' || $credential === 'repository') && $option = $this->command->option($credential)) {
0 ignored issues
show
Bug Compatibility introduced by Maxime Fabre
The expression $this->command->option($credential); of type string|array adds the type array to the return on line 177 which is incompatible with the return type documented by Rocketeer\Services\Conne...lsGatherer::askQuestion of type string.
Loading history...
177 1
            return $option;
178
        }
179
180 7
        return $isPassword ? $this->command->askHidden(...$question) : $this->command->ask(...$question);
0 ignored issues
show
Documentation Bug introduced by Maxime FABRE
The method askHidden does not exist on object<Rocketeer\Console...mmands\AbstractCommand>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
Documentation Bug introduced by Maxime FABRE
The method ask does not exist on object<Rocketeer\Console...mmands\AbstractCommand>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
181
    }
182
183
    /**
184
     * @param string $prefix
185
     * @param string $credential
186
     *
187
     * @return string
188
     */
189 7
    protected function getCredentialConstant($prefix, $credential)
190
    {
191 7
        return mb_strtoupper($prefix.'_'.$credential);
192
    }
193
194
    /**
195
     * @return Closure
196
     */
197
    protected function getCredentialsValidator()
198
    {
199 6
        return function ($answer) {
200
            return $answer ?: true;
201 6
        };
202
    }
203
204
    /**
205
     * Present the connections in a table-like manner.
206
     *
207
     * @param array $connections
208
     */
209 5
    protected function presentConnections($connections)
210
    {
211
        $headers = [
212 5
            'name' => 'Name',
213 5
            'host' => 'Host',
214 5
            'username' => 'Username',
215 5
            'password' => 'Password',
216 5
            'key' => 'Key',
217 5
            'keyphrase' => 'Keyphrase',
218 5
            'root_directory' => 'Root directory',
219 5
        ];
220
221 5
        $rows = [];
222 5
        foreach ($connections as $name => $connection) {
223 5
            $connection[mb_strtoupper($name.'_NAME')] = $name;
224
225 5
            $row = [];
226 5
            foreach ($headers as $key => $value) {
227 5
                $key = mb_strtoupper($name.'_'.$key);
228 5
                $row[] = isset($connection[$key]) ? $connection[$key] : '';
229 5
            }
230
231 5
            $rows[] = $row;
232 5
        }
233
234 5
        $this->command->writeln('Here are the current connections defined:');
0 ignored issues
show
Documentation Bug introduced by Maxime FABRE
The method writeln does not exist on object<Rocketeer\Console...mmands\AbstractCommand>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
235 5
        $this->command->table(array_values($headers), $rows);
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method table does not exist on object<Rocketeer\Console...mmands\AbstractCommand>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
236 5
    }
237
}
238