ClientBuilder::setUri()   A
last analyzed

Complexity

Conditions 6
Paths 5

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 10
nc 5
nop 1
dl 0
loc 15
rs 9.2222
c 1
b 0
f 0
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\Tests\Integration;
15
16
use Tarantool\Client\Client;
17
use Tarantool\Client\Connection\Connection;
18
use Tarantool\Client\Connection\StreamConnection;
19
use Tarantool\Client\Handler\DefaultHandler;
20
use Tarantool\Client\Handler\MiddlewareHandler;
21
use Tarantool\Client\Middleware\AuthenticationMiddleware;
22
use Tarantool\Client\Middleware\RetryMiddleware;
23
use Tarantool\Client\Packer\Packer;
24
use Tarantool\Client\Packer\PurePacker;
25
26
final class ClientBuilder
27
{
28
    private const DEFAULT_TCP_HOST = '127.0.0.1';
29
    private const DEFAULT_TCP_PORT = 3301;
30
31
    private $packerFactory;
32
    private $uri;
33
    private $options = [];
34
    private $connectionOptions = [];
35
36
    public function setPackerFactory(\Closure $factory) : self
37
    {
38
        $this->packerFactory = $factory;
39
40
        return $this;
41
    }
42
43
    public function setOptions(array $options) : self
44
    {
45
        $this->options = $options;
46
47
        return $this;
48
    }
49
50
    public function setConnectionOptions(array $options) : self
51
    {
52
        $this->connectionOptions = $options;
53
54
        return $this;
55
    }
56
57
    public function isTcpConnection() : bool
58
    {
59
        return 0 === strpos($this->uri, 'tcp:');
60
    }
61
62
    public function setHost(string $host) : self
63
    {
64
        $port = parse_url($this->uri, PHP_URL_PORT);
65
        $this->uri = sprintf('tcp://%s:%d', $host, $port ?: self::DEFAULT_TCP_PORT);
66
67
        return $this;
68
    }
69
70
    public function setPort(int $port) : self
71
    {
72
        $host = parse_url($this->uri, PHP_URL_HOST);
73
        $this->uri = sprintf('tcp://%s:%d', $host ?: self::DEFAULT_TCP_HOST, $port);
74
75
        return $this;
76
    }
77
78
    public function setUri(string $uri) : self
79
    {
80
        if (0 === strpos($uri, '/')) {
81
            $uri = 'unix://'.$uri;
82
        } elseif (0 === strpos($uri, 'unix/:')) {
83
            $uri = 'unix://'.substr($uri, 6);
84
        } elseif (!preg_match('/[\D]/', $uri)) {
85
            $uri = 'tcp://127.0.0.1:'.$uri;
86
        } elseif (0 !== strpos($uri, 'tcp://') && (0 !== strpos($uri, 'unix://'))) {
87
            $uri = 'tcp://'.$uri;
88
        }
89
90
        $this->uri = $uri;
91
92
        return $this;
93
    }
94
95
    public function getUri() : string
96
    {
97
        return $this->uri;
98
    }
99
100
    public function build() : Client
101
    {
102
        $connection = $this->createConnection();
103
        $packer = $this->createPacker();
104
        $handler = new DefaultHandler($connection, $packer);
105
106
        $middlewares = [];
107
        if (isset($this->options['max_retries'])) {
108
            $middlewares[] = RetryMiddleware::linear($this->options['max_retries']);
109
        }
110
        if (isset($this->options['username'])) {
111
            $middlewares[] = new AuthenticationMiddleware($this->options['username'], $this->options['password'] ?? '');
112
        }
113
        if ($middlewares) {
114
            $handler = MiddlewareHandler::append($handler, $middlewares);
115
        }
116
117
        return new Client($handler);
118
    }
119
120
    public static function createFromEnv() : self
121
    {
122
        return (new self())
123
            ->setUri(getenv('TNT_LISTEN_URI'));
124
    }
125
126
    public static function createForFakeServer() : self
127
    {
128
        $builder = self::createFromEnv();
129
130
        if ($builder->isTcpConnection()) {
131
            $builder->setHost('0.0.0.0');
132
            $builder->setPort(self::findAvailableTcpPort(8000));
133
        } else {
134
            $builder->setUri(sprintf('unix://%s/tnt_client_%s.sock', sys_get_temp_dir(), bin2hex(random_bytes(10))));
135
        }
136
137
        return $builder;
138
    }
139
140
    public function createConnection() : Connection
141
    {
142
        if (!$this->uri) {
143
            throw new \LogicException('Connection URI is not set');
144
        }
145
146
        return StreamConnection::create($this->uri, $this->connectionOptions);
147
    }
148
149
    public function createPacker() : Packer
150
    {
151
        return $this->packerFactory ? ($this->packerFactory)() : new PurePacker();
152
    }
153
154
    private static function findAvailableTcpPort(int $min) : int
155
    {
156
        $maxTries = 10;
157
        $try = 0;
158
159
        while (true) {
160
            $port = $min + $try * 500 + random_int(1, 500);
161
            if (!$fp = @stream_socket_client("tcp://127.0.0.1:$port", $errorCode, $errorMessage, 1)) {
162
                return $port;
163
            }
164
165
            fclose($fp);
166
167
            if (++$try === $maxTries) {
168
                throw new \RuntimeException('Failed to find open tcp port');
169
            }
170
        }
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return integer. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
171
    }
172
}
173