Client   A
last analyzed

Complexity

Total Complexity 27

Size/Duplication

Total Lines 340
Duplicated Lines 18.53 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 30.83%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 27
c 3
b 0
f 0
lcom 1
cbo 12
dl 63
loc 340
ccs 41
cts 133
cp 0.3083
rs 10

13 Methods

Rating   Name   Duplication   Size   Complexity  
A getEventDispatcher() 0 8 2
A setEventDispatcher() 0 6 1
A getMulticastFactory() 0 8 2
A setMulticastFactory() 0 6 1
A getRequestFactory() 0 8 2
A setRequestFactory() 0 6 1
A sendRequest() 0 12 2
A receiveResponse() 0 10 2
A alive() 21 21 1
A byebye() 21 21 1
B discover() 0 50 4
A update() 21 21 1
C createDiscoverEvent() 0 32 7

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * This file is part of the Ssdp project.
4
 *
5
 * @author Daniel Schröder <[email protected]>
6
 */
7
8
namespace GravityMedia\Ssdp;
9
10
use GravityMedia\Ssdp\Event\DiscoverEvent;
11
use GravityMedia\Ssdp\Exception\DiscoverException;
12
use GravityMedia\Ssdp\Multicast\Factory as MulticastFactory;
13
use GravityMedia\Ssdp\Options\AliveOptions;
14
use GravityMedia\Ssdp\Options\ByebyeOptions;
15
use GravityMedia\Ssdp\Options\DiscoverOptions;
16
use GravityMedia\Ssdp\Options\UpdateOptions;
17
use GravityMedia\Ssdp\Request\Factory as RequestFactory;
18
use Psr\Http\Message\RequestInterface;
19
use Socket\Raw\Exception as SocketException;
20
use Socket\Raw\Socket;
21
use Symfony\Component\EventDispatcher\EventDispatcher;
22
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
23
use Zend\Diactoros\Request\Serializer as RequestSerializer;
24
use Zend\Diactoros\Response\Serializer as ResponseSerializer;
25
use Zend\Diactoros\Uri;
26
27
/**
28
 * Ssdp client class
29
 *
30
 * @package GravityMedia\Ssdp
31
 */
32
class Client
33
{
34
    /**
35
     * The multicast address
36
     */
37
    const MULTICAST_ADDRESS = '239.255.255.250';
38
39
    /**
40
     * The multicast port
41
     */
42
    const MULTICAST_PORT = 1900;
43
44
    /**
45
     * @var EventDispatcherInterface
46
     */
47
    protected $eventDispatcher;
48
49
    /**
50
     * @var MulticastFactory
51
     */
52
    protected $multicastFactory;
53
54
    /**
55
     * @var RequestFactory
56
     */
57
    protected $requestFactory;
58
59
    /**
60
     * Get event dispatcher
61
     *
62
     * @return EventDispatcherInterface
63
     */
64
    public function getEventDispatcher()
65
    {
66
        if (null === $this->eventDispatcher) {
67
            $this->eventDispatcher = new EventDispatcher();
68
        }
69
70
        return $this->eventDispatcher;
71
    }
72
73
    /**
74
     * Set event dispatcher
75
     *
76
     * @param EventDispatcherInterface $eventDispatcher
77
     *
78
     * @return $this
79
     */
80 2
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
81
    {
82 2
        $this->eventDispatcher = $eventDispatcher;
83
84 2
        return $this;
85
    }
86
87
    /**
88
     * Get multicast factory
89
     *
90
     * @return MulticastFactory
91
     */
92 2
    public function getMulticastFactory()
93
    {
94 2
        if (null === $this->multicastFactory) {
95
            $this->multicastFactory = new MulticastFactory();
96
        }
97
98 2
        return $this->multicastFactory;
99
    }
100
101
    /**
102
     * Set multicast factory
103
     *
104
     * @param MulticastFactory $multicastFactory
105
     *
106
     * @return $this
107
     */
108 2
    public function setMulticastFactory(MulticastFactory $multicastFactory)
109
    {
110 2
        $this->multicastFactory = $multicastFactory;
111
112 2
        return $this;
113
    }
114
115
    /**
116
     * Get request factory
117
     *
118
     * @return RequestFactory
119
     */
120 2
    public function getRequestFactory()
121
    {
122 2
        if (null === $this->requestFactory) {
123
            $this->requestFactory = new RequestFactory();
124
        }
125
126 2
        return $this->requestFactory;
127
    }
128
129
    /**
130
     * Set request factory
131
     *
132
     * @param RequestFactory $requestFactory
133
     *
134
     * @return $this
135
     */
136 2
    public function setRequestFactory(RequestFactory $requestFactory)
137
    {
138 2
        $this->requestFactory = $requestFactory;
139
140 2
        return $this;
141
    }
142
143
    /**
144
     * Send request
145
     *
146
     * @param Socket $socket
147
     * @param RequestInterface $request
148
     * @param callable $onSuccess
149
     * @param callable $onFailure
150
     *
151
     * @return boolean
152
     */
153 2
    protected function sendRequest(Socket $socket, RequestInterface $request, $onSuccess, $onFailure)
154
    {
155 2
        $data = trim(RequestSerializer::toString($request)) . "\r\n\r\n";
156
157
        try {
158 2
            $bytes = $socket->sendTo($data, 0, sprintf('%s:%s', self::MULTICAST_ADDRESS, self::MULTICAST_PORT));
159 1
        } catch (SocketException $exception) {
160
            return $onFailure($socket, $exception);
161
        }
162
163 2
        return $onSuccess($socket, $bytes);
164
    }
165
166
    /**
167
     * Receive response
168
     *
169
     * @param Socket $socket
170
     * @param callable $onSuccess
171
     * @param callable $onFailure
172
     *
173
     * @return boolean
174
     */
175 2
    protected function receiveResponse(Socket $socket, $onSuccess, $onFailure)
176
    {
177
        try {
178 2
            $message = $socket->recvFrom(1024, MSG_WAITALL, $remote);
179 1
        } catch (SocketException $exception) {
180
            return $onFailure($socket, $exception);
181
        }
182
183 2
        return $onSuccess($socket, $message, $remote);
184
    }
185
186
    /**
187
     * Send alive request
188
     *
189
     * @param AliveOptions $options
190
     *
191
     * @return boolean
192
     */
193 View Code Duplication
    public function alive(AliveOptions $options)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
194
    {
195
        return $this->sendRequest(
196
            $this->getMulticastFactory()->createSocket(self::MULTICAST_ADDRESS),
197
            $this->getRequestFactory()->createAliveRequest($options),
198
            function (Socket $socket) {
199
                $socket->close();
200
201
                return true;
202
            },
203
            function (Socket $socket, SocketException $exception) {
204
                $socket->close();
205
206
                $event = new DiscoverEvent();
207
                $event->setException(new DiscoverException('Error sending alive broadcast', 0, $exception));
208
                $this->getEventDispatcher()->dispatch(DiscoverEvent::EVENT_DISCOVER_ERROR, $event);
209
210
                return false;
211
            }
212
        );
213
    }
214
215
    /**
216
     * Send byebye request
217
     *
218
     * @param ByebyeOptions $options
219
     *
220
     * @return boolean
221
     */
222 View Code Duplication
    public function byebye(ByebyeOptions $options)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
223
    {
224
        return $this->sendRequest(
225
            $this->getMulticastFactory()->createSocket(self::MULTICAST_ADDRESS),
226
            $this->getRequestFactory()->createByebyeRequest($options),
227
            function (Socket $socket) {
228
                $socket->close();
229
230
                return true;
231
            },
232
            function (Socket $socket, SocketException $exception) {
233
                $socket->close();
234
235
                $event = new DiscoverEvent();
236
                $event->setException(new DiscoverException('Error sending byebye broadcast', 0, $exception));
237
                $this->getEventDispatcher()->dispatch(DiscoverEvent::EVENT_DISCOVER_ERROR, $event);
238
239
                return false;
240
            }
241
        );
242
    }
243
244
    /**
245
     * Send discover request
246
     *
247
     * @param DiscoverOptions $options
248
     * @param int $timeout
249
     *
250
     * @return boolean
251
     */
252 2
    public function discover(DiscoverOptions $options, $timeout = 2)
253
    {
254 2
        return $this->sendRequest(
255 2
            $this->getMulticastFactory()->createSocket(self::MULTICAST_ADDRESS, $timeout),
256 2
            $this->getRequestFactory()->createDiscoverRequest($options),
257
            function (Socket $socket) {
258
                do {
259 2
                    $continue = $this->receiveResponse(
260 1
                        $socket,
261
                        function (Socket $socket, $message, $remote) {
262 2
                            if (null === $message) {
263 2
                                return false;
264
                            }
265
266
                            $event = $this->createDiscoverEvent($message)->setRemote($remote);
267
                            $this->getEventDispatcher()->dispatch(DiscoverEvent::EVENT_DISCOVER, $event);
268
269
                            return true;
270 2
                        },
271
                        function (Socket $socket, SocketException $exception) {
272
                            if (SOCKET_EAGAIN === $exception->getCode()) {
273
                                return false;
274
                            }
275
276
                            $exception = new DiscoverException('Error receiving discover message', 0, $exception);
277
                            $event = new DiscoverEvent();
278
                            $event->setException($exception);
279
                            $this->getEventDispatcher()->dispatch(DiscoverEvent::EVENT_DISCOVER_ERROR, $event);
280
281
                            return false;
282 1
                        }
283 1
                    );
284
285 2
                } while ($continue);
286
287 2
                $socket->close();
288
289 2
                return true;
290 2
            },
291
            function (Socket $socket, SocketException $exception) {
292
                $socket->close();
293
294
                $event = new DiscoverEvent();
295
                $event->setException(new DiscoverException('Error sending discover broadcast', 0, $exception));
296
                $this->getEventDispatcher()->dispatch(DiscoverEvent::EVENT_DISCOVER_ERROR, $event);
297
298
                return false;
299 1
            }
300 1
        );
301
    }
302
303
    /**
304
     * Send update request
305
     *
306
     * @param UpdateOptions $options
307
     *
308
     * @return boolean
309
     */
310 View Code Duplication
    public function update(UpdateOptions $options)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
311
    {
312
        return $this->sendRequest(
313
            $this->getMulticastFactory()->createSocket(self::MULTICAST_ADDRESS),
314
            $this->getRequestFactory()->createUpdateRequest($options),
315
            function (Socket $socket) {
316
                $socket->close();
317
318
                return true;
319
            },
320
            function (Socket $socket, SocketException $exception) {
321
                $socket->close();
322
323
                $event = new DiscoverEvent();
324
                $event->setException(new DiscoverException('Error sending byebye broadcast', 0, $exception));
325
                $this->getEventDispatcher()->dispatch(DiscoverEvent::EVENT_DISCOVER_ERROR, $event);
326
327
                return false;
328
            }
329
        );
330
    }
331
332
    /**
333
     * Create discover event
334
     *
335
     * @param string $message
336
     *
337
     * @return DiscoverEvent
338
     */
339
    protected function createDiscoverEvent($message)
340
    {
341
        $response = ResponseSerializer::fromString($message);
342
        $event = new DiscoverEvent();
343
344
        if ($response->hasHeader('CACHE-CONTROL')) {
345
            $value = $response->getHeaderLine('CACHE-CONTROL');
346
            $event->setLifetime(intval(substr($value, strpos($value, '=') + 1)));
347
        }
348
349
        if ($response->hasHeader('DATE')) {
350
            $event->setDate(new \DateTime($response->getHeaderLine('DATE')));
351
        }
352
353
        if ($response->hasHeader('LOCATION')) {
354
            $event->setDescriptionUrl(new Uri($response->getHeaderLine('LOCATION')));
355
        }
356
357
        if ($response->hasHeader('SERVER')) {
358
            $event->setServerString($response->getHeaderLine('SERVER'));
359
        }
360
361
        if ($response->hasHeader('ST')) {
362
            $event->setSearchTargetString($response->getHeaderLine('ST'));
363
        }
364
365
        if ($response->hasHeader('USN')) {
366
            $event->setUniqueServiceNameString($response->getHeaderLine('USN'));
367
        }
368
369
        return $event;
370
    }
371
}
372