Passed
Push — master ( 049c92...563669 )
by 世昌
02:14
created

PrepareTrait   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 253
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 96
c 2
b 0
f 0
dl 0
loc 253
rs 9.1199
wmc 41

13 Methods

Rating   Name   Duplication   Size   Complexity  
A createQueryOperation() 0 15 6
A prepareReadFields() 0 13 4
A getQueryForArray() 0 10 3
A prepareWhere() 0 10 2
A prepareInParameter() 0 13 3
A prepareUpdateSet() 0 10 2
A getQueryForString() 0 10 4
A prepareWhereString() 0 9 2
A replaceQuote() 0 3 1
A prepareIn() 0 8 3
A mergeBinder() 0 11 4
A count() 0 6 3
A prepareQueryMark() 0 20 4

How to fix   Complexity   

Complex Class

Complex classes like PrepareTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PrepareTrait, and based on these observations, apply Extract Interface, too.

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, 'IN', $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, $operation, $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
            $where = $this->replaceQuote($name, $inSQL, $where);
113
            return new Query($where, $binders);
114
        } elseif ($value instanceof  Binder) {
115
            return new Query($where, [$value]);
116
        } else {
117
            return new Query($where, [new Binder($name, $value)]);
118
        }
119
    }
120
121
    /**
122
     * @param string $name
123
     * @param string $replace
124
     * @param string $target
125
     * @return string
126
     */
127
    protected function replaceQuote(string $name, string $replace, string $target) {
128
        $name = ltrim($name, ':');
129
        return preg_replace('/(?<!_):'.preg_quote($name).'/', $replace, $target);
130
    }
131
132
    /**
133
     * @param string $where
134
     * @param array $whereBinder
135
     * @return array
136
     * @throws SQLException
137
     */
138
    protected function prepareWhereString(string $where, array $whereBinder)
139
    {
140
        $newWhereBinder = [];
141
        foreach ($whereBinder as $name => $value) {
142
            $query = $this->getQueryForString($where, $name, $value);
143
            $where = $query->getQuery();
144
            $newWhereBinder = array_merge($newWhereBinder, $query->getBinder());
145
        }
146
        return [$where, $newWhereBinder];
147
    }
148
149
150
    /**
151
     * 准备In
152
     *
153
     * @param string $name
154
     * @param IteratorAggregate|array|Query|Statement $values
155
     * @return Query
156
     * @throws SQLException
157
     */
158
    protected function prepareIn(string $name, string $operation, $values)
159
    {
160
        if ($values instanceof Query || $values instanceof Statement) {
161
            return $this->createQueryOperation($name, 'in', $values);
162
        }
163
        list($inSQL, $binders) = $this->prepareInParameter($values, $name);
164
        $sql = '`'.$name.'` '.strtoupper($operation).' ('.$inSQL.')';
165
        return new Query($sql, $binders);
166
    }
167
168
    /**
169
     * @param IteratorAggregate|array $values
170
     * @param string $name
171
     * @return array
172
     * @throws SQLException
173
     */
174
    protected function prepareInParameter($values, string $name)
175
    {
176
        if ($this->count($values) <= 0) {
177
            throw new SQLException('on field '.$name.' value can\'t be empty array');
178
        }
179
        $names = [];
180
        $binders = [];
181
        foreach ($values as $value) {
182
            $_name = Binder::index($name);
183
            $binders[] = new Binder($_name, $value);
184
            $names[] = ':'.$_name;
185
        }
186
        return [implode(',', $names), $binders];
187
    }
188
189
    /**
190
     * @param array|Countable|IteratorAggregate $value
191
     * @return int
192
     */
193
    private function count($value)
194
    {
195
        if (is_array($value) || $value instanceof Countable) {
196
            return count($value);
197
        }
198
        return iterator_count($value);
199
    }
200
201
    /**
202
     * 准备更新
203
     *
204
     * @param array $data
205
     * @return array
206
     */
207
    protected function prepareUpdateSet(array $data)
208
    {
209
        $binders = [];
210
        $sets = [];
211
        foreach ($data as $name => $value) {
212
            $_name = Binder::index($name);
213
            $binders[] = new Binder($_name, $value, $name);
214
            $sets[] = "`{$name}`=:{$_name}";
215
        }
216
        return [implode(',', $sets), $binders];
217
    }
218
219
    /**
220
     * 编译 ? 字符
221
     *
222
     * @param string $sql
223
     * @param array $parameter
224
     * @return array
225
     */
226
    protected function prepareQueryMark(string $sql, array $parameter)
227
    {
228
        $binders = [];
229
        $query = preg_replace_callback('/\?/', function ($match) use (&$binders, $parameter) {
230
            $index = count($binders);
231
            if (array_key_exists($index, $parameter)) {
232
                $name = Binder::index($index);
233
                if (is_array($parameter[$index]) || $parameter[$index] instanceof IteratorAggregate) {
234
                    list($inSQL, $inBinders) = $this->prepareInParameter($parameter[$index], $index);
235
                    $binders = array_merge($binders, $inBinders);
236
                    return $inSQL;
237
                } else {
238
                    $binder = new Binder($name, $parameter[$index]);
239
                    $binders[] = $binder;
240
                    return ':'.$binder->getName();
241
                }
242
            }
243
            return $match[0];
244
        }, $sql);
245
        return [$query, $binders];
246
    }
247
248
    /**
249
     * 合并绑定工具
250
     *
251
     * @param Binder[] $binderArray
252
     * @param array $parameter
253
     * @return Binder[]
254
     */
255
    protected function mergeBinder(array $binderArray, array $parameter)
256
    {
257
        foreach ($parameter as $key => $value) {
258
            if (! ($value instanceof Binder)) {
259
                $value = new Binder($key, $value);
260
            }
261
            if (!in_array($value, $binderArray)) {
262
                $binderArray[] = $value;
263
            }
264
        }
265
        return $binderArray;
266
    }
267
}
268