Completed
Push — develop ( a3c683...8c914c )
by Stuart
02:11
created

ParseNetInterface::breakupOutput()   C

Complexity

Conditions 13
Paths 30

Size

Total Lines 58
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 58
rs 6.5104
cc 13
eloc 41
nc 30
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Copyright (c) 2016-present Ganbaro Digital Ltd
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 *
11
 *   * Redistributions of source code must retain the above copyright
12
 *     notice, this list of conditions and the following disclaimer.
13
 *
14
 *   * Redistributions in binary form must reproduce the above copyright
15
 *     notice, this list of conditions and the following disclaimer in
16
 *     the documentation and/or other materials provided with the
17
 *     distribution.
18
 *
19
 *   * Neither the names of the copyright holders nor the names of his
20
 *     contributors may be used to endorse or promote products derived
21
 *     from this software without specific prior written permission.
22
 *
23
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
 * POSSIBILITY OF SUCH DAMAGE.
35
 *
36
 * @category  Libraries
37
 * @package   OperatingSystem/IpRoute/Parsers
38
 * @author    Stuart Herbert <[email protected]>
39
 * @copyright 2016-present Ganbaro Digital Ltd www.ganbarodigital.com
40
 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
41
 * @link      http://code.ganbarodigital.com/php-operating-system
42
 */
43
44
namespace GanbaroDigital\OperatingSystem\IpRoute\Parsers;
45
46
use GanbaroDigital\ArrayTools\ValueBuilders\ConvertToArray;
47
use GanbaroDigital\OperatingSystem\Exceptions\E4xx_CannotClassifyIpAddrLine;
48
use GanbaroDigital\OperatingSystem\Exceptions\E4xx_CannotParseNetInterface;
49
use GanbaroDigital\OperatingSystem\Exceptions\E5xx_CannotParseNetInterface;
50
use GanbaroDigital\OperatingSystem\Exceptions\E4xx_UnsupportedType;
51
use GanbaroDigital\OperatingSystem\Exceptions\E5xx_UnsupportedIpAddrLine;
52
use GanbaroDigital\OperatingSystem\IpRoute\Classifiers\ClassifyIpAddrLine;
53
use GanbaroDigital\OperatingSystem\NetInterfaces\Values\NetInterface;
54
use GanbaroDigital\Reflection\Maps\MapTypeToMethod;
55
use GanbaroDigital\Reflection\ValueBuilders\SimpleType;
56
use GanbaroDigital\TextTools\Filters\FilterOutEmptyValues;
57
58
/**
59
 * parse a single network interface definition, as found in the output of
60
 * the 'ip addr' and 'ip link' commands on Linux
61
 */
62
class ParseNetInterface
63
{
64
    /**
65
     * parse a single network interface from the output of the Linux 'ip'
66
     * command
67
     *
68
     * @param  mixed $output
69
     *         the command output to parse
70
     * @return NetInterface
71
     *         the network interface definition obtained from the command
72
     *         output
73
     */
74
    public function __invoke($output)
75
    {
76
        return self::from($output);
77
    }
78
79
    /**
80
     * parse a single network interface from the output of the Linux 'ip'
81
     * command
82
     *
83
     * @param  mixed $output
84
     *         the command output to parse
85
     * @return NetInterface
86
     *         the network interface definition obtained from the command
87
     *         output
88
     */
89
    public static function from($output)
90
    {
91
        $method = MapTypeToMethod::using($output, self::$dispatchMap);
92
        return self::$method($output);
93
    }
94
95
    /**
96
     * parse a single network interface from the output of the Linux 'ip'
97
     * command
98
     *
99
     * @param  mixed $output
100
     *         the command output to parse
101
     * @return NetInterface
102
     *         the network interface definition obtained from the command
103
     *         output
104
     */
105
    private static function fromTraversable($output)
106
    {
107
        // strip out any empty values
108
        $lines = FilterOutEmptyValues::from($output);
109
110
        // group the output into smaller chunks for parsing
111
        $breakdown = self::breakupOutput($lines);
112
113
        $inets = self::convertToInetAddresses($breakdown['inet']);
114
        $inet6s = self::convertToInet6Addresses($breakdown['inet6']);
115
        $link = self::convertToLink($breakdown['link']);
116
117
        // we can build our complete NetInterface now
118
        $retval = new NetInterface($link, $inets, $inet6s);
119
120
        // all done
121
        return $retval;
122
    }
123
124
    /**
125
     * take the output from 'ip addr show <interface>' and break it up into
126
     * the individual groups that we can parse
127
     *
128
     * @param  array $lines
129
     *         the output to break up
130
     * @return array
131
     *         the grouped output
132
     */
133
    private static function breakupOutput($lines)
134
    {
135
        $linkLines = [];
136
        $inetLines = [];
137
        $inet6Lines = [];
138
139
        // what do we have?
140
        try {
141
            foreach ($lines as $line) {
142
                $lineType = ClassifyIpAddrLine::from($line);
143
144
                switch ($lineType) {
145
                    case ClassifyIpAddrLine::LINK_START:
146
                    case ClassifyIpAddrLine::LINK_ETHER:
147
                    case ClassifyIpAddrLine::LINK_LOOPBACK:
148
                    case ClassifyIpAddrLine::LINK_NONE:
149
                        $linkLines[] = $line;
150
                        break;
151
                    case ClassifyIpAddrLine::INET_START:
152
                        $inetLines[] = [ $line ];
153
                        $optionsTarget = 'inetLines';
154
                        break;
155
                    case ClassifyIpAddrLine::INET6_START:
156
                        $inet6Lines[] = [ $line ];
157
                        $optionsTarget = 'inet6Lines';
158
                        break;
159
                    case ClassifyIpAddrLine::INET_OPTIONS:
160
                        switch ($optionsTarget) {
0 ignored issues
show
Bug introduced by
The variable $optionsTarget does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
161
                            case 'inetLines':
162
                                $inetLines[count($inetLines) - 1][] = $line;
163
                                break;
164
                            case 'inet6Lines':
165
                                $inet6Lines[count($inet6Lines) - 1][] = $line;
166
                                break;
167
                        }
168
                        break;
169
170
                    default:
171
                        // important that we catch any line types that we do not
172
                        // yet know what to do with
173
                        throw new E5xx_UnsupportedIpAddrLine($line, $lineType);
174
                }
175
            }
176
        }
177
        catch (E4xx_CannotClassifyIpAddrLine $e) {
178
            throw new E4xx_CannotParseNetInterface($lines, $e);
179
        }
180
        catch (E5xx_UnsupportedIpAddrLine $e) {
181
            throw new E5xx_CannotParseNetInterface($lines, $e);
182
        }
183
184
        // all done
185
        return [
186
            'link' => $linkLines,
187
            'inet' => $inetLines,
188
            'inet6' => $inet6Lines,
189
        ];
190
    }
191
192
    /**
193
     * convert the isolated lines of text for inet (IPv4) addresses into a
194
     * list of InetAddress objects
195
     *
196
     * @param  array $inets
197
     *         the 'ip addr' lines for the inet addresses
198
     * @return array<InetAddress>
199
     *         a list of InetAddress entries
200
     */
201
    private static function convertToInetAddresses($inets)
202
    {
203
        // our return value
204
        $retval = [];
205
206
        foreach ($inets as $inetLines) {
207
            $retval[] = ParseInetAddress::from($inetLines);
208
        }
209
210
        // all done
211
        return $retval;
212
    }
213
214
    /**
215
     * convert the isolated lines of text for inet6 (IPv6) addresses into a
216
     * list of Inet6Address objects
217
     *
218
     * @param  array $inet6s
219
     *         the 'ip addr' lines for the inet6 addresses
220
     * @return array<Inet6Address>
221
     *         a list of Inet6Address entries
222
     */
223
    private static function convertToInet6Addresses($inet6s)
224
    {
225
        // our return value
226
        $retval = [];
227
228
        foreach ($inet6s as $inet6Lines) {
229
            $retval[] = ParseInet6Address::from($inet6Lines);
230
        }
231
232
        // all done
233
        return $retval;
234
    }
235
236
    /**
237
     * convert the isolated lines of text for the physical link into a
238
     * single NetLink object
239
     *
240
     * @param  array $lines
241
     *         the 'ip addr' lines for the physical link
242
     * @return NetLink
0 ignored issues
show
Documentation introduced by
Should the return type not be \GanbaroDigital\Operatin...terfaces\Values\NetLink?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
243
     *         the value object extracted from the lines of output
244
     */
245
    private static function convertToLink($lines)
246
    {
247
        return ParseNetLink::from($lines);
248
    }
249
250
    /**
251
     * parse a single network interface from the output of the Linux 'ip'
252
     * command
253
     *
254
     * @param  mixed $output
255
     *         the command output to parse
256
     * @return NetInterface
257
     *         the network interface definition obtained from the command
258
     *         output
259
     */
260
    private static function fromString($output)
261
    {
262
        $lines = explode("\n", $output);
263
        return self::fromTraversable($lines);
264
    }
265
266
    /**
267
     * called when we've been asked to parse a datatype that we do not support
268
     *
269
     * @param  mixed $output
270
     *         the command output to parse
271
     * @return void
272
     * @throws E4xx_UnsupportedType
273
     */
274
    private static function nothingMatchesTheInputType($output)
275
    {
276
        throw new E4xx_UnsupportedType(SimpleType::from($output));
277
    }
278
279
    /**
280
     * our map of how to handle each data type we are passed
281
     * @var array
282
     */
283
    private static $dispatchMap = [
284
        'String' => 'fromString',
285
        'Traversable' => 'fromTraversable',
286
    ];
287
}
288