Passed
Push — master ( 87ab26...f9244b )
by 世昌
02:18
created

PrepareTrait::count()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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