Passed
Push — master ( 241b1a...3e7e5a )
by Igor
02:54 queued 18s
created

TcpTransport::getSocketClient()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2
1
<?php
2
/*
3
 * This file is part of JSON RPC Client.
4
 *
5
 * (c) Igor Lazarev <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Strider2038\JsonRpcClient\Transport;
12
13
use Strider2038\JsonRpcClient\Exception\ConnectionFailedException;
14
use Strider2038\JsonRpcClient\Exception\InvalidConfigException;
15
use Strider2038\JsonRpcClient\Exception\RemoteProcedureCallFailedException;
16
17
/**
18
 * @author Igor Lazarev <[email protected]>
19
 */
20
class TcpTransport implements TransportInterface
21
{
22
    /** @var string */
23
    private $url;
24
25
    /** @var int */
26
    private $timeoutMs;
27
28
    /** @var resource|null */
29
    private $client;
30
31
    /**
32
     * @param string $url
33
     * @param int    $timeoutMs
34
     *
35
     * @throws InvalidConfigException
36
     */
37 9
    public function __construct(string $url, int $timeoutMs = 1000)
38
    {
39 9
        $this->validateUrl($url);
40
41 6
        $this->url = $url;
42 6
        $this->timeoutMs = $timeoutMs;
43 6
    }
44
45 6
    public function __destruct()
46
    {
47 6
        if (is_resource($this->client)) {
48 5
            fclose($this->client);
49
        }
50 6
    }
51
52 5
    public function send(string $request): string
53
    {
54 5
        $client = $this->getSocketClient();
55
56 5
        fwrite($client, $request);
57 5
        fwrite($client, "\n");
58 5
        fflush($client);
59
60 5
        $response = fgets($client);
61
62 5
        $this->validateMetaData($client);
63
64 4
        return $response;
65
    }
66
67
    /**
68
     * @param string $url
69
     *
70
     * @throws InvalidConfigException
71
     */
72 9
    private function validateUrl(string $url): void
73
    {
74 9
        if (false === filter_var($url, FILTER_VALIDATE_URL)) {
75 2
            throw new InvalidConfigException(
76 2
                sprintf('Valid URL is expected for TCP/IP transport. Given value is "%s".', $url)
77
            );
78
        }
79
80 7
        if ('tcp' !== parse_url($url, PHP_URL_SCHEME)) {
81 1
            throw new InvalidConfigException(
82 1
                sprintf('URL for TCP/IP transport must start with "tcp://" scheme. Given value is "%s".', $url)
83
            );
84
        }
85 6
    }
86
87
    /**
88
     * @return resource
89
     *
90
     * @throws ConnectionFailedException
91
     */
92 5
    private function getSocketClient()
93
    {
94 5
        if (null === $this->client) {
95 5
            $this->client = $this->createSocketClient();
96
        }
97
98 5
        return $this->client;
99
    }
100
101
    /**
102
     * @return resource
103
     *
104
     * @throws ConnectionFailedException
105
     */
106 5
    private function createSocketClient()
107
    {
108 5
        $client = stream_socket_client($this->url, $errno, $errstr, ((float) $this->timeoutMs) / 1000);
109
110 5
        if (!is_resource($client)) {
111
            throw new ConnectionFailedException($this->url, sprintf('%d: %s', $errno, $errstr));
112
        }
113
114 5
        if (false === stream_set_timeout($client, 0, $this->timeoutMs * 1000)) {
115
            throw new ConnectionFailedException($this->url, 'cannot set timeout');
116
        }
117
118 5
        return $client;
119
    }
120
121 5
    private function validateMetaData($client): void
122
    {
123 5
        $info = stream_get_meta_data($client);
124
125 5
        if ($info['timed_out']) {
126 1
            $errorMessage = sprintf('JSON RPC request to %s failed by timeout %d ms.', $this->url, $this->timeoutMs);
127
128 1
            throw new RemoteProcedureCallFailedException($errorMessage);
129
        }
130 4
    }
131
}
132