ParseNetInterface::nothingMatchesTheInputType()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
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
        $optionsTarget = 'inetLines';
139
140
        // what do we have?
141
        try {
142
            foreach ($lines as $line) {
143
                $lineType = ClassifyIpAddrLine::from($line);
144
145
                switch ($lineType) {
146
                    case ClassifyIpAddrLine::LINK_START:
147
                    case ClassifyIpAddrLine::LINK_ETHER:
148
                    case ClassifyIpAddrLine::LINK_LOOPBACK:
149
                    case ClassifyIpAddrLine::LINK_NONE:
150
                        $linkLines[] = $line;
151
                        break;
152
                    case ClassifyIpAddrLine::INET_START:
153
                        $inetLines[] = [ $line ];
154
                        $optionsTarget = 'inetLines';
155
                        break;
156
                    case ClassifyIpAddrLine::INET6_START:
157
                        $inet6Lines[] = [ $line ];
158
                        $optionsTarget = 'inet6Lines';
159
                        break;
160
                    case ClassifyIpAddrLine::INET_OPTIONS:
161
                        switch ($optionsTarget) {
162
                            case 'inetLines':
163
                                $inetLines[count($inetLines) - 1][] = $line;
164
                                break;
165
                            case 'inet6Lines':
166
                                $inet6Lines[count($inet6Lines) - 1][] = $line;
167
                                break;
168
                        }
169
                        break;
170
171
                    default:
172
                        // important that we catch any line types that we do not
173
                        // yet know what to do with
174
                        throw new E5xx_UnsupportedIpAddrLine($line, $lineType);
175
                }
176
            }
177
        }
178
        catch (E4xx_CannotClassifyIpAddrLine $e) {
179
            throw new E4xx_CannotParseNetInterface($lines, $e);
180
        }
181
        catch (E5xx_UnsupportedIpAddrLine $e) {
182
            throw new E5xx_CannotParseNetInterface($lines, $e);
183
        }
184
185
        // all done
186
        return [
187
            'link' => $linkLines,
188
            'inet' => $inetLines,
189
            'inet6' => $inet6Lines,
190
        ];
191
    }
192
193
    /**
194
     * convert the isolated lines of text for inet (IPv4) addresses into a
195
     * list of InetAddress objects
196
     *
197
     * @param  array $inets
198
     *         the 'ip addr' lines for the inet addresses
199
     * @return array<InetAddress>
200
     *         a list of InetAddress entries
201
     */
202
    private static function convertToInetAddresses($inets)
203
    {
204
        // our return value
205
        $retval = [];
206
207
        foreach ($inets as $inetLines) {
208
            $retval[] = ParseInetAddress::from($inetLines);
209
        }
210
211
        // all done
212
        return $retval;
213
    }
214
215
    /**
216
     * convert the isolated lines of text for inet6 (IPv6) addresses into a
217
     * list of Inet6Address objects
218
     *
219
     * @param  array $inet6s
220
     *         the 'ip addr' lines for the inet6 addresses
221
     * @return array<Inet6Address>
222
     *         a list of Inet6Address entries
223
     */
224
    private static function convertToInet6Addresses($inet6s)
225
    {
226
        // our return value
227
        $retval = [];
228
229
        foreach ($inet6s as $inet6Lines) {
230
            $retval[] = ParseInet6Address::from($inet6Lines);
231
        }
232
233
        // all done
234
        return $retval;
235
    }
236
237
    /**
238
     * convert the isolated lines of text for the physical link into a
239
     * single NetLink object
240
     *
241
     * @param  array $lines
242
     *         the 'ip addr' lines for the physical link
243
     * @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...
244
     *         the value object extracted from the lines of output
245
     */
246
    private static function convertToLink($lines)
247
    {
248
        return ParseNetLink::from($lines);
249
    }
250
251
    /**
252
     * parse a single network interface from the output of the Linux 'ip'
253
     * command
254
     *
255
     * @param  mixed $output
256
     *         the command output to parse
257
     * @return NetInterface
258
     *         the network interface definition obtained from the command
259
     *         output
260
     */
261
    private static function fromString($output)
262
    {
263
        $lines = explode("\n", $output);
264
        return self::fromTraversable($lines);
265
    }
266
267
    /**
268
     * called when we've been asked to parse a datatype that we do not support
269
     *
270
     * @param  mixed $output
271
     *         the command output to parse
272
     * @return void
273
     * @throws E4xx_UnsupportedType
274
     */
275
    private static function nothingMatchesTheInputType($output)
276
    {
277
        throw new E4xx_UnsupportedType(SimpleType::from($output));
278
    }
279
280
    /**
281
     * our map of how to handle each data type we are passed
282
     * @var array
283
     */
284
    private static $dispatchMap = [
285
        'String' => 'fromString',
286
        'Traversable' => 'fromTraversable',
287
    ];
288
}
289