Passed
Push — master ( 943e24...2888fe )
by Mikael
03:26
created

Database   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 395
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 40.52%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 30
c 1
b 0
f 0
lcom 1
cbo 1
dl 0
loc 395
ccs 47
cts 116
cp 0.4052
rs 10

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A setOptions() 0 15 1
B connect() 0 25 4
B expandParamArray() 0 27 4
A executeFetchAll() 0 5 1
A executeFetch() 0 5 1
A executeFetchClass() 0 6 1
A executeFetchInto() 0 10 2
A fetchAll() 0 4 1
A fetch() 0 4 1
A fetchClass() 0 5 1
A fetchInto() 0 5 1
A execute() 0 16 3
A createException() 0 16 2
A pdoException() 0 4 1
A statementException() 0 4 1
A lastInsertId() 0 4 1
A rowCount() 0 4 1
A fetchObject() 0 4 1
A fetchOne() 0 4 1
1
<?php
2
3
namespace Anax\Database;
4
5
/**
6
 * Database wrapper, provides a database API on top of PHP PDO for
7
 * enhancing the API and dealing with error reporting and tracking.
8
 */
9
class Database
10
{
11
    /**
12
     * @var array        $options used when creating the PDO object
13
     * @var PDO          $pdo     the PDO object
14
     * @var PDOStatement $stmt    the latest PDOStatement used
15
     */
16
    private $options;
17
    private $pdo = null;
18
    private $stmt = null;
19
20
21
22
    /**
23
     * Constructor creating a PDO object connecting to a choosen database.
24
     *
25
     * @param array $options containing details for connecting to the database.
26
     */
27 7
    public function __construct($options = [])
28
    {
29 7
        $this->setOptions($options);
30 7
    }
31
32
33
34
    /**
35
     * Set options and connection details.
36
     *
37
     * @param array $options containing details for connecting to the database.
38
     *
39
     * @return void
40
     */
41 7
    public function setOptions($options = [])
42
    {
43
        $default = [
44 7
            'dsn'             => null,
45 7
            'username'        => null,
46 7
            'password'        => null,
47 7
            'driver_options'  => null,
48 7
            'table_prefix'    => null,
49 7
            'fetch_mode'      => \PDO::FETCH_OBJ,
50 7
            'session_key'     => 'Anax\Database',
51 7
            'verbose'         => null,
52 7
            'debug_connect'   => false,
53 7
        ];
54 7
        $this->options = array_merge($default, $options);
55 7
    }
56
57
58
59
    /**
60
     * Connect to the database.
61
     *
62
     * @return self
63
     *
64
     * @throws \Anax\Database\Exception
65
     */
66 7
    public function connect()
67
    {
68 7
        if (!isset($this->options['dsn'])) {
69 1
            throw new Exception("You can not connect, missing dsn.");
70
        }
71
72
        try {
73 7
            $this->pdo = new \PDO(
74 7
                $this->options['dsn'],
75 7
                $this->options['username'],
76 7
                $this->options['password'],
77 7
                $this->options['driver_options']
78 7
            );
79
80 7
            $this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, $this->options['fetch_mode']);
81 7
            $this->pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
82 7
        } catch (\PDOException $e) {
83 1
            if ($this->options['debug_connect']) {
84 1
                throw $e;
85
            }
86
            throw new Exception("Could not connect to database, hiding connection details.");
87
        }
88
89 7
        return $this;
90
    }
91
92
93
94
    /**
95
     * Support arrays in params, extract array items and add to $params
96
     * and insert ? for each entry in the array.
97
     *
98
     * @param string $query  as the query to prepare.
99
     * @param array  $params the parameters that may contain arrays.
100
     *
101
     * @return array with query and params.
102
     */
103 3
    private function expandParamArray($query, $params)
104
    {
105 3
        $param = [];
106 3
        $offset = -1;
107
108 3
        foreach ($params as $val) {
109 1
            $offset = strpos($query, '?', $offset + 1);
110
111 1
            if (is_array($val)) {
112
                $nrOfItems = count($val);
113
114
                if ($nrOfItems) {
115
                    $query = substr($query, 0, $offset)
116
                        . str_repeat('?,', $nrOfItems  - 1)
117
                        . '?'
118
                        . substr($query, $offset + 1);
119
                    $param = array_merge($param, $val);
120
                } else {
121
                    $param[] = null;
122
                }
123
            } else {
124 1
                $param[] = $val;
125
            }
126 3
        }
127
128 3
        return [$query, $param];
129
    }
130
131
132
133
    /**
134
     * Execute a select-query with arguments and return the resultset.
135
     *
136
     * @param string $query  the SQL statement
137
     * @param array  $params the params array
138
     *
139
     * @return array with resultset
140
     */
141
    public function executeFetchAll($query, $params = [])
142
    {
143
        $this->execute($query, $params);
144
        return $this->stmt->fetchAll();
145
    }
146
147
148
149
    /**
150
     * Execute a select-query with arguments and return the first row
151
     * in the resultset.
152
     *
153
     * @param string $query  the SQL statement
154
     * @param array  $params the params array
155
     *
156
     * @return array with resultset
157
     */
158
    public function executeFetch($query, $params = [])
159
    {
160
        $this->execute($query, $params);
161
        return $this->stmt->fetch();
162
    }
163
164
165
166
    /**
167
     * Execute a select-query with arguments and insert the results into
168
     * a new object of the class.
169
     *
170
     * @param string $query  the SQL statement
171
     * @param array  $params the params array
172
     * @param string $class  the class to create an object of and insert into
173
     *
174
     * @return array with resultset
175
     */
176
    public function executeFetchClass($query, $params, $class)
177
    {
178
        $this->execute($query, $params);
179
        $this->stmt->setFetchMode(\PDO::FETCH_CLASS, $class);
180
        return $this->stmt->fetch();
181
    }
182
183
184
185
    /**
186
     * Execute a select-query with arguments and insert the results into
187
     * an existing object.
188
     *
189
     * @param string $query  the SQL statement
190
     * @param array  $params the params array
0 ignored issues
show
Documentation introduced by
There is no parameter named $params. Did you maybe mean $param?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
191
     * @param string $object the existing object to insert into
192
     *
193
     * @return array with resultset
194
     */
195
    public function executeFetchInto($query, $param, $object = null)
196
    {
197
        if (!$object) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $object of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
198
            $object = $param;
199
            $param = [];
200
        }
201
        $this->execute($query, $param);
202
        $this->stmt->setFetchMode(\PDO::FETCH_INTO, $object);
203
        return $this->stmt->fetch();
204
    }
205
206
207
208
    /**
209
     * Fetch all resultset.
210
     *
211
     * @return array with resultset.
212
     */
213
    public function fetchAll()
214
    {
215
        return $this->stmt->fetchAll();
216
    }
217
218
219
220
    /**
221
     * Fetch one resultset.
222
     *
223
     * @return array with resultset.
224
     */
225
    public function fetch()
226
    {
227
        return $this->stmt->fetch();
228
    }
229
230
231
232
    /**
233
     * Fetch one resultset as a new object from this class.
234
     *
235
     * @param object $class which type of object to instantiate.
236
     *
237
     * @return array with resultset.
238
     */
239
    public function fetchClass($class)
240
    {
241
        $this->stmt->setFetchMode(\PDO::FETCH_CLASS, $class);
242
        return $this->stmt->fetch();
243
    }
244
245
246
247
    /**
248
     * Fetch one resultset into an object.
249
     *
250
     * @param object $object to insert values into.
251
     *
252
     * @return array with resultset.
253
     */
254
    public function fetchInto($object)
255
    {
256
        $this->stmt->setFetchMode(\PDO::FETCH_INTO, $object);
257
        return $this->stmt->fetch();
258
    }
259
260
261
262
    /**
263
     * Execute a SQL-query and ignore the resultset.
264
     *
265
     * @param string $query  the SQL statement
266
     * @param array  $params the params array
267
     *
268
     * @return boolean returns TRUE on success or FALSE on failure.
269
     *
270
     * @throws Exception when failing to prepare question.
271
     */
272 3
    public function execute($query, $params = [])
273
    {
274 3
        list($query, $params) = $this->expandParamArray($query, $params);
275
276 3
        $this->stmt = $this->pdo->prepare($query);
277 3
        if (!$this->stmt) {
278
            $this->pdoException($query, $params);
279
        }
280
281 3
        $res = $this->stmt->execute($params);
282 3
        if (!$res) {
283
            $this->statementException($query, $params);
284
        }
285
286 3
        return $res;
287
    }
288
289
290
291
    /**
292
     * Throw exception using detailed message.
293
     *
294
     * @param string       $msg     detailed error message from PDO
295
     * @param string       $query   query to execute
296
     * @param array        $param   to match ? in statement
297
     *
298
     * @return void
299
     *
300
     * @throws \Anax\Database\Exception
301
     */
302
    protected function createException($msg, $query, $param)
303
    {
304
        throw new Exception(
305
            $msg
306
            . "<br><br>SQL ("
307
            . substr_count($query, "?")
308
            . " params):<br><pre>$query</pre><br>PARAMS ("
309
            . count($param)
310
            . "):<br><pre>"
311
            . implode($param, "\n")
312
            . "</pre>"
313
            . ((count(array_filter(array_keys($param), 'is_string')) > 0)
314
                ? "WARNING your params array has keys, should only have values."
315
                : null)
316
        );
317
    }
318
319
320
321
    /**
322
     * Throw exception when pdo failed using detailed message.
323
     *
324
     * @param string       $query   query to execute
325
     * @param array        $param   to match ? in statement
326
     *
327
     * @return void
328
     *
329
     * @throws \Anax\Database\Exception
330
     */
331
    protected function pdoException($query, $param)
332
    {
333
        $this->createException($this->pdo->errorInfo()[2], $query, $param);
334
    }
335
336
337
338
    /**
339
     * Throw exception when statement failed using detailed message.
340
     *
341
     * @param string       $query   query to execute
342
     * @param array        $param   to match ? in statement
343
     *
344
     * @return void
345
     *
346
     * @throws \Anax\Database\Exception
347
     */
348
    protected function statementException($query, $param)
349
    {
350
        $this->createException($this->stmt->errorInfo()[2], $query, $param);
351
    }
352
353
354
355
    /**
356
     * Return last insert id from autoincremented key on INSERT.
357
     *
358
     * @return integer as last insert id.
359
     */
360
    public function lastInsertId()
361
    {
362
        return $this->pdo->lastInsertId();
363
    }
364
365
366
367
    /**
368
    * Return rows affected of last INSERT, UPDATE, DELETE
369
    *
370
    * @return integer as rows affected on last statement
371
    */
372
    public function rowCount()
373
    {
374
        return $this->stmt->rowCount();
375
    }
376
377
378
379
    /**
380
     * Fetch one resultset as an object from this class.
381
     * OBSOLETE replaced by fetchClass
382
     *
383
     * @param object $class which type of object to instantiate.
384
     *
385
     * @return array with resultset.
386
     */
387
    public function fetchObject($class)
388
    {
389
        return $this->stmt->fetchObject($class);
390
    }
391
392
393
394
    /**
395
     * Fetch one resultset. OBSOLETE replace by fetch()
396
     *
397
     * @return array with resultset.
398
     */
399
    public function fetchOne()
400
    {
401
        return $this->stmt->fetch();
402
    }
403
}
404