Completed
Branch 0.4-dev (f926df)
by Evgenij
04:00
created

AbstractSocket::close()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

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