Completed
Pull Request — master (#12)
by Alexander
01:58
created

Mysql::delete()   C

Complexity

Conditions 7
Paths 17

Size

Total Lines 23
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 17
nc 17
nop 3
dl 0
loc 23
rs 6.7272
c 0
b 0
f 0
1
<?php
2
3
namespace alkemann\h2l\data;
4
5
use alkemann\h2l\exceptions\ConnectionError;
6
use PDO;
7
use alkemann\h2l\Log;
8
9
class Mysql implements Source
10
{
11
12
    /**
13
     * @var array
14
     */
15
    protected $config = [];
16
17
    /**
18
     * @var PDO
19
     */
20
    protected $db = null;
21
22
    public function __construct(array $config = [])
23
    {
24
        $defaults = [
25
            'host' => 'localhost',
26
            'db' => 'test',
27
            'user' => null,
28
            'pass' => null
29
        ];
30
        $this->config = $config + $defaults;
31
    }
32
33
    private function handler() //: PDO
34
    {
35
        if ($this->db) {
36
            return $this->db;
37
        }
38
39
        $host = $this->config['host'];
40
        $db = $this->config['db'];
41
        $user = $this->config['user'];
42
        $pass = $this->config['pass'];
43
        $opts = [
44
            PDO::ATTR_EMULATE_PREPARES => false,
45
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
46
        ];
47
        try {
48
            $this->db = new PDO("mysql:host={$host};dbname={$db}", $user, $pass, $opts);
49
            // @TODO use this?
1 ignored issue
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
50
            // $this->db->setAttribute( PDO::ATTR_EMULATE_PREPARES, false);
1 ignored issue
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
51
        } catch (\PDOException $e) {
52
            throw new ConnectionError("Unable to connect to $host : $db with user $user");
1 ignored issue
show
Unused Code introduced by
The call to ConnectionError::__construct() has too many arguments starting with "Unable to connect to {$...$db} with user {$user}".

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
53
        }
54
        return $this->db;
55
    }
56
57
    public function query($query, array $params = [])
58
    {
59
        Log::debug("PDO:QUERY [$query]");
60
        $result = $this->handler()->query($query);
61
        return $result->fetchAll(PDO::FETCH_ASSOC);
62
    }
63
64
    public function one(string $table, array $conditions, array $options = []): ?array
65
    {
66
        $result = $this->find($table, $conditions, $options);
67
        $result = iterator_to_array($result);
68
        $hits = sizeof($result);
69
        if ($hits === 0) {
70
            return null;
71
        }
72
        if ($hits > 1) {
73
            throw new \Error("One request found more than 1 match!");
74
        }
75
76
        return $result[0];
77
    }
78
79
    public function find(string $table, array $conditions, array $options = []): iterable
80
    {
81
        $where = $this->where($conditions);
82
        $limit = array_key_exists('limit', $options) ? "LIMIT :o_offset,:o_limit " : '';
83
        $query = "SELECT * FROM `{$table}` {$where}{$limit};";
84
        $params = $this->boundDebugString($conditions, $options);
85
        Log::debug("PDO:QUERY [$query][$params]");
86
        $dbh = $this->handler();
87
        $stmt = $dbh->prepare($query);
88
        foreach ($conditions as $key => $value) {
89
            $stmt->bindValue(":c_{$key}", $value);
90
        }
91
        if (empty($limit) === false) {
92
            $stmt->bindValue(":o_offset", $options['offset'] ?? 0, PDO::PARAM_INT);
93
            $stmt->bindValue(":o_limit", $options['limit'], PDO::PARAM_INT);
94
        }
95
        if ($stmt->execute() === false) {
96
            return new \EmptyIterator;
97
        }
98
        if ($stmt && $stmt instanceof \PDOStatement) {
99
            // @codeCoverageIgnoreStart
100
            $stmt->setFetchMode(PDO::FETCH_ASSOC);
101
            // codeCoverageIgnoreEnd
102
        }
103
        return $stmt;
104
    }
105
106
    private function boundDebugString(array $conditions, array $options, array $data = []): string
107
    {
108
        $out = [];
109
        foreach ($conditions as $k => $v) {
110
            $out[] = "c_{$k}:'{$v}'";
111
        }
112
        foreach ($data as $k => $v) {
113
            $out[] = "d_{$k}:{$v}";
114
        }
115
        foreach ($options as $k => $v) {
116
            $out[] = "o_{$k}:{$v}";
117
        }
118
        return join(", ", $out);
119
    }
120
121
    private function where(array $conditions): string
122
    {
123
        if (empty($conditions)) {
124
            return "";
125
        }
126
        $fun = function ($o, $v) {
127
            return "{$o}{$v} = :c_{$v}";
128
        };
129
        $where = array_reduce(array_keys($conditions), $fun, "");
130
        return "WHERE {$where} ";
131
    }
132
133
    public function update(string $table, array $conditions, array $data, array $options = []): int
134
    {
135
        if (empty($conditions) || empty($data)) {
136
            return 0;
137
        }
138
139
        $datasql = $this->data($data);
140
        $where = $this->where($conditions);
141
        $query = "UPDATE `{$table}` SET {$datasql} {$where};";
142
143
        $params = $this->boundDebugString($conditions, $options, $data);
144
        Log::debug("PDO:QUERY [$query][$params]");
145
        $dbh = $this->handler();
146
        $stmt = $dbh->prepare($query);
147
        foreach ($data as $key => $value) {
148
            $stmt->bindValue(":d_{$key}", $value);
149
        }
150
        foreach ($conditions as $key => $value) {
151
            $stmt->bindValue(":c_{$key}", $value);
152
        }
153
        $result = $stmt->execute();
154
        return ($result === true) ? $stmt->rowCount() : 0;
155
    }
156
157
    private function data(array $data): string
158
    {
159
        $fun = function ($o, $v) {
160
            return "{$o}{$v} = :d_{$v}";
161
        };
162
        return array_reduce(array_keys($data), $fun, "");
163
    }
164
165
    public function insert(string $table, array $data, array $options = []): ?int
166
    {
167
        $keys = implode(', ', array_keys($data));
168
        $data_phs = ':d_' . implode(', :d_', array_keys($data));
169
        $query = "INSERT INTO `{$table}` ({$keys}) VALUES ({$data_phs});";
170
        $params = $this->boundDebugString([], [], $data);
171
        Log::debug("PDO:QUERY [$query][$params]");
172
        $dbh = $this->handler();
173
        $stmt = $dbh->prepare($query);
174
        foreach ($data as $key => $value) {
175
            $stmt->bindValue(':d_' . $key, $value);
176
        }
177
        $result = $stmt->execute();
178
        return ($result === true) ? $dbh->lastInsertId() : null;
179
    }
180
181
    public function delete(string $table, array $conditions, array $options = []): int
182
    {
183
        $where = $this->where($conditions);
184
        if (empty($conditions) || empty($where)) {
185
            return 0;
186
        }
187
188
        $limit = array_key_exists('limit', $options) ? "LIMIT :o_offset,:o_limit " : '';
189
        $query = "DELETE FROM `{$table}` {$where}{$limit};";
190
        $params = $this->boundDebugString($conditions, $options);
191
        Log::debug("PDO:QUERY [$query][$params]");
192
        $dbh = $this->handler();
193
        $stmt = $dbh->prepare($query);
194
        foreach ($conditions as $key => $value) {
195
            $stmt->bindValue(":c_{$key}", $value);
196
        }
197
        if (empty($limit) === false) {
198
            $stmt->bindValue(":o_offset", $options['offset'] ?? 0, PDO::PARAM_INT);
199
            $stmt->bindValue(":o_limit", $options['limit'], PDO::PARAM_INT);
200
        }
201
        $result = $stmt->execute();
202
        return ($result === true) ? $stmt->rowCount() : 0;
203
    }
204
}
205