StreamConnection::open()   B
last analyzed

Complexity

Conditions 7
Paths 13

Size

Total Lines 42
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 28
c 0
b 0
f 0
nc 13
nop 0
dl 0
loc 42
ccs 30
cts 30
cp 1
crap 7
rs 8.5386
1
<?php
2
3
/**
4
 * This file is part of the tarantool/client package.
5
 *
6
 * (c) Eugene Leonovich <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Tarantool\Client\Connection;
15
16
use Tarantool\Client\Exception\CommunicationFailed;
17
use Tarantool\Client\Exception\ConnectionFailed;
18
use Tarantool\Client\Packer\PacketLength;
19
20
final class StreamConnection implements Connection
21
{
22
    public const DEFAULT_TCP_URI = 'tcp://127.0.0.1:3301';
23
24
    /** @var string */
25
    private $uri;
26
27
    /** @var float */
28
    private $connectTimeout;
29
30
    /** @var float */
31
    private $socketTimeout;
32
33
    /** @var bool */
34
    private $persistent;
35
36
    /** @var resource|null */
37
    private $streamContext;
38
39
    /** @var resource|null */
40
    private $stream;
41
42
    /** @var Greeting|null */
43
    private $greeting;
44
45
    /**
46
     * @param string $uri
47
     */
48 999
    private function __construct($uri, float $connectTimeout, float $socketTimeout, bool $persistent, bool $tcpNoDelay)
49
    {
50 999
        $this->uri = $uri;
51 999
        $this->connectTimeout = $connectTimeout;
52 999
        $this->socketTimeout = $socketTimeout;
53 999
        $this->persistent = $persistent;
54
55 999
        if ($tcpNoDelay) {
56 19
            $this->streamContext = \stream_context_create(['socket' => ['tcp_nodelay' => true]]);
57
        }
58
    }
59
60 838
    public static function createTcp(string $uri = self::DEFAULT_TCP_URI, array $options = []) : self
61
    {
62 838
        return new self($uri,
63 838
            $options['connect_timeout'] ?? 5.0,
64 838
            $options['socket_timeout'] ?? 5.0,
65 838
            $options['persistent'] ?? false,
66 838
            $options['tcp_nodelay'] ?? false
67 838
        );
68
    }
69
70 251
    public static function createUds(string $uri, array $options = []) : self
71
    {
72 251
        return new self($uri,
73 251
            $options['connect_timeout'] ?? 5.0,
74 251
            $options['socket_timeout'] ?? 5.0,
75 251
            $options['persistent'] ?? false,
76 251
            false
77 251
        );
78
    }
79
80 823
    public static function create(string $uri, array $options = []) : self
81
    {
82 823
        return 0 === \strpos($uri, 'unix://')
83 207
            ? self::createUds($uri, $options)
84 823
            : self::createTcp($uri, $options);
85
    }
86
87 740
    public function open() : Greeting
88
    {
89 740
        if ($this->greeting) {
90 450
            return $this->greeting;
91
        }
92
93 740
        $flags = $this->persistent
94 8
            ? \STREAM_CLIENT_CONNECT | \STREAM_CLIENT_PERSISTENT
95 740
            : \STREAM_CLIENT_CONNECT;
96
97 740
        $stream = $this->streamContext ? @\stream_socket_client(
98 740
            $this->uri,
99 740
            $errorCode,
100 740
            $errorMessage,
101 740
            $this->connectTimeout,
102 740
            $flags,
103 740
            $this->streamContext
104 740
        ) : @\stream_socket_client(
105 740
            $this->uri,
106 740
            $errorCode,
107 740
            $errorMessage,
108 740
            $this->connectTimeout,
109 740
            $flags
110 740
        );
111
112 740
        if (false === $stream) {
113 11
            throw ConnectionFailed::fromUriAndReason($this->uri, $errorMessage);
114
        }
115
116 729
        $socketTimeoutSeconds = (int) $this->socketTimeout;
117 729
        $socketTimeoutMicroSeconds = (int) (($this->socketTimeout - $socketTimeoutSeconds) * 1000000);
118 729
        \stream_set_timeout($stream, $socketTimeoutSeconds, $socketTimeoutMicroSeconds);
119
120 729
        $this->stream = $stream;
121
122 729
        if ($this->persistent && \ftell($stream)) {
123 4
            return $this->greeting = Greeting::unknown();
124
        }
125
126 729
        $greeting = $this->read(Greeting::SIZE_BYTES, 'Error reading greeting');
127
128 721
        return $this->greeting = Greeting::parse($greeting);
129
    }
130
131 60
    public function close() : void
132
    {
133 60
        if ($this->stream) {
134
            /** @psalm-suppress InvalidPropertyAssignmentValue */
135 56
            \fclose($this->stream);
136
        }
137
138 60
        $this->stream = null;
139 60
        $this->greeting = null;
140
    }
141
142 8
    public function isClosed() : bool
143
    {
144 8
        return !$this->stream;
145
    }
146
147 645
    public function send(string $data) : string
148
    {
149 645
        if (!$this->stream) {
150
            throw new CommunicationFailed('Error writing request: connection closed');
151
        }
152
153 645
        if (!\fwrite($this->stream, $data)) {
154 8
            throw CommunicationFailed::withLastPhpError('Error writing request');
155
        }
156
157 645
        $length = $this->read(PacketLength::SIZE_BYTES, 'Error reading response length');
158 633
        $length = PacketLength::unpack($length);
159
160 633
        return $this->read($length, 'Error reading response');
161
    }
162
163 729
    private function read(int $length, string $errorMessage) : string
164
    {
165
        /** @psalm-suppress PossiblyNullArgument */
166 729
        if ($data = \stream_get_contents($this->stream, $length)) {
167 721
            return $data;
168
        }
169
170
        /** @psalm-suppress PossiblyNullArgument */
171 28
        $meta = \stream_get_meta_data($this->stream);
172 28
        if ($meta['timed_out']) {
173 8
            throw new CommunicationFailed('Read timed out');
174
        }
175
176 20
        throw CommunicationFailed::withLastPhpError($errorMessage);
177
    }
178
}
179