Database::executeFetchInto()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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