Completed
Push — master ( abf175...09e679 )
by Mikael
06:27 queued 11s
created

Database::fetch()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
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
0 ignored issues
show
Bug introduced by
There is no parameter named $query. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

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

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

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

Loading history...
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($sql, $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($sql, $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->statementException($sql, $param);
0 ignored issues
show
Bug introduced by
The variable $sql does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $param does not exist. Did you mean $params?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
279
        }
280
281 3
        $res = $this->stmt->execute($params);
282 3
        if (!$res) {
283
            $this->statementException($sql, $param);
0 ignored issues
show
Bug introduced by
The variable $param does not exist. Did you mean $params?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
284
        }
285
286 3
        return $res;
287
    }
288
289
290
291
    /**
292
     * Through exception with detailed message.
293
     *
294
     * @param string       $sql     statement to execute
295
     * @param array        $param   to match ? in statement
296
     *
297
     * @return void
298
     *
299
     * @throws \Anax\Database\Exception
300
     */
301
    private function statementException($sql, $param)
302
    {
303
        throw new Exception(
304
            $this->stmt->errorInfo()[2]
305
            . "<br><br>SQL ("
306
            . substr_count($sql, "?")
307
            . " params):<br><pre>$sql</pre><br>PARAMS ("
308
            . count($param)
309
            . "):<br><pre>"
310
            . implode($param, "\n")
311
            . "</pre>"
312
            . ((count(array_filter(array_keys($param), 'is_string')) > 0)
313
                ? "WARNING your params array has keys, should only have values."
314
                : null)
315
        );
316
    }
317
318
319
320
    /**
321
     * Return last insert id from autoincremented key on INSERT.
322
     *
323
     * @return integer as last insert id.
324
     */
325
    public function lastInsertId()
326
    {
327
        return $this->pdo->lastInsertId();
328
    }
329
330
331
332
    /**
333
    * Return rows affected of last INSERT, UPDATE, DELETE
334
    *
335
    * @return integer as rows affected on last statement
336
    */
337
    public function rowCount()
338
    {
339
        return $this->stmt->rowCount();
340
    }
341
342
343
344
    /**
345
     * Fetch one resultset as an object from this class.
346
     * OBSOLETE replaced by fetchClass
347
     *
348
     * @param object $class which type of object to instantiate.
349
     *
350
     * @return array with resultset.
351
     */
352
    public function fetchObject($class)
353
    {
354
        return $this->stmt->fetchObject($class);
355
    }
356
357
358
359
    /**
360
     * Fetch one resultset. OBSOLETE replace by fetch()
361
     *
362
     * @return array with resultset.
363
     */
364
    public function fetchOne()
365
    {
366
        return $this->stmt->fetch();
367
    }
368
}
369