Passed
Push — master ( 76fde7...d6d75c )
by Magnus
01:55
created

Database::lastInsertId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Anax\Database;
4
5
use \Anax\Database\Exception\Exception;
6
7
/**
8
 * Database wrapper, provides a database API on top of PHP PDO for
9
 * enhancing the API and dealing with error reporting and tracking.
10
 */
11
class Database
12
{
13
    /**
14
     * @var array        $options used when creating the PDO object
15
     * @var PDO          $pdo     the PDO object
16
     * @var PDOStatement $stmt    the latest PDOStatement used
17
     */
18
    protected $options;
19
    private $pdo = null;
20
    private $stmt = null;
21
22
23
24
    /**
25
     * Constructor creating a PDO object connecting to a choosen database.
26
     *
27
     * @param array $options containing details for connecting to the database.
28
     */
29
    public function __construct($options = [])
30
    {
31
        $this->setOptions($options);
32
    }
33
34
35
36
    /**
37
     * Set options and connection details.
38
     *
39
     * @param array $options containing details for connecting to the database.
40
     *
41
     * @return void
42
     */
43
    public function setOptions($options = [])
44
    {
45
        $default = [
46
            'dsn'             => null,
47
            'username'        => null,
48
            'password'        => null,
49
            'driver_options'  => null,
50
            'table_prefix'    => null,
51
            'fetch_mode'      => \PDO::FETCH_OBJ,
52
            'session_key'     => 'Anax\Database',
53
            'verbose'         => null,
54
            'debug_connect'   => false,
55
        ];
56
        $this->options = array_merge($default, $options);
57
    }
58
59
60
61
    /**
62
     * Set a single option.
63
     *
64
     * @param string $option which to set.
65
     * @param mixed  $value  to set.
66
     *
67
     * @return self
68
     */
69
    public function setOption($option, $value)
70
    {
71
        $this->options[$option] = $value;
72
        return $this;
73
    }
74
75
76
77
    /**
78
     * Connect to the database, allow being called multiple times
79
     * but ignore when connection is already made.
80
     *
81
     * @return self
82
     *
83
     * @throws \Anax\Database\Exception
84
     */
85
    public function connect()
86
    {
87
        if ($this->pdo) {
88
            return $this;
89
        }
90
91
        if (!isset($this->options['dsn'])) {
92
            throw new Exception("You can not connect, missing dsn.");
93
        }
94
95
        try {
96
            $this->pdo = new \PDO(
97
                $this->options['dsn'],
98
                $this->options['username'],
99
                $this->options['password'],
100
                $this->options['driver_options']
101
            );
102
103
            $this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, $this->options['fetch_mode']);
104
            $this->pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
105
        } catch (\PDOException $e) {
106
            if ($this->options['debug_connect']) {
107
                throw $e;
108
            }
109
            throw new Exception("Could not connect to database, hiding connection details.");
110
        }
111
112
        return $this;
113
    }
114
115
    
116
117
    public function next()
118
    {
119
        return $this->stmt->nextRowSet();
120
    }
121
122
123
124
    /**
125
     * Support arrays in params, extract array items and add to $params
126
     * and insert ? for each entry in the array.
127
     *
128
     * @param string $query  as the query to prepare.
129
     * @param array  $params the parameters that may contain arrays.
130
     *
131
     * @return array with query and params.
132
     */
133
    private function expandParamArray($query, $params)
134
    {
135
        $param = [];
136
        $offset = -1;
137
138
        foreach ($params as $val) {
139
            $offset = strpos($query, '?', $offset + 1);
140
141
            if (is_array($val)) {
142
                $nrOfItems = count($val);
143
144
                if ($nrOfItems) {
145
                    $query = substr($query, 0, $offset)
146
                        . str_repeat('?,', $nrOfItems  - 1)
147
                        . '?'
148
                        . substr($query, $offset + 1);
149
                    $param = array_merge($param, $val);
150
                } else {
151
                    $param[] = null;
152
                }
153
            } else {
154
                $param[] = $val;
155
            }
156
        }
157
158
        return [$query, $param];
159
    }
160
161
162
163
    /**
164
     * Execute a select-query with arguments and return the resultset.
165
     *
166
     * @param string $query  the SQL statement
167
     * @param array  $params the params array
168
     *
169
     * @return array with resultset
170
     */
171
    public function executeFetchAll($query, $params = [])
172
    {
173
        $this->execute($query, $params);
174
        return $this->stmt->fetchAll();
175
    }
176
177
178
179
    /**
180
     * Execute a select-query with arguments and return the first row
181
     * in the resultset.
182
     *
183
     * @param string $query  the SQL statement
184
     * @param array  $params the params array
185
     *
186
     * @return array with resultset
187
     */
188
    public function executeFetch($query, $params = [])
189
    {
190
        $this->execute($query, $params);
191
        return $this->stmt->fetch();
192
    }
193
194
195
196
    /**
197
     * Execute a select-query with arguments and insert the results into
198
     * a new object of the class.
199
     *
200
     * @param string $query  the SQL statement
201
     * @param array  $params the params array
202
     * @param string $class  the class to create an object of and insert into
203
     *
204
     * @return array with resultset
205
     */
206
    public function executeFetchClass($query, $params, $class)
207
    {
208
        $this->execute($query, $params);
209
        $this->stmt->setFetchMode(\PDO::FETCH_CLASS, $class);
210
        return $this->stmt->fetch();
211
    }
212
213
214
215
    /**
216
     * Execute a select-query with arguments and insert the results into
217
     * an existing object.
218
     *
219
     * @param string $query  the SQL statement
220
     * @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...
221
     * @param string $object the existing object to insert into
222
     *
223
     * @return array with resultset
224
     */
225
    public function executeFetchInto($query, $param, $object = null)
226
    {
227
        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...
228
            $object = $param;
229
            $param = [];
230
        }
231
        $this->execute($query, $param);
232
        $this->stmt->setFetchMode(\PDO::FETCH_INTO, $object);
233
        return $this->stmt->fetch();
234
    }
235
236
237
238
    /**
239
     * Fetch all resultset.
240
     *
241
     * @return array with resultset.
242
     */
243
    public function fetchAll()
244
    {
245
        return $this->stmt->fetchAll();
246
    }
247
248
249
250
    /**
251
     * Fetch one resultset.
252
     *
253
     * @return array with resultset.
254
     */
255
    public function fetch()
256
    {
257
        return $this->stmt->fetch();
258
    }
259
260
261
262
    /**
263
     * Fetch one resultset as a new object from this class.
264
     *
265
     * @param object $class which type of object to instantiate.
266
     *
267
     * @return array with resultset.
268
     */
269
    public function fetchClass($class)
270
    {
271
        $this->stmt->setFetchMode(\PDO::FETCH_CLASS, $class);
272
        return $this->stmt->fetch();
273
    }
274
275
276
277
    /**
278
     * Fetch full resultset as new objects from this class.
279
     *
280
     * @param object $class which type of object to instantiate.
281
     *
282
     * @return array with resultset.
283
     */
284
    public function fetchAllClass($class)
285
    {
286
        $this->stmt->setFetchMode(\PDO::FETCH_CLASS, $class);
287
        return $this->stmt->fetchAll();
288
    }
289
290
291
292
    /**
293
     * Fetch one resultset into an object.
294
     *
295
     * @param object $object to insert values into.
296
     *
297
     * @return array with resultset.
298
     */
299
    public function fetchInto($object)
300
    {
301
        $this->stmt->setFetchMode(\PDO::FETCH_INTO, $object);
302
        return $this->stmt->fetch();
303
    }
304
305
306
307
    /**
308
     * Execute a SQL-query and ignore the resultset.
309
     *
310
     * @param string $query  the SQL statement
311
     * @param array  $params the params array
312
     *
313
     * @return self
314
     *
315
     * @throws Exception when failing to prepare question.
316
     */
317
    public function execute($query, $params = [])
318
    {
319
        list($query, $params) = $this->expandParamArray($query, $params);
320
321
        $this->stmt = $this->pdo->prepare($query);
322
        if (!$this->stmt) {
323
            $this->pdoException($query, $params);
324
        }
325
326
        if ($this->options["verbose"]) {
327
            echo $query . "\n";
328
            print_r($params);
329
        }
330
331
        $res = $this->stmt->execute($params);
332
        if (!$res) {
333
            $this->statementException($query, $params);
334
        }
335
336
        return $this;
337
    }
338
339
340
341
    /**
342
     * Throw exception using detailed message.
343
     *
344
     * @param string       $msg     detailed error message from PDO
345
     * @param string       $query   query to execute
346
     * @param array        $param   to match ? in statement
347
     *
348
     * @return void
349
     *
350
     * @throws \Anax\Database\Exception
351
     */
352
    protected function createException($msg, $query, $param)
353
    {
354
        throw new Exception(
355
            $msg
356
            . "<br><br>SQL ("
357
            . substr_count($query, "?")
358
            . " params):<br><pre>$query</pre><br>PARAMS ("
359
            . count($param)
360
            . "):<br><pre>"
361
            . implode($param, "\n")
362
            . "</pre>"
363
            . ((count(array_filter(array_keys($param), 'is_string')) > 0)
364
                ? "WARNING your params array has keys, should only have values."
365
                : null)
366
        );
367
    }
368
369
370
371
    /**
372
     * Throw exception when pdo failed using detailed message.
373
     *
374
     * @param string       $query   query to execute
375
     * @param array        $param   to match ? in statement
376
     *
377
     * @return void
378
     *
379
     * @throws \Anax\Database\Exception
380
     */
381
    protected function pdoException($query, $param)
382
    {
383
        $this->createException($this->pdo->errorInfo()[2], $query, $param);
384
    }
385
386
387
388
    /**
389
     * Throw exception when statement failed using detailed message.
390
     *
391
     * @param string       $query   query to execute
392
     * @param array        $param   to match ? in statement
393
     *
394
     * @return void
395
     *
396
     * @throws \Anax\Database\Exception
397
     */
398
    protected function statementException($query, $param)
399
    {
400
        $this->createException($this->stmt->errorInfo()[2], $query, $param);
401
    }
402
403
404
405
    /**
406
     * Return last insert id from autoincremented key on INSERT.
407
     *
408
     * @return integer as last insert id.
409
     */
410
    public function lastInsertId()
411
    {
412
        return $this->pdo->lastInsertId();
413
    }
414
415
416
417
    /**
418
    * Return rows affected of last INSERT, UPDATE, DELETE
419
    *
420
    * @return integer as rows affected on last statement
421
    */
422
    public function rowCount()
423
    {
424
        return $this->stmt->rowCount();
425
    }
426
427
428
429
    /**
430
     * Fetch one resultset as an object from this class.
431
     * OBSOLETE replaced by fetchClass
432
     *
433
     * @param object $class which type of object to instantiate.
434
     *
435
     * @return array with resultset.
436
     */
437
    public function fetchObject($class)
438
    {
439
        return $this->stmt->fetchObject($class);
440
    }
441
442
443
444
    /**
445
     * Fetch one resultset. OBSOLETE replace by fetch()
446
     *
447
     * @return array with resultset.
448
     */
449
    public function fetchOne()
450
    {
451
        return $this->stmt->fetch();
452
    }
453
}
454