Completed
Branch master (1d1574)
by Evgenij
18:38
created

AbstractSocket   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 96.72%

Importance

Changes 30
Bugs 1 Features 7
Metric Value
wmc 18
c 30
b 1
f 7
lcom 1
cbo 3
dl 0
loc 192
ccs 59
cts 61
cp 0.9672
rs 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A read() 0 9 2
A write() 0 9 2
A getStreamResource() 0 4 1
A setDisconnectedState() 0 4 1
A __construct() 0 4 1
createSocketResource() 0 1 ?
createIoInterface() 0 1 ?
B open() 0 26 3
A close() 0 10 2
B resolveSocketType() 0 24 4
A __toString() 0 4 2
1
<?php
2
/**
3
 * Async sockets
4
 *
5
 * @copyright Copyright (c) 2015-2016, Efimov Evgenij <[email protected]>
6
 *
7
 * This source file is subject to the MIT license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
11
namespace AsyncSockets\Socket;
12
13
use AsyncSockets\Exception\ConnectionException;
14
use AsyncSockets\Frame\FramePickerInterface;
15
use AsyncSockets\Socket\Io\DisconnectedIo;
16
use AsyncSockets\Socket\Io\IoInterface;
17
18
/**
19
 * Class AbstractSocket
20
 */
21
abstract class AbstractSocket implements SocketInterface
22
{
23
    /**
24
     * Tcp socket type
25
     */
26
    const SOCKET_TYPE_TCP = 'tcp';
27
28
    /**
29
     * Udp socket type
30
     */
31
    const SOCKET_TYPE_UDP = 'udp';
32
33
    /**
34
     * Unix socket type
35
     */
36
    const SOCKET_TYPE_UNIX = 'unix';
37
38
    /**
39
     * Unix datagram socket type
40
     */
41
    const SOCKET_TYPE_UDG = 'udg';
42
43
    /**
44
     * Unknown type of socket
45
     */
46
    const SOCKET_TYPE_UNKNOWN = '';
47
48
    /**
49
     * This socket resource
50
     *
51
     * @var resource
52
     */
53
    private $resource;
54
55
    /**
56
     * I/O interface
57
     *
58
     * @var IoInterface
59
     */
60
    private $ioInterface;
61
62
    /**
63
     * Socket address
64
     *
65
     * @var string
66
     */
67
    private $remoteAddress;
68
69
    /**
70
     * AbstractSocket constructor.
71
     */
72 107
    public function __construct()
73
    {
74 107
        $this->setDisconnectedState();
75 107
    }
76
77
    /**
78
     * Create certain socket resource
79
     *
80
     * @param string   $address Network address to open in form transport://path:port
81
     * @param resource $context Valid stream context created by function stream_context_create or null
82
     *
83
     * @return resource
84
     */
85
    abstract protected function createSocketResource($address, $context);
86
87
    /**
88
     * Create I/O interface for socket
89
     *
90
     * @param string $type Type of this socket, one of SOCKET_TYPE_* consts
91
     * @param string $address Address passed to open method
92
     *
93
     * @return IoInterface
94
     */
95
    abstract protected function createIoInterface($type, $address);
96
97
    /** {@inheritdoc} */
98 65
    public function open($address, $context = null)
99
    {
100 65
        $this->resource = $this->createSocketResource(
101 65
            $address,
102 65
            $context ?: stream_context_get_default()
103 65
        );
104
105 59
        $result = false;
106 59
        if (is_resource($this->resource)) {
107 59
            $result              = true;
108 59
            $this->remoteAddress = $address;
109
110
            // https://bugs.php.net/bug.php?id=51056
111 59
            stream_set_blocking($this->resource, 0);
112
113
            // https://bugs.php.net/bug.php?id=52602
114 59
            stream_set_timeout($this->resource, 0, 0);
115
116 59
            $this->ioInterface = $this->createIoInterface(
117 59
                $this->resolveSocketType(),
118
                $address
119 59
            );
120 55
        }
121
122 55
        return $result;
123
    }
124
125
    /** {@inheritdoc} */
126 12
    public function close()
127
    {
128 12
        if ($this->resource) {
129 8
            $this->setDisconnectedState();
130 8
            stream_socket_shutdown($this->resource, STREAM_SHUT_RDWR);
131 8
            fclose($this->resource);
132 8
            $this->resource      = null;
133 8
            $this->remoteAddress = null;
134 8
        }
135 12
    }
136
137
    /** {@inheritdoc} */
138 31
    public function read(FramePickerInterface $picker)
139
    {
140
        try {
141 31
            return $this->ioInterface->read($picker);
142 10
        } catch (ConnectionException $e) {
143 5
            $this->setDisconnectedState();
144 5
            throw $e;
145
        }
146
    }
147
148
    /** {@inheritdoc} */
149 10
    public function write($data)
150
    {
151
        try {
152 10
            return $this->ioInterface->write($data);
153 10
        } catch (ConnectionException $e) {
154 5
            $this->setDisconnectedState();
155 5
            throw $e;
156
        }
157
    }
158
159
    /** {@inheritdoc} */
160 41
    public function getStreamResource()
161
    {
162 41
        return $this->resource;
163
    }
164
165
    /**
166
     * Get current socket type
167
     *
168
     * @return string One of SOCKET_TYPE_* consts
169
     */
170 59
    private function resolveSocketType()
171
    {
172 59
        $info = stream_get_meta_data($this->resource);
173 59
        if (!isset($info['stream_type'])) {
174 4
            return self::SOCKET_TYPE_UNKNOWN;
175
        }
176
177 55
        $parts = explode('/', $info['stream_type']);
178
        $map   = [
179 55
            'tcp'  => self::SOCKET_TYPE_TCP,
180 55
            'udp'  => self::SOCKET_TYPE_UDP,
181 55
            'udg'  => self::SOCKET_TYPE_UDG,
182 55
            'unix' => self::SOCKET_TYPE_UNIX,
183 55
        ];
184
185 55
        $regexp = '#^('. implode('|', array_keys($map)) . ')_socket$#';
186 55
        foreach ($parts as $part) {
187 55
            if (preg_match($regexp, $part, $pockets)) {
188 41
                return $map[$pockets[1]];
189
            }
190 14
        }
191
192 14
        return self::SOCKET_TYPE_UNKNOWN;
193
    }
194
195
    /**
196
     * Set disconnected state for socket
197
     *
198
     * @return void
199
     */
200 107
    private function setDisconnectedState()
201
    {
202 107
        $this->ioInterface = new DisconnectedIo($this);
203 107
    }
204
205
    /**
206
     * @inheritDoc
207
     */
208
    public function __toString()
209
    {
210
        return $this->remoteAddress ?: '"closed socket"';
211
    }
212
}
213