Completed
Branch 0.4-dev (79cc15)
by Evgenij
03:32
created

AbstractSocket::read()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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