Passed
Push — master ( 76c092...6f3180 )
by Mikael
01:34
created

Database::rowCount()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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