Passed
Push — master ( 326302...0f8412 )
by Alex
02:09
created

PdoCrud.php (1 issue)

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