Arguments   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 169
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 85.19%

Importance

Changes 0
Metric Value
dl 0
loc 169
ccs 69
cts 81
cp 0.8519
rs 10
c 0
b 0
f 0
wmc 23
lcom 1
cbo 2

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getCliArguments() 0 17 1
A getOption() 0 4 1
A getFlag() 0 8 2
A withFlags() 0 7 1
A withOptions() 0 7 1
A withFlag() 0 7 1
A withOption() 0 7 1
A withDefaults() 0 8 1
A withMultiplexing() 0 12 1
B generateControlPath() 0 30 8
A buildFlagsFromArray() 0 20 4
A __toString() 0 4 1
1
<?php
2
/* (c) Anton Medvedev <[email protected]>
3
 *
4
 * For the full copyright and license information, please view the LICENSE
5
 * file that was distributed with this source code.
6
 */
7
8
namespace Deployer\Ssh;
9
10
use Deployer\Exception\Exception;
11
use Deployer\Host\Host;
12
13
/**
14
 * @author Michael Woodward <[email protected]>
15
 */
16
class Arguments
17
{
18
    /**
19
     * @var array
20
     */
21
    private $flags = [];
22
23
    /**
24
     * @var array
25
     */
26
    private $options = [];
27
28 9
    public function getCliArguments()
29
    {
30 9
        $boolFlags = array_keys(array_filter($this->flags, 'is_null'));
31
32 9
        $valueFlags = array_filter($this->flags);
33 9
        $valueFlags = array_map(function ($key, $value) {
34 4
            return "$key $value";
35 9
        }, array_keys($valueFlags), $valueFlags);
36
37 9
        $options = array_map(function ($key, $value) {
38 7
            return "-o $key=$value";
39 9
        }, array_keys($this->options), $this->options);
40
41 9
        $args = sprintf('%s %s %s', implode(' ', $boolFlags), implode(' ', $valueFlags), implode(' ', $options));
42
43 9
        return trim(preg_replace('!\s+!', ' ', $args));
44
    }
45
46 3
    public function getOption(string $option)
47
    {
48 3
        return $this->options[$option] ?? '';
49
    }
50
51 1
    public function getFlag(string $flag)
52
    {
53 1
        if (!array_key_exists($flag, $this->flags)) {
54
            return false;
55
        }
56
57 1
        return $this->flags[$flag] ?? true;
58
    }
59
60 8
    public function withFlags(array $flags)
61
    {
62 8
        $clone = clone $this;
63 8
        $clone->flags = $this->buildFlagsFromArray($flags);
64
65 8
        return $clone;
66
    }
67
68 10
    public function withOptions(array $options)
69
    {
70 10
        $clone = clone $this;
71 10
        $clone->options = $options;
72
73 10
        return $clone;
74
    }
75
76 4
    public function withFlag(string $flag, string $value = null)
77
    {
78 4
        $clone = clone $this;
79 4
        $clone->flags = array_merge($this->flags, [$flag => $value]);
80
81 4
        return $clone;
82
    }
83
84 4
    public function withOption(string $option, string $value)
85
    {
86 4
        $clone = clone $this;
87 4
        $clone->options = array_merge($this->options, [$option => $value]);
88
89 4
        return $clone;
90
    }
91
92 4
    public function withDefaults(Arguments $defaultOptions)
93
    {
94 4
        $clone = clone $this;
95 4
        $clone->options = array_merge($defaultOptions->options, $this->options);
96 4
        $clone->flags = array_merge($defaultOptions->flags, $this->flags);
97
98 4
        return $clone;
99
    }
100
101 2
    public function withMultiplexing(Host $host)
102
    {
103 2
        $controlPath = $this->generateControlPath($host);
104
105 2
        $multiplexDefaults = (new Arguments)->withOptions([
106 2
            'ControlMaster' => 'auto',
107 2
            'ControlPersist' => '60',
108 2
            'ControlPath' => $controlPath,
109
        ]);
110
111 2
        return $this->withDefaults($multiplexDefaults);
112
    }
113
114
    /**
115
     * Return SSH multiplexing control path
116
     *
117
     * When ControlPath is longer than 104 chars we can get:
118
     *
119
     *     SSH Error: unix_listener: too long for Unix domain socket
120
     *
121
     * So try to get as descriptive path as possible.
122
     * %C is for creating hash out of connection attributes.
123
     *
124
     * @param Host $host
125
     * @return string ControlPath
126
     * @throws Exception
127
     */
128 2
    private function generateControlPath(Host $host)
129
    {
130 2
        $port = empty($host->getPort()) ? '' : ':' . $host->getPort();
131 2
        $connectionData = "$host$port";
132 2
        $tryLongestPossible = 0;
133 2
        $controlPath = '';
134
        do {
135
            switch ($tryLongestPossible) {
136 2
                case 1:
137
                    $controlPath = "~/.ssh/deployer_%C";
138
                    break;
139 2
                case 2:
140
                    $controlPath = "~/deployer_$connectionData";
141
                    break;
142 2
                case 3:
143
                    $controlPath = "~/deployer_%C";
144
                    break;
145 2
                case 4:
146
                    $controlPath = "~/mux_%C";
147
                    break;
148 2
                case 5:
149
                    throw new Exception("The multiplexing control path is too long. Control path is: $controlPath");
150
                default:
151 2
                    $controlPath = "~/.ssh/deployer_$connectionData";
152
            }
153 2
            $tryLongestPossible++;
154 2
        } while (strlen($controlPath) > 104); // Unix socket max length
155
156 2
        return $controlPath;
157
    }
158
159
    private function buildFlagsFromArray($flags)
160
    {
161 8
        $boolFlags = array_filter(array_map(function ($key, $value) {
162 8
            if (is_int($key)) {
163 6
                return $value;
164
            }
165
166 4
            if (null === $value) {
167 3
                return $key;
168
            }
169
170 4
            return false;
171 8
        }, array_keys($flags), $flags));
172
173 8
        $valueFlags = array_filter($flags, function ($key, $value) {
174 8
            return is_string($key) && is_string($value);
175 8
        }, ARRAY_FILTER_USE_BOTH);
176
177 8
        return array_merge(array_fill_keys($boolFlags, null), $valueFlags);
178
    }
179
180
    public function __toString()
181
    {
182
        return $this->getCliArguments();
183
    }
184
}
185