Completed
Push — master ( 9c1ef9...9b3ca3 )
by Alex
10:25
created

PdoCrud::execSelect()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 5
rs 10
1
<?php
2
namespace Mezon\PdoCrud;
3
4
/**
5
 * Class PdoCrud
6
 *
7
 * @package Mezon
8
 * @subpackage PdoCrud
9
 * @author Dodonov A.A.
10
 * @version v.1.0 (2019/08/16)
11
 * @copyright Copyright (c) 2019, aeon.org
12
 */
13
14
// TODO mark all old methods as deprecated
15
// TODO use PHPUnit 8.5 in Travis
16
17
/**
18
 * Class provides simple CRUD operations
19
 */
20
class PdoCrud
21
{
22
23
    /**
24
     * PDO object
25
     *
26
     * @var \PDO
27
     */
28
    private $pdo = null;
29
30
    /**
31
     * PDO statement
32
     *
33
     * @var \PDOStatement
34
     */
35
    private $pdoStatement = null;
36
37
    /**
38
     * Method connects to the database
39
     *
40
     * @param array $connnectionData
41
     *            Connection settings
42
     * @codeCoverageIgnore
43
     */
44
    public function connect(array $connnectionData): void
45
    {
46
        // no need to test this single string. assume that PDO developers did it
47
        $this->pdo = new \PDO($connnectionData['dsn'], $connnectionData['user'], $connnectionData['password']);
48
49
        $this->query('SET NAMES utf8');
50
    }
51
52
    /**
53
     * Method handles request errors
54
     *
55
     * @param mixed $result
56
     *            Query result
57
     * @param string $query
58
     *            SQL Query
59
     * @codeCoverageIgnore
60
     */
61
    protected function processQueryError($result, string $query): void
62
    {
63
        if ($result === false) {
64
            $errorInfo = $this->pdo->errorInfo();
65
66
            throw (new \Exception($errorInfo[2] . ' in statement ' . $query));
67
        }
68
    }
69
70
    /**
71
     * Method sets safe query
72
     *
73
     * @param string $query
74
     *            safe query
75
     * @codeCoverageIgnore
76
     */
77
    public function prepare(string $query): void
78
    {
79
        $this->pdoStatement = $this->pdo->prepare($query, [
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->pdo->prepare($que...> PDO::CURSOR_FWDONLY)) can also be of type boolean. However, the property $pdoStatement is declared as type PDOStatement. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
80
            \PDO::ATTR_CURSOR => \PDO::CURSOR_FWDONLY
81
        ]);
82
83
        if ($this->pdoStatement === false) {
84
            $errorInfo = $this->pdo->errorInfo();
85
            throw (new \Exception('Query "' . query . '" was not prepared. ' . $errorInfo[2], - 1));
0 ignored issues
show
Bug introduced by
The constant Mezon\PdoCrud\query was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
86
        }
87
    }
88
89
    /**
90
     * Method executes select query and fetches results
91
     *
92
     * @param array $data
93
     *            query data
94
     * @return array query result as an array of objects
95
     * @codeCoverageIgnore
96
     */
97
    public function execSelect(array $data = []): array
98
    {
99
        $this->pdoStatement->execute($data);
100
101
        return $this->pdoStatement->fetchAll(\PDO::FETCH_OBJ);
102
    }
103
104
    /**
105
     * Getting records
106
     *
107
     * @param string $fields
108
     *            List of fields
109
     * @param string $tableNames
110
     *            List of tables
111
     * @param string $where
112
     *            Condition
113
     * @param int $from
114
     *            First record in query
115
     * @param int $limit
116
     *            Count of records
117
     * @return array List of records
118
     * @deprecated since 2020-06-16
119
     */
120
    public function select(
121
        string $fields,
122
        string $tableNames,
123
        string $where = '1 = 1',
124
        int $from = 0,
125
        int $limit = 1000000): array
126
    {
127
        $query = "SELECT $fields FROM $tableNames WHERE $where LIMIT " . intval($from) . ' , ' . intval($limit);
128
129
        $result = $this->query($query);
130
131
        $this->processQueryError($result, $query);
132
133
        return $result->fetchAll(\PDO::FETCH_ASSOC);
134
    }
135
136
    /**
137
     * Method compiles set-query
138
     *
139
     * @param array $record
140
     *            Inserting record
141
     * @return string Compiled query string
142
     */
143
    protected function compileGetQuery(array $record): string
144
    {
145
        $setFieldsStatement = [];
146
147
        foreach ($record as $field => $value) {
148
            if (is_string($value) && strtoupper($value) === 'INC') {
149
                $setFieldsStatement[] = $field . ' = ' . $field . ' + 1';
150
            } elseif (is_string($value) && strtoupper($value) !== 'NOW()') {
151
                $setFieldsStatement[] = $field . ' = "' . $value . '"';
152
            } elseif ($value === null) {
153
                $setFieldsStatement[] = $field . ' = NULL';
154
            } else {
155
                $setFieldsStatement[] = $field . ' = ' . $value;
156
            }
157
        }
158
159
        return implode(' , ', $setFieldsStatement);
160
    }
161
162
    /**
163
     * Method compiles set-multyple-query
164
     *
165
     * @param array $records
166
     *            Inserting records
167
     * @return string Compiled query string
168
     */
169
    protected function setMultypleQuery(array $records): string
170
    {
171
        $query = '( ' . implode(' , ', array_keys($records[0])) . ' ) VALUES ';
172
173
        $values = [];
174
175
        foreach ($records as $record) {
176
            $values[] = "( '" . implode("' , '", array_values($record)) . "' )";
177
        }
178
179
        return $query . implode(' , ', $values);
180
    }
181
182
    /**
183
     * Updating records
184
     *
185
     * @param string $tableName
186
     *            Table name
187
     * @param array $record
188
     *            Updating records
189
     * @param string $where
190
     *            Condition
191
     * @param int $limit
192
     *            Liti for afffecting records
193
     * @return int Count of updated records
194
     */
195
    public function update(string $tableName, array $record, string $where, int $limit = 10000000): int
196
    {
197
        $query = 'UPDATE ' . $tableName . ' SET ' . $this->compileGetQuery($record) . ' WHERE ' . $where . ' LIMIT ' .
198
            $limit;
199
200
        $result = $this->query($query);
201
202
        $this->processQueryError($result, $query);
203
204
        return $result->rowCount();
205
    }
206
207
    /**
208
     * Deleting records
209
     *
210
     * @param string $tableName
211
     *            Table name
212
     * @param string $where
213
     *            Condition
214
     * @param int $limit
215
     *            Liti for afffecting records
216
     * @return int Count of deleted records
217
     */
218
    public function delete($tableName, $where, $limit = 10000000): int
219
    {
220
        $query = 'DELETE FROM ' . $tableName . ' WHERE ' . $where . ' LIMIT ' . intval($limit);
221
222
        $result = $this->query($query);
223
224
        $this->processQueryError($result, $query);
225
226
        return $result->rowCount();
227
    }
228
229
    /**
230
     * Method compiles lock queries
231
     *
232
     * @param array $tables
233
     *            List of tables
234
     * @param array $modes
235
     *            List of lock modes
236
     * @return string Query
237
     */
238
    protected function lockQuery(array $tables, array $modes): string
239
    {
240
        $query = [];
241
242
        foreach ($tables as $i => $table) {
243
            $query[] = $table . ' ' . $modes[$i];
244
        }
245
246
        return 'LOCK TABLES ' . implode(' , ', $query);
247
    }
248
249
    /**
250
     * Method locks tables
251
     *
252
     * @param array $tables
253
     *            List of tables
254
     * @param array $modes
255
     *            List of lock modes
256
     */
257
    public function lock(array $tables, array $modes): void
258
    {
259
        $query = $this->lockQuery($tables, $modes);
260
261
        $result = $this->query($query);
262
263
        $this->processQueryError($result, $query);
264
    }
265
266
    /**
267
     * Method unlocks locked tables
268
     */
269
    public function unlock(): void
270
    {
271
        $result = $this->query('UNLOCK TABLES');
272
273
        $this->processQueryError($result, 'UNLOCK TABLES');
274
    }
275
276
    /**
277
     * Method starts transaction
278
     */
279
    public function startTransaction(): void
280
    {
281
        // setting autocommit off
282
        $result = $this->query('SET AUTOCOMMIT = 0');
283
284
        $this->processQueryError($result, 'SET AUTOCOMMIT = 0');
285
286
        // starting transaction
287
        $result = $this->query('START TRANSACTION');
288
289
        $this->processQueryError($result, 'START TRANSACTION');
290
    }
291
292
    /**
293
     * Commiting transaction
294
     */
295
    public function commit(): void
296
    {
297
        // commit transaction
298
        $result = $this->query('COMMIT');
299
300
        $this->processQueryError($result, 'COMMIT');
301
302
        // setting autocommit on
303
        $result = $this->query('SET AUTOCOMMIT = 1');
304
305
        $this->processQueryError($result, 'SET AUTOCOMMIT = 1');
306
    }
307
308
    /**
309
     * Rollback transaction
310
     */
311
    public function rollback(): void
312
    {
313
        // rollback transaction
314
        $result = $this->query('ROLLBACK');
315
316
        $this->processQueryError($result, 'ROLLBACK');
317
    }
318
319
    /**
320
     * Method executes query
321
     *
322
     * @param string $query
323
     *            Query
324
     * @return mixed Query execution result
325
     */
326
    public function query(string $query)
327
    {
328
        // @codeCoverageIgnoreStart
329
        return $this->pdo->query($query);
330
        // @codeCoverageIgnoreEnd
331
    }
332
333
    /**
334
     * Method returns id of the last inserted record
335
     *
336
     * @return int id of the last inserted record
337
     */
338
    public function lastInsertId(): int
339
    {
340
        // @codeCoverageIgnoreStart
341
        return (int) $this->pdo->lastInsertId();
342
        // @codeCoverageIgnoreEnd
343
    }
344
345
    /**
346
     * Method inserts record
347
     *
348
     * @param string $tableName
349
     *            Table name
350
     * @param array $record
351
     *            Inserting record
352
     * @return int New record's id
353
     */
354
    public function insert(string $tableName, array $record): int
355
    {
356
        $query = 'INSERT ' . $tableName . ' SET ' . $this->compileGetQuery($record);
357
358
        $result = $this->query($query);
359
360
        $this->processQueryError($result, $query);
361
362
        return $this->lastInsertId();
363
    }
364
365
    /**
366
     * Method inserts record
367
     *
368
     * @param string $tableName
369
     *            Table name
370
     * @param array $records
371
     *            Inserting records
372
     * @return int New record's id
373
     */
374
    public function insertMultyple(string $tableName, array $records): int
375
    {
376
        $query = 'INSERT INTO ' . $tableName . ' ' . $this->setMultypleQuery($records) . ';';
377
378
        $result = $this->query($query);
379
380
        $this->processQueryError($result, $query);
381
382
        return 0;
383
    }
384
385
    /**
386
     * Method destroys connection
387
     */
388
    public function __destruct()
389
    {
390
        $this->pdo = null;
391
392
        unset($this->pdo);
393
    }
394
}
395