Completed
Push — master ( b079db...6e8858 )
by Daniel
02:44
created

Client::getAliveRequestFactory()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
ccs 0
cts 5
cp 0
rs 9.4285
cc 2
eloc 4
nc 2
nop 0
crap 6
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\Options\AliveOptions;
13
use GravityMedia\Ssdp\Options\ByebyeOptions;
14
use GravityMedia\Ssdp\Options\DiscoverOptions;
15
use GravityMedia\Ssdp\Options\UpdateOptions;
16
use GravityMedia\Ssdp\Request\Factory as RequestFactory;
17
use Socket\Raw\Exception as SocketException;
18
use Socket\Raw\Factory as SocketFactory;
19
use Symfony\Component\EventDispatcher\EventDispatcher;
20
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
21
use Zend\Diactoros\Request\Serializer as RequestSerializer;
22
use Zend\Diactoros\Response\Serializer as ResponseSerializer;
23
use Zend\Diactoros\Uri;
24
25
/**
26
 * Ssdp client class
27
 *
28
 * @package GravityMedia\Ssdp
29
 */
30
class Client
31
{
32
    /**
33
     * The multicast address
34
     */
35
    const MULTICAST_ADDRESS = '239.255.255.250';
36
37
    /**
38
     * The multicast port
39
     */
40
    const MULTICAST_PORT = 1900;
41
42
    /**
43
     * @var EventDispatcherInterface
44
     */
45
    protected $eventDispatcher;
46
47
    /**
48
     * @var SocketFactory
49
     */
50
    protected $socketFactory;
51
52
    /**
53
     * @var RequestFactory
54
     */
55
    protected $requestFactory;
56
57
    /**
58
     * Get event dispatcher
59
     *
60
     * @return EventDispatcherInterface
61
     */
62
    public function getEventDispatcher()
63
    {
64
        if (null === $this->eventDispatcher) {
65
            $this->eventDispatcher = new EventDispatcher();
66
        }
67
68
        return $this->eventDispatcher;
69
    }
70
71
    /**
72
     * Set event dispatcher
73
     *
74
     * @param EventDispatcherInterface $eventDispatcher
75
     *
76
     * @return $this
77
     */
78 2
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
79
    {
80 2
        $this->eventDispatcher = $eventDispatcher;
81
82 2
        return $this;
83
    }
84
85
    /**
86
     * Get socket factory
87
     *
88
     * @return SocketFactory
89
     */
90 2
    public function getSocketFactory()
91
    {
92 2
        if (null === $this->socketFactory) {
93
            $this->socketFactory = new SocketFactory();
94
        }
95
96 2
        return $this->socketFactory;
97
    }
98
99
    /**
100
     * Set socket factory
101
     *
102
     * @param SocketFactory $socketFactory
103
     *
104
     * @return $this
105
     */
106 2
    public function setSocketFactory(SocketFactory $socketFactory)
107
    {
108 2
        $this->socketFactory = $socketFactory;
109
110 2
        return $this;
111
    }
112
113
    /**
114
     * Get request factory
115
     *
116
     * @return RequestFactory
117
     */
118 2
    public function getRequestFactory()
119
    {
120 2
        if (null === $this->requestFactory) {
121
            $this->requestFactory = new RequestFactory();
122
        }
123
124 2
        return $this->requestFactory;
125
    }
126
127
    /**
128
     * Set request factory
129
     *
130
     * @param RequestFactory $requestFactory
131
     *
132
     * @return $this
133
     */
134 2
    public function setRequestFactory(RequestFactory $requestFactory)
135
    {
136 2
        $this->requestFactory = $requestFactory;
137
138 2
        return $this;
139
    }
140
141
    /**
142
     * Send alive request
143
     *
144
     * @param AliveOptions $options
145
     *
146
     * @return PromiseInterface
147
     */
148 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...
149
    {
150
        $request = $this->getRequestFactory()->createAliveRequest($options);
151
        $data = trim(RequestSerializer::toString($request)) . "\r\n\r\n";
152
153
        $socket = $this->getSocketFactory()->createUdp4();
154
        $socket->setOption(SOL_SOCKET, SO_BROADCAST, 1);
155
        $socket->setOption(IPPROTO_IP, IP_MULTICAST_IF, 0);
156
        $socket->setOption(IPPROTO_IP, IP_MULTICAST_LOOP, 0);
157
        $socket->setOption(IPPROTO_IP, IP_MULTICAST_TTL, 4);
158
        $socket->setOption(IPPROTO_IP, MCAST_JOIN_GROUP, ['group' => self::MULTICAST_ADDRESS, 'interface' => 0]);
159
        $socket->bind('0.0.0.0');
160
        $socket->sendTo($data, 0, sprintf('%s:%s', self::MULTICAST_ADDRESS, self::MULTICAST_PORT));
161
162
        return $this;
163
    }
164
165
    /**
166
     * Send byebye request
167
     *
168
     * @param ByebyeOptions $options
169
     *
170
     * @return PromiseInterface
171
     */
172 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...
173
    {
174
        $request = $this->getRequestFactory()->createByebyeRequest($options);
175
        $data = trim(RequestSerializer::toString($request)) . "\r\n\r\n";
176
177
        $socket = $this->getSocketFactory()->createUdp4();
178
        $socket->setOption(SOL_SOCKET, SO_BROADCAST, 1);
179
        $socket->setOption(IPPROTO_IP, IP_MULTICAST_IF, 0);
180
        $socket->setOption(IPPROTO_IP, IP_MULTICAST_LOOP, 0);
181
        $socket->setOption(IPPROTO_IP, IP_MULTICAST_TTL, 4);
182
        $socket->setOption(IPPROTO_IP, MCAST_JOIN_GROUP, ['group' => self::MULTICAST_ADDRESS, 'interface' => 0]);
183
        $socket->bind('0.0.0.0');
184
        $socket->sendTo($data, 0, sprintf('%s:%s', self::MULTICAST_ADDRESS, self::MULTICAST_PORT));
185
186
        return $this;
187
    }
188
189
    /**
190
     * Send discover request
191
     *
192
     * @param DiscoverOptions $options
193
     * @param int $timeout
194
     *
195
     * @return $this
196
     */
197 2
    public function discover(DiscoverOptions $options, $timeout = 2)
198
    {
199 2
        $request = $this->getRequestFactory()->createDiscoverRequest($options);
200 2
        $data = trim(RequestSerializer::toString($request)) . "\r\n\r\n";
201
202 2
        $socket = $this->getSocketFactory()->createUdp4();
203 2
        $socket->setOption(SOL_SOCKET, SO_BROADCAST, 1);
204
205
        try {
206 2
            $socket->sendTo($data, 0, sprintf('%s:%s', self::MULTICAST_ADDRESS, self::MULTICAST_PORT));
207 1
        } catch (SocketException $exception) {
208
            $socket->close();
209
210
            $event = new DiscoverEvent();
211
            $event->setException(new DiscoverException('Error sending discover broadcast', 0, $exception));
212
            $this->getEventDispatcher()->dispatch(DiscoverEvent::EVENT_DISCOVER_ERROR, $event);
213
214
            return $this;
215
        }
216
217 2
        $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, ['sec' => $timeout, 'usec' => 0]);
218
        do {
219
            try {
220 2
                $message = $socket->recvFrom(1024, MSG_WAITALL, $remote);
221 1
            } catch (SocketException $exception) {
222
                if (SOCKET_EAGAIN === $exception->getCode()) {
223
                    break;
224
                }
225
226
                $socket->close();
227
228
                $event = new DiscoverEvent();
229
                $event->setException(new DiscoverException('Error receiving discover message', 0, $exception));
230
                $this->getEventDispatcher()->dispatch(DiscoverEvent::EVENT_DISCOVER_ERROR, $event);
231
232
                return $this;
233
            }
234
235 2
            if (null !== $message) {
236
                $event = $this->createDiscoverEvent($message)->setRemote($remote);
237
                $this->getEventDispatcher()->dispatch(DiscoverEvent::EVENT_DISCOVER, $event);
238
            }
239 2
        } while (null !== $message);
240
241 2
        $socket->close();
242
243 2
        return $this;
244
    }
245
246
    /**
247
     * Send update request
248
     *
249
     * @param UpdateOptions $options
250
     *
251
     * @return PromiseInterface
252
     */
253 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...
254
    {
255
        $request = $this->getRequestFactory()->createUpdateRequest($options);
256
        $data = trim(RequestSerializer::toString($request)) . "\r\n\r\n";
257
258
        $socket = $this->getSocketFactory()->createUdp4();
259
        $socket->setOption(SOL_SOCKET, SO_BROADCAST, 1);
260
        $socket->setOption(IPPROTO_IP, IP_MULTICAST_IF, 0);
261
        $socket->setOption(IPPROTO_IP, IP_MULTICAST_LOOP, 0);
262
        $socket->setOption(IPPROTO_IP, IP_MULTICAST_TTL, 4);
263
        $socket->setOption(IPPROTO_IP, MCAST_JOIN_GROUP, ['group' => self::MULTICAST_ADDRESS, 'interface' => 0]);
264
        $socket->bind('0.0.0.0');
265
        $socket->sendTo($data, 0, sprintf('%s:%s', self::MULTICAST_ADDRESS, self::MULTICAST_PORT));
266
267
        return $this;
268
    }
269
270
    /**
271
     * Create discover event
272
     *
273
     * @param string $message
274
     *
275
     * @return DiscoverEvent
276
     */
277
    protected function createDiscoverEvent($message)
278
    {
279
        $response = ResponseSerializer::fromString($message);
280
        $event = new DiscoverEvent();
281
282
        if ($response->hasHeader('CACHE-CONTROL')) {
283
            $value = $response->getHeaderLine('CACHE-CONTROL');
284
            $event->setLifetime(intval(substr($value, strpos($value, '=') + 1)));
285
        }
286
287
        if ($response->hasHeader('DATE')) {
288
            $event->setDate(new \DateTime($response->getHeaderLine('DATE')));
289
        }
290
291
        if ($response->hasHeader('LOCATION')) {
292
            $event->setDescriptionUrl(new Uri($response->getHeaderLine('LOCATION')));
293
        }
294
295
        if ($response->hasHeader('SERVER')) {
296
            $event->setServerString($response->getHeaderLine('SERVER'));
297
        }
298
299
        if ($response->hasHeader('ST')) {
300
            $event->setSearchTargetString($response->getHeaderLine('ST'));
301
        }
302
303
        if ($response->hasHeader('USN')) {
304
            $event->setUniqueServiceNameString($response->getHeaderLine('USN'));
305
        }
306
307
        return $event;
308
    }
309
}
310