PingParserForNix::setHostStatus()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
/**
4
 * Ping for Laravel.
5
 *
6
 * This class makes Ping request to a host.
7
 *
8
 * Ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to elicit an ICMP ECHO_RESPONSE from a host or gateway.
9
 *
10
 * @author  Angel Campos <[email protected]>
11
 *
12
 * @requires PHP 8.0
13
 *
14
 * @version  2.1.2
15
 */
16
17
namespace Acamposm\Ping\Parsers;
18
19
use Acamposm\Ping\System;
20
21
final class PingParserForNix extends PingParser
22
{
23
    protected bool $is_unreachable;
24
25
    /**
26
     * PingParserForNix constructor.
27
     *
28
     * @param array $ping
29
     */
30
    public function __construct(array $ping)
31
    {
32
        parent::__construct($ping);
33
34
        $this->is_unreachable = $this->isUnreachable($ping);
35
36
        $this->host_status = 'Unreachable';
37
38
        if (preg_match('/^--- (.+) ---$/i', $ping[count($ping) - 2])) {
39
            $this->setStatistics($ping[count($ping) - 1]);
40
41
            if ($this->is_unreachable === false) {
42
                // $this->setRoundTripTime($ping[count($ping)-1]);
43
                $this->setSequence();
44
                $this->setHostStatus();
45
            }
46
        } else {
47
            $this->setStatistics($ping[count($ping) - 2]);
48
49
            if ($this->is_unreachable === false) {
50
                $this->setRoundTripTime($ping[count($ping) - 1]);
51
                $this->setSequence();
52
                $this->setHostStatus();
53
            }
54
        }
55
    }
56
57
    private function cleanStatisticsRecord(string $row): array
58
    {
59
        $search = explode(
60
            '|',
61
            'packets|transmitted|received|+|errors|%|packet|loss|time|ms'
62
        );
63
64
        $row = trim(str_replace($search, '', $row));
65
66
        return array_map('trim', explode(', ', $row));
67
    }
68
69
    private function isUnreachable($ping): bool
70
    {
71
        $unreachable = false;
72
73
        foreach ($ping as $row) {
74
            if ($unreachable === strpos($row, '100% packet loss')) {
75
                break;
76
            }
77
        }
78
79
        return $unreachable !== false;
80
    }
81
82
    /**
83
     * Get the host status.
84
     *
85
     * @return string
86
     */
87
    private function getHostStatus(): string
88
    {
89
        return ($this->statistics['packets_received'] == $this->statistics['packets_transmitted']) ? 'Ok'
90
            : ($this->statistics['packets_lost'] == $this->statistics['packets_transmitted']
91
                ? 'Unreachable' : 'High Latency');
92
    }
93
94
    /**
95
     * Return an array with the Ping sequence and latency.
96
     *
97
     * @return array
98
     */
99
    private function getSequence(): array
100
    {
101
        $ping = $this->results;
102
103
        // Remove unnecessary index
104
        unset($ping[0]);
105
        unset($ping[count($ping) - 4]);
106
        unset($ping[count($ping) - 3]);
107
        unset($ping[count($ping) - 2]);
108
        unset($ping[count($ping) - 1]);
109
110
        $sequence = [];
111
112
        foreach ($ping as $row) {
113
            if (str_contains($row, 'Unreachable') && !empty($row)) {
114
                $data = explode(': ', str_replace(' ms', '', $row));
115
                $items = explode(' ', $data[1]);
116
117
                $key = (int) explode('=', $items[0])[1];
118
                $value = (float) explode('=', $items[2])[1];
119
120
                $sequence[$key] = $value;
121
            }
122
        }
123
124
        return $sequence;
125
    }
126
127
    /**
128
     * Return an object with Ping Round Trip Time.
129
     *
130
     * @param string $row
131
     *
132
     * @return array
133
     */
134
    private function parseRoundTripTime(string $row): array
135
    {
136
        if (!str_contains($row, 'rtt')) {
137
            return [];
138
        }
139
140
        $row = trim(str_replace(['ms', System::isLinux() ? 'rtt' : 'round-trip'], '', $row));
141
142
        $rtt = explode(' = ', $row);
143
144
        $keys = explode('/', $rtt[0]);
145
146
        $values = array_map('floatval', explode('/', $rtt[1]));
147
148
        return array_combine($keys, $values);
149
    }
150
151
    /**
152
     * Return an object with Ping Statistics.
153
     *
154
     * @param string $row
155
     *
156
     * @return array
157
     */
158
    private function parseStatistics(string $row): array
159
    {
160
        $statistics = $this->cleanStatisticsRecord($row);
161
162
        $results = [
163
            'packets_transmitted' => (int) $statistics[0],
164
            'packets_received'    => (int) $statistics[1],
165
            'packets_lost'        => (int) ($statistics[0] - $statistics[1]),
166
        ];
167
168
        if (count($statistics) === 5 && $this->is_unreachable) {
169
            $results['errors'] = (int) $statistics[2];
170
        }
171
172
        if (count($statistics) === 5) {
173
            $results['packets_lost'] = (float) $statistics[3];
174
            $results['time'] = (int) $statistics[4];
175
        }
176
177
        if (count($statistics) === 4) {
178
            $results['packets_lost'] = (float) $statistics[2];
179
            $results['time'] = (int) $statistics[3];
180
        }
181
182
        return $results;
183
    }
184
185
    /**
186
     * Set the host status.
187
     */
188
    private function setHostStatus(): void
189
    {
190
        $this->host_status = $this->getHostStatus();
191
    }
192
193
    /**
194
     * Set the Round Trip Time statistics.
195
     *
196
     * @param string $rtt
197
     */
198
    private function setRoundTripTime(string $rtt): void
199
    {
200
        $this->round_trip_time = $this->parseRoundTripTime($rtt);
201
    }
202
203
    /**
204
     * Set the Ping sequence.
205
     */
206
    private function setSequence(): void
207
    {
208
        $this->sequence = $this->getSequence();
209
    }
210
211
    /**
212
     * Set the Statistics.
213
     *
214
     * @param string $statistics
215
     */
216
    private function setStatistics(string $statistics): void
217
    {
218
        $this->statistics = $this->parseStatistics($statistics);
219
    }
220
221
    /**
222
     * Return the Ping execution result parsed as object.
223
     *
224
     * @return object
225
     */
226
    public function parse(): object
227
    {
228
        $parsed = [
229
            'host_status' => $this->host_status,
230
            'raw'         => (object) $this->results,
231
        ];
232
233
        if (count($this->round_trip_time) > 0) {
234
            $parsed['latency'] = $this->round_trip_time['avg'];
235
            $parsed['rtt'] = (object) $this->round_trip_time;
236
        }
237
238
        if (count($this->sequence) > 0) {
239
            $parsed['sequence'] = (object) $this->sequence;
240
        }
241
242
        if (count($this->statistics) > 0) {
243
            $parsed['statistics'] = (object) $this->statistics;
244
        }
245
246
        return (object) $parsed;
247
    }
248
}
249