WherePrepareTrait::fixWhereArray()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 5
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 7
rs 10
1
<?php
2
3
4
namespace suda\database\statement;
5
6
use Countable;
7
use IteratorAggregate;
8
use suda\database\Binder;
9
use suda\database\exception\SQLException;
10
11
trait WherePrepareTrait
12
{
13
14
    /**
15
     * 处理?模板
16
     *
17
     * @param string $sql
18
     * @param array $parameter
19
     * @return array
20
     */
21
    public function prepareQueryMark(string $sql, array $parameter)
22
    {
23
        $binders = [];
24
        $query = preg_replace_callback('/\?/', function ($match) use (&$binders, $parameter) {
25
            $index = count($binders);
26
            if (array_key_exists($index, $parameter)) {
27
                $name = Binder::index($index);
28
                if (is_array($parameter[$index]) || $parameter[$index] instanceof IteratorAggregate) {
29
                    [$inSQL, $inBinders] = $this->prepareInParameter($parameter[$index], $index);
30
                    $binders = array_merge($binders, $inBinders);
31
                    return $inSQL;
32
                } else {
33
                    $binder = new Binder($name, $parameter[$index]);
34
                    $binders[] = $binder;
35
                    return ':' . $binder->getName();
36
                }
37
            }
38
            return $match[0];
39
        }, $sql);
40
        return [$query, $binders];
41
    }
42
43
    /**
44
     * 处理In语句
45
     * @param IteratorAggregate|array $values
46
     * @param string $name
47
     * @return array
48
     * @throws SQLException
49
     */
50
    public function prepareInParameter($values, string $name)
51
    {
52
        if ($this->countObject($values) <= 0) {
53
            throw new SQLException('on field ' . $name . ' value can\'t be empty array');
54
        }
55
        $names = [];
56
        $binders = [];
57
        foreach ($values as $value) {
58
            $_name = Binder::index($name);
59
            $binders[] = new Binder($_name, $value);
60
            $names[] = ':' . $_name;
61
        }
62
        return [implode(',', $names), $binders];
63
    }
64
65
    /**
66
     * @param array|Countable|IteratorAggregate $value
67
     * @return int
68
     */
69
    protected function countObject($value)
70
    {
71
        if (is_array($value) || $value instanceof Countable) {
72
            return count($value);
73
        }
74
        return iterator_count($value);
75
    }
76
77
    /**
78
     * @param string $name
79
     * @param string $operator
80
     * @param mixed $value
81
     * @param string $indexName
82
     * @return Query
83
     * @throws SQLException
84
     */
85
    public function createQueryOperation(string $name, string $operator, $value, string $indexName = '')
86
    {
87
        if ($value instanceof Query) {
88
            return new Query("`{$name}` {$operator} " . $value, $value->getBinder());
89
        }
90
        if ($value instanceof Statement) {
91
            return new Query("`{$name}` {$operator} (" . $value->getQuery() . ')', $value->getBinder());
92
        }
93
        if ($value instanceof IteratorAggregate || is_array($value)) {
94
            return $this->prepareIn($name, $operator, $value);
95
        }
96
        if (strlen($indexName) === 0) {
97
            $indexName = Binder::index($name);
98
        }
99
        return new Query("`{$name}` {$operator} :{$indexName}", [new Binder($indexName, $value)]);
100
    }
101
102
    /**
103
     * @param string $name
104
     * @param string $operator
105
     * @param $value
106
     * @return Query
107
     * @throws SQLException
108
     */
109
    public function getQueryForArray(string $name, string $operator, $value)
110
    {
111
        if ($this->isArray($value)) {
112
            return $this->prepareIn($name, $operator, $value);
113
        } else {
114
            return $this->createQueryOperation($name, $operator, $value);
115
        }
116
    }
117
118
    /**
119
     * @param string $where
120
     * @param array $whereBinder
121
     * @return array
122
     * @throws SQLException
123
     */
124
    public function prepareWhereString(string $where, array $whereBinder)
125
    {
126
        $newWhereBinder = [];
127
        foreach ($whereBinder as $name => $value) {
128
            $query = $this->getQueryForString($where, $name, $value);
129
            $where = $query->getQuery();
130
            $newWhereBinder = array_merge($newWhereBinder, $query->getBinder());
131
        }
132
        return [$where, $newWhereBinder];
133
    }
134
135
    /**
136
     * 准备条件列
137
     *
138
     * @param array $where
139
     * @return array
140
     * @throws SQLException
141
     */
142
    public function prepareWhere(array $where)
143
    {
144
        $whereArray = $this->normalizeWhereArray($where);
145
        $and = [];
146
        $binders = [];
147
        foreach ($whereArray as $item) {
148
            [$name, $option, $value] = $this->fixWhereArray($item);
149
            $query = $this->getQueryForArray($name, $option, $value);
150
            $and[] = $query->getQuery();
151
            $binders = array_merge($binders, $query->getBinder());
152
        }
153
        return [implode(' AND ', $and), $binders];
154
    }
155
156
    /**
157
     * @param array $item
158
     * @return array
159
     */
160
    protected function fixWhereArray(array $item) {
161
        if (count($item) === 2) {
162
            [$name, $value] = $item;
163
            $op = $this->isArray($value)?'in':'=';
164
            return [$name, $op, $value];
165
        }
166
        return $item;
167
    }
168
169
    /**
170
     * @param array $where
171
     * @return array
172
     */
173
    protected function normalizeWhereArray(array $where)
174
    {
175
        if ($this->isNumberArray($where)) {
176
            return $where;
177
        }
178
        $newWhere = [];
179
        foreach ($where as $name => $value) {
180
            if (is_array($value)) {
181
                $newWhere[] = [$name, $value[0], $value[1]];
182
            } else {
183
                $op = $this->isArray($value)?'in':'=';
184
                $newWhere[] = [$name, $op, $value];
185
            }
186
        }
187
        return $newWhere;
188
    }
189
190
    /**
191
     * @param $value
192
     * @return bool
193
     */
194
    protected function isArray($value) {
195
        return is_array($value) || $value instanceof IteratorAggregate;
196
    }
197
198
    /**
199
     * @param array $where
200
     * @return bool
201
     */
202
    protected function isNumberArray(array $where)
203
    {
204
        return is_numeric(key($where)) && array_keys($where) === range(0, count($where) - 1);
205
    }
206
207
    /**
208
     * @param string $where
209
     * @param string $name
210
     * @param $value
211
     * @return Query
212
     * @throws SQLException
213
     */
214
    public function getQueryForString(string $where, string $name, $value)
215
    {
216
        if ($this->isArray($value)) {
217
            [$inSQL, $binders] = $this->prepareInParameter($value, $name);
218
            $where = $this->replaceQuote($name, $inSQL, $where);
219
            return new Query($where, $binders);
220
        } elseif ($value instanceof Binder) {
221
            return new Query($where, [$value]);
222
        } else {
223
            return new Query($where, [new Binder($name, $value)]);
224
        }
225
    }
226
227
    /**
228
     * 准备In
229
     *
230
     * @param string $name
231
     * @param string $operation
232
     * @param IteratorAggregate|array|Query|Statement $values
233
     * @return Query
234
     * @throws SQLException
235
     */
236
    public function prepareIn(string $name, string $operation, $values)
237
    {
238
        if ($values instanceof Query || $values instanceof Statement) {
239
            return $this->createQueryOperation($name, 'in', $values);
240
        }
241
        [$inSQL, $binders] = $this->prepareInParameter($values, $name);
242
        $sql = '`' . $name . '` ' . strtoupper($operation) . ' (' . $inSQL . ')';
243
        return new Query($sql, $binders);
244
    }
245
246
    /**
247
     * 替换占位符
248
     *
249
     * @param string $name
250
     * @param string $replace
251
     * @param string $target
252
     * @return string
253
     */
254
    public function replaceQuote(string $name, string $replace, string $target)
255
    {
256
        $name = ltrim($name, ':');
257
        return preg_replace('/(?<!_):' . preg_quote($name) . '/', $replace, $target);
258
    }
259
}
260