Test Setup Failed
Push — testing ( 4db353...e928dd )
by Roman
02:58
created

Connection::__call()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
namespace lroman242\LaravelCassandra;
4
5
use Cassandra;
6
use Cassandra\BatchStatement;
7
8
class Connection extends \Illuminate\Database\Connection
9
{
10
    const DEFAULT_PAGE_SIZE = 5000;
11
    
12
    /**
13
     * The Cassandra keyspace
14
     *
15
     * @var string
16
     */
17
    protected $keyspace;
18
19
    /**
20
     * The Cassandra cluster
21
     *
22
     * @var \Cassandra\Cluster
23
     */
24
    protected $cluster;
25
26
    /**
27
     * The Cassandra connection handler.
28
     *
29
     * @var \Cassandra\Session
30
     */
31
    protected $session;
32
33
    /**
34
     * The config
35
     *
36
     * @var array
37
     */
38
    protected $config;
39
40
    /**
41
     * Create a new database connection instance.
42
     *
43
     * @param  array   $config
44
     */
45
    public function __construct(array $config)
46
    {
47
        $this->config = $config;
48
        if (empty($this->config['page_size'])) {
49
            $this->config['page_size'] = self::DEFAULT_PAGE_SIZE;
50
        }
51
52
        // You can pass options directly to the Cassandra constructor
53
        $options = array_get($config, 'options', []);
54
55
        // Create the connection
56
        $this->cluster = $this->createCluster($config, $options);
57
58
        if (isset($options['database']) || isset($config['keyspace'])) {
59
            $keyspaceName = isset($options['database']) ? $options['database'] : $config['keyspace'];
60
61
            $this->keyspace = $keyspaceName;
62
            $this->session = $this->cluster->connect($keyspaceName);
63
        }
64
65
        $this->useDefaultPostProcessor();
66
67
        $this->useDefaultSchemaGrammar();
68
69
        $this->setQueryGrammar($this->getDefaultQueryGrammar());
70
    }
71
72
    /**
73
     * Begin a fluent query against a database table.
74
     *
75
     * @param  string  $table
76
     * @return Query\Builder
77
     */
78
    public function table($table)
79
    {
80
        $processor = $this->getPostProcessor();
81
82
        $query = new Query\Builder($this, null, $processor);
0 ignored issues
show
Documentation introduced by
$processor is of type object<Illuminate\Databa...y\Processors\Processor>, but the function expects a null|object<lroman242\La...sandra\Query\Processor>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
83
84
        return $query->from($table);
85
    }
86
87
    /**
88
     * return Cassandra cluster.
89
     *
90
     * @return \Cassandra\Cluster
91
     */
92
    public function getCassandraCluster()
93
    {
94
        return $this->cluster;
95
    }
96
97
    /**
98
     * return Cassandra Session.
99
     *
100
     * @return \Cassandra\Session
101
     */
102
    public function getCassandraSession()
103
    {
104
        return $this->session;
105
    }
106
107
    /**
108
     * Return the Cassandra keyspace
109
     *
110
     * @return string
111
     */
112
    public function getKeyspace()
113
    {
114
        return $this->keyspace;
115
    }
116
117
    /**
118
     * Create a new Cassandra cluster object.
119
     *
120
     * @param  array   $config
121
     * @param  array   $options
122
     * @return \Cassandra\Cluster
123
     */
124
    protected function createCluster(array $config, array $options)
125
    {
126
        $cluster = Cassandra::cluster();
127
128
        // Check if the credentials are not already set in the options
129 View Code Duplication
        if (!isset($options['username']) && !empty($config['username'])) {
130
            $options['username'] = $config['username'];
131
        }
132 View Code Duplication
        if (!isset($options['password']) && !empty($config['password'])) {
133
            $options['password'] = $config['password'];
134
        }
135
136
        // Authentication
137
        if (isset($options['username']) && isset($options['password'])) {
138
            $cluster->withCredentials($options['username'], $options['password']);
139
        }
140
141
        // Contact Points/Host
142
        if (isset($options['contactpoints']) || (isset($config['host']) && !empty($config['host']))) {
143
            $contactPoints = $config['host'];
144
145
            if (isset($options['contactpoints'])) {
146
                $contactPoints = $options['contactpoints'];
147
            }
148
149
            if (is_array($contactPoints)) {
150
                $contactPoints = implode(',', $contactPoints);
151
            }
152
153
            $contactPoints = !empty($contactPoints) ? $contactPoints : '127.0.0.1';
154
155
            $cluster->withContactPoints($contactPoints);
156
        }
157
158
        if (!isset($options['port']) && !empty($config['port'])) {
159
            $cluster->withPort((int) $config['port']);
160
        }
161
162
        if (array_key_exists('page_size', $config) && !empty($config['page_size'])) {
163
            $cluster->withDefaultPageSize(intval($config['page_size'] ?? self::DEFAULT_PAGE_SIZE));
164
        }
165
166
        if (array_key_exists('consistency', $config) && in_array(strtoupper($config['consistency']), [
167
                Cassandra::CONSISTENCY_ANY, Cassandra::CONSISTENCY_ONE, Cassandra::CONSISTENCY_TWO,
168
                Cassandra::CONSISTENCY_THREE, Cassandra::CONSISTENCY_QUORUM, Cassandra::CONSISTENCY_ALL,
169
                Cassandra::CONSISTENCY_SERIAL, Cassandra::CONSISTENCY_QUORUM, Cassandra::CONSISTENCY_LOCAL_QUORUM,
170
                Cassandra::CONSISTENCY_EACH_QUORUM, Cassandra::CONSISTENCY_LOCAL_SERIAL, Cassandra::CONSISTENCY_LOCAL_ONE,
171
            ])) {
172
            $cluster->withDefaultConsistency($config['consistency']);
173
        }
174
175
        if (array_key_exists('timeout', $config) && !empty($config['timeout'])) {
176
            $cluster->withDefaultTimeout(intval($config['timeout']));
177
        }
178
179
        if (array_key_exists('connect_timeout', $config) && !empty($config['connect_timeout'])) {
180
            $cluster->withConnectTimeout(floatval($config['connect_timeout']));
181
        }
182
183
        if (array_key_exists('request_timeout', $config) && !empty($config['request_timeout'])) {
184
            $cluster->withRequestTimeout(floatval($config['request_timeout']));
185
        }
186
187
        return $cluster->build();
188
    }
189
190
    /**
191
     * Disconnect from the underlying Cassandra connection.
192
     */
193
    public function disconnect()
194
    {
195
        unset($this->connection);
196
    }
197
198
    /**
199
     * Get the PDO driver name.
200
     *
201
     * @return string
202
     */
203
    public function getDriverName()
204
    {
205
        return 'cassandra';
206
    }
207
208
    /**
209
     * Run a select statement against the database.
210
     *
211
     * @param  string  $query
212
     * @param  array  $bindings
213
     * @param  bool  $useReadPdo
214
     * @param  array  $customOptions
215
     *
216
     * @return array
217
     */
218
    public function select($query, $bindings = [], $useReadPdo = true, array $customOptions = [])
219
    {
220
        return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo, $customOptions) {
221
            if ($this->pretending()) {
222
                return [];
223
            }
224
225
            $preparedStatement = $this->session->prepare($query);
226
227
            //Add bindings
228
            $customOptions['arguments'] = $bindings;
229
230
            return $this->session->execute($preparedStatement, $customOptions);
231
        });
232
    }
233
234
    /**
235
     * Run an bulk insert statement against the database.
236
     *
237
     * @param  array  $queries
238
     * @param  array  $bindings
239
     * @param  int  $type
240
     * @param  array  $customOptions
241
     *
242
     * @return bool
243
     */
244
    public function insertBulk($queries = [], $bindings = [], $type = Cassandra::BATCH_LOGGED, array $customOptions = [])
245
    {
246
        return $this->batchStatement($queries, $bindings, $type, $customOptions);
247
    }
248
249
    /**
250
     * Execute a group of queries inside a batch statement against the database.
251
     *
252
     * @param  array  $queries
253
     * @param  array  $bindings
254
     * @param  int  $type
255
     * @param  array  $customOptions
256
     *
257
     * @return bool
258
     */
259
    public function batchStatement($queries = [], $bindings = [], $type = Cassandra::BATCH_LOGGED, array $customOptions = [])
260
    {
261
        return $this->run($queries, $bindings, function ($queries, $bindings) use ($type, $customOptions) {
0 ignored issues
show
Documentation introduced by
$queries is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
262
            if ($this->pretending()) {
263
                return [];
264
            }
265
266
            $batch = new BatchStatement($type);
267
268
            foreach ($queries as $k => $query) {
269
                $preparedStatement = $this->session->prepare($query);
270
                $batch->add($preparedStatement, $bindings[$k]);
271
            }
272
273
            return $this->session->execute($batch, $customOptions);
274
        });
275
    }
276
277
    /**
278
     * Execute an SQL statement and return the boolean result.
279
     *
280
     * @param  string  $query
281
     * @param  array   $bindings
282
     * @param  array  $customOptions
283
     *
284
     * @return bool
285
     */
286 View Code Duplication
    public function statement($query, $bindings = [], array $customOptions = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
287
    {
288
        return $this->run($query, $bindings, function ($query, $bindings) use ($customOptions) {
289
            if ($this->pretending()) {
290
                return [];
291
            }
292
293
            $preparedStatement = $this->session->prepare($query);
294
            //$this->recordsHaveBeenModified();
295
296
            //Add bindings
297
            $customOptions['arguments'] = $bindings;
298
299
            return $this->session->execute($preparedStatement, $customOptions);
300
        });
301
    }
302
303
    /**
304
     * Because Cassandra is an eventually consistent database, it's not possible to obtain
305
     * the affected count for statements so we're just going to return 0, based on the idea
306
     * that if the query fails somehow, an exception will be thrown
307
     *
308
     * @param  string  $query
309
     * @param  array   $bindings
310
     * @param  array  $customOptions
311
     *
312
     * @return int
313
     */
314 View Code Duplication
    public function affectingStatement($query, $bindings = [], array $customOptions = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
315
    {
316
        return $this->run($query, $bindings, function ($query, $bindings) {
317
            if ($this->pretending()) {
318
                return 0;
319
            }
320
321
            $preparedStatement = $this->session->prepare($query);
322
            //$this->recordsHaveBeenModified();
323
324
            //Add bindings
325
            $customOptions['arguments'] = $bindings;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$customOptions was never initialized. Although not strictly required by PHP, it is generally a good practice to add $customOptions = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
326
327
            $this->session->execute($preparedStatement, $customOptions);
328
329
            return 1;
330
        });
331
    }
332
333
    /**
334
     * @inheritdoc
335
     */
336
    protected function getDefaultPostProcessor()
337
    {
338
        return new Query\Processor();
339
    }
340
341
    /**
342
     * @inheritdoc
343
     */
344
    protected function getDefaultQueryGrammar()
345
    {
346
        return new Query\Grammar();
347
    }
348
349
    /**
350
     * @inheritdoc
351
     */
352
    protected function getDefaultSchemaGrammar()
353
    {
354
        //return new Schema\Grammar();
355
    }
356
357
    /**
358
     * Reconnect to the database if connection is missing.
359
     *
360
     * @return void
361
     */
362
    protected function reconnectIfMissingConnection()
363
    {
364
        if (is_null($this->session)) {
365
            $this->session = $this->createCluster($this->config, [])->connect($this->keyspace);
366
        }
367
    }
368
369
    /**
370
     * Dynamically pass methods to the connection.
371
     *
372
     * @param  string  $method
373
     * @param  array   $parameters
374
     * @return mixed
375
     */
376
    public function __call($method, $parameters)
377
    {
378
        return call_user_func_array([$this->cluster, $method], $parameters);
379
    }
380
}
381