Completed
Push — m/note-default-schema ( 4c069f...201d93 )
by
unknown
16:20 queued 11:11
created

Client::setDefaultSchema()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * Licensed to CRATE Technology GmbH("Crate") under one or more contributor
4
 * license agreements.  See the NOTICE file distributed with this work for
5
 * additional information regarding copyright ownership.  Crate licenses
6
 * this file to you under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.  You may
8
 * obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
15
 * License for the specific language governing permissions and limitations
16
 * under the License.
17
 *
18
 * However, if you have executed another commercial license agreement
19
 * with Crate these terms will supersede the license and you may use the
20
 * software solely pursuant to the terms of the relevant commercial agreement.
21
 */
22
23
namespace Crate\PDO\Http;
24
25
use Crate\PDO\Exception\RuntimeException;
26
use Crate\PDO\Exception\UnsupportedException;
27
use Crate\Stdlib\Collection;
28
use GuzzleHttp\Exception\BadResponseException;
29
use GuzzleHttp\Exception\ConnectException;
30
use GuzzleHttp\Exception\ParseException;
31
32
class Client implements ClientInterface
33
{
34
35
    const DEFAULT_SERVER = "localhost:4200";
36
37
    /**
38
     * @var array
39
     */
40
    private $availableServers = [];
41
42
    /**
43
     * @var array
44
     */
45
    private $serverPool = [];
46
47
    /**
48
     * Client constructor.
49
     * @param array $servers
50
     * @param array $options
51
     */
52 1
    public function __construct(array $servers, array $options)
53
    {
54 1
        if ($servers == null || count($servers) == 0) {
55
            $this->serverPool[self::DEFAULT_SERVER] = new Server(self::DEFAULT_SERVER, $options);
56
            $this->availableServers[] = self::DEFAULT_SERVER;
57
        } else {
58 1
            foreach ($servers as &$server) {
59 1
                $this->serverPool[$server] = new Server($server, $options);
60 1
                $this->availableServers[] = $server;
61 1
            }
62
        }
63 1
    }
64
65
    /**
66
     * {@Inheritdoc}
67
     */
68 2
    public function execute($query, array $parameters)
69
    {
70
        $body = [
71 2
            'stmt' => $query,
72
            'args' => $parameters
73 2
        ];
74
75 2
        while (true) {
76 2
            $nextServer = $this->nextServer();
77
            /**
78
             * @var Server $s
79
             */
80 2
            $s = $this->serverPool[$nextServer];
81
82
            try {
83
84 2
                $response = $s->doRequest($body);
85 1
                $responseBody = $response->json();
86
87 1
                return new Collection(
88 1
                    $responseBody['rows'],
89 1
                    $responseBody['cols'],
90 1
                    $responseBody['duration'],
91 1
                    $responseBody['rowcount']
92 1
                );
93
94 1
            } catch (ConnectException $exception) {
95
                // drop the server from the list of available servers
96
                $this->dropServer($nextServer);
97
                // break the loop if no more servers are available
98
                $this->raiseIfNoMoreServers($exception);
99 1
            } catch (BadResponseException $exception) {
100
101
                try {
102
103 1
                    $json = $exception->getResponse()->json();
104
105 1
                    $errorCode    = $json['error']['code'];
106 1
                    $errorMessage = $json['error']['message'];
107
108 1
                    throw new RuntimeException($errorMessage, $errorCode, $exception);
109
110 1
                } catch (ParseException $e) {
111
                    throw new RuntimeException('Server returned non-JSON response.', 0, $exception);
112
                }
113
114
            }
115
        }
116
        return null;
117
    }
118
119
    /**
120
     * {@Inheritdoc}
121
     */
122 1
    public function getServerInfo()
123
    {
124 1
        throw new UnsupportedException('Not yet implemented');
125
    }
126
127
    /**
128
     * {@Inheritdoc}
129
     */
130 1
    public function getServerVersion()
131
    {
132 1
        throw new UnsupportedException('Not yet implemented');
133
    }
134
135
    /**
136
     * {@Inheritdoc}
137
     */
138 1
    public function setTimeout($timeout)
139
    {
140 1
        foreach ($this->serverPool as $k => &$s) {
141
            /**
142
             * @var $s Server
143
             */
144 1
            $s->setTimeout($timeout);
145 1
        }
146 1
    }
147
148
    /**
149
     * {@Inheritdoc}
150
     */
151
    public function setHttpBasicAuth($username, $passwd)
152
    {
153
        foreach ($this->serverPool as $k => &$s) {
154
            /**
155
             * @var $s Server
156
             */
157
            $s->setHttpBasicAuth($username, $passwd);
158
        }
159
    }
160
161
    /**
162
     * {@Inheritdoc}
163
     */
164
    public function setHttpHeader($name, $value)
165
    {
166
        foreach ($this->serverPool as $k => &$s) {
167
            /**
168
             * @var $s Server
169
             */
170
            $s->setHttpHeader($name, $value);
171
        }
172
    }
173
174
    /**
175
     * {@Inheritdoc}
176
     */
177 1
    public function setDefaultSchema($schemaName)
178
    {
179 1
        $this->setHttpHeader("Default-Schema", $schemaName);
180 1
    }
181
182
    /**
183
     * @return string The next available server instance
184
     */
185 3
    private function nextServer()
186
    {
187 3
        $server = $this->availableServers[0];
188 3
        $this->roundRobin();
189 3
        return $server;
190
    }
191
192
    /**
193
     * Very simple round-robin implementation
194
     * Pops the first item of the availableServers array and appends it at the end.
195
     *
196
     * @return void
197
     */
198 3
    private function roundRobin()
199
    {
200
        /**
201
         * Performing round robin on the array only makes sense
202
         * if there are more than 1 available servers.
203
         */
204 3
        if (count($this->availableServers) > 1) {
205 1
            $this->availableServers[] = array_shift($this->availableServers);
206 1
        }
207 3
    }
208
209
    /**
210
     * @param string           $server
211
     */
212 2
    private function dropServer($server)
213
    {
214 2
        if (($idx = array_search($server, $this->availableServers)) !== false) {
215 2
            unset($this->availableServers[$idx]);
216 2
        }
217 2
    }
218
219
    /**
220
     * @param ConnectException $exception
221
     */
222 1
    private function raiseIfNoMoreServers($exception)
223
    {
224 1
        if (count($this->availableServers) == 0) {
225 1
            throw new ConnectException(
226 1
                "No more servers available, exception from last server: " . $exception->getMessage(),
227 1
                $exception->getRequest(),
228 1
                $exception->getResponse(),
229
                $exception
230 1
            );
231
        }
232
    }
233
}
234