Completed
Pull Request — master (#37)
by Eugene
03:07
created

StreamConnection   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 83
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 97.37%

Importance

Changes 0
Metric Value
wmc 12
lcom 1
cbo 3
dl 0
loc 83
ccs 37
cts 38
cp 0.9737
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 2
A open() 0 24 2
A close() 0 7 2
A isClosed() 0 4 1
A send() 0 11 2
A read() 0 9 3
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Tarantool Client package.
7
 *
8
 * (c) Eugene Leonovich <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Tarantool\Client\Connection;
15
16
use Tarantool\Client\Exception\ConnectionException;
17
use Tarantool\Client\IProto;
18
use Tarantool\Client\Packer\PackUtils;
19
20
final class StreamConnection implements Connection
21
{
22
    private const DEFAULT_URI = 'tcp://127.0.0.1:3301';
23
24
    private $uri;
25
26
    private $options = [
27
        'connect_timeout' => 5,
28
        'socket_timeout' => 5,
29
        'tcp_nodelay' => true,
30
    ];
31
32
    private $stream;
33
34 126
    public function __construct(string $uri = self::DEFAULT_URI, array $options = [])
35
    {
36 126
        $this->uri = $uri;
37
38 126
        if ($options) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
39 4
            $this->options = $options + $this->options;
40
        }
41 126
    }
42
43 126
    public function open() : string
44
    {
45 126
        $this->close();
46
47 126
        $stream = @\stream_socket_client(
48 126
            $this->uri,
49 126
            $errorCode,
50 126
            $errorMessage,
51 126
            (float) $this->options['connect_timeout'],
52 126
            \STREAM_CLIENT_CONNECT,
53 126
            \stream_context_create(['socket' => ['tcp_nodelay' => (bool) $this->options['tcp_nodelay']]])
54
        );
55
56 126
        if (false === $stream) {
57 3
            throw new ConnectionException(\sprintf('Unable to connect to %s: %s.', $this->uri, $errorMessage));
58
        }
59
60 123
        $this->stream = $stream;
61 123
        \stream_set_timeout($this->stream, $this->options['socket_timeout']);
62
63 123
        $greeting = $this->read(IProto::GREETING_SIZE, 'Unable to read greeting.');
64
65 120
        return IProto::parseGreeting($greeting);
66
    }
67
68 126
    public function close() : void
69
    {
70 126
        if ($this->stream) {
71 2
            \fclose($this->stream);
72 2
            $this->stream = null;
73
        }
74 126
    }
75
76 106
    public function isClosed() : bool
77
    {
78 106
        return !\is_resource($this->stream);
79
    }
80
81 105
    public function send(string $data) : string
82
    {
83 105
        if (!\fwrite($this->stream, $data)) {
84
            throw new ConnectionException('Unable to write request.');
85
        }
86
87 105
        $length = $this->read(IProto::LENGTH_SIZE, 'Unable to read response length.');
88 103
        $length = PackUtils::unpackLength($length);
89
90 103
        return $this->read($length, 'Unable to read response.');
91
    }
92
93 123
    private function read(int $length, string $errorMessage) : string
94
    {
95 123
        if ($data = \stream_get_contents($this->stream, $length)) {
96 120
            return $data;
97
        }
98
99 7
        $meta = \stream_get_meta_data($this->stream);
100 7
        throw new ConnectionException($meta['timed_out'] ? 'Read timed out.' : $errorMessage);
101
    }
102
}
103