Completed
Pull Request — master (#100)
by Maxime
03:53
created

WebSocketClient::start()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 0
cts 14
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 10
nc 2
nop 1
crap 12
1
<?php
2
3
/**
4
 * This file is a part of Woketo package.
5
 *
6
 * (c) Nekland <[email protected]>
7
 *
8
 * For the full license, take a look to the LICENSE file
9
 * on the root directory of this project
10
 */
11
12
namespace Nekland\Woketo\Client;
13
14
use Nekland\Woketo\Http\Url;
15
use Nekland\Woketo\Message\MessageHandlerInterface;
16
use Nekland\Woketo\Rfc6455\MessageHandler\CloseFrameHandler;
17
use Nekland\Woketo\Rfc6455\MessageHandler\PingFrameHandler;
18
use Nekland\Woketo\Rfc6455\MessageHandler\RsvCheckFrameHandler;
19
use Nekland\Woketo\Rfc6455\MessageHandler\WrongOpcodeHandler;
20
use Nekland\Woketo\Rfc6455\MessageProcessor;
21
use React\EventLoop\Factory as LoopFactory;
22
use React\EventLoop\LoopInterface;
23
24
class WebSocketClient
25
{
26
    /**
27
     * @var Url
28
     */
29
    private $url;
30
31
    /**
32
     * @var array
33
     */
34
    private $config;
35
36
    /**
37
     * @var Connection
38
     */
39
    private $connection;
40
41
    /**
42
     * @var ConnectorFactoryInterface
43
     */
44
    private $connectorFactory;
45
46
    /**
47
     * @var LoopInterface
48
     */
49
    private $loop;
50
51
    /**
52
     * @var MessageProcessor
53
     */
54
    private $messageProcessor;
55
56
    public function __construct(string $url, array $config = [], ConnectorFactoryInterface $connectorFactory = null)
57
    {
58
        $this->url = new Url($url);
59
        $this->connectorFactory = $connectorFactory;
60
        $this->setConfig($config);
61
    }
62
63
    /**
64
     * @param MessageHandlerInterface $handler
65
     * @throws \Exception
66
     */
67
    public function start(MessageHandlerInterface $handler)
68
    {
69
        if ($this->config['prod'] && \extension_loaded('xdebug')) {
70
            throw new \Exception('xdebug is enabled, it\'s a performance issue. Disable that extension or specify "prod" option to false.');
71
        }
72
73
        $this->connection = new Connection(
74
            $this->url,
75
            $this->getConnectorFactory()->createConnector()->connect($this->url->getHost() . ':' . $this->url->getPort()),
76
            $this->getMessageProcessor(),
77
            $handler,
78
            $this->loop
79
        );
80
81
        $this->loop->run();
82
    }
83
84
    /**
85
     * @param array $config
86
     * @return self
87
     */
88
    public function setConfig(array $config = [])
89
    {
90
        $this->config = array_merge([
91
            'prod' => true,
92
            'ssl' => [],
93
            'dns' => null,
94
        ], $config);
95
96
        return $this;
97
    }
98
99
    /**
100
     * Creates a connector factory with the given configuration if none given in the constructor.
101
     *
102
     * @return ConnectorFactoryInterface
103
     */
104
    private function getConnectorFactory() : ConnectorFactoryInterface
105
    {
106
        if ($this->connectorFactory !== null) {
107
            return $this->connectorFactory;
108
        }
109
        $this->connectorFactory = new ConnectorFactory($this->getLoop());
110
        $this->connectorFactory->setSslOptions($this->config['ssl']);
111
112
        $this->connectorFactory->enableDns();
113
        if (null !== $this->config['dns']) {
114
            $this->connectorFactory->setDnsServer($this->config['dns']);
115
        }
116
        if ($this->url->isSecured()) {
117
            $this->connectorFactory->enableSsl();
118
        }
119
120
        return $this->connectorFactory;
121
    }
122
123
    /**
124
     * @return LoopInterface
125
     */
126
    private function getLoop() : LoopInterface
127
    {
128
        if (null !== $this->loop) {
129
            return $this->loop;
130
        }
131
132
        return $this->loop = LoopFactory::create();
133
    }
134
135
    /**
136
     * @return MessageProcessor
137
     */
138
    private function getMessageProcessor(): MessageProcessor
139
    {
140
        if (!empty($this->messageProcessor)) {
141
            return $this->messageProcessor;
142
        }
143
144
        $this->messageProcessor = new MessageProcessor(true);
145
146
        $this->messageProcessor->addHandler(new PingFrameHandler());
147
        $this->messageProcessor->addHandler(new CloseFrameHandler());
148
        $this->messageProcessor->addHandler(new WrongOpcodeHandler());
149
        $this->messageProcessor->addHandler(new RsvCheckFrameHandler());
150
151
        return $this->messageProcessor;
152
    }
153
}
154