1 | <?php |
||||
2 | |||||
3 | namespace Kir\MySQL\Builder; |
||||
4 | |||||
5 | use Kir\MySQL\Builder\Internal\Types; |
||||
6 | use Kir\MySQL\Tools\AliasReplacer; |
||||
7 | use RuntimeException; |
||||
8 | use Traversable; |
||||
9 | use UnexpectedValueException; |
||||
10 | |||||
11 | /** |
||||
12 | * @phpstan-import-type DBParameterValueType from Types |
||||
13 | */ |
||||
14 | abstract class Insert extends InsertUpdateStatement { |
||||
15 | /** @var array<string|int, DBParameterValueType> */ |
||||
16 | private array $fields = []; |
||||
17 | /** @var array<string|int, DBParameterValueType> */ |
||||
18 | private array $update = []; |
||||
19 | private ?string $table = null; |
||||
20 | private ?string $keyField = null; |
||||
21 | private bool $ignore = false; |
||||
22 | private ?Select $from = null; |
||||
23 | |||||
24 | /** |
||||
25 | * @param string $table |
||||
26 | * @return $this |
||||
27 | */ |
||||
28 | public function into(string $table) { |
||||
29 | $this->table = $table; |
||||
30 | return $this; |
||||
31 | } |
||||
32 | |||||
33 | /** |
||||
34 | * @param bool $value |
||||
35 | * @return $this |
||||
36 | */ |
||||
37 | public function setIgnore(bool $value = true) { |
||||
38 | $this->ignore = $value; |
||||
39 | return $this; |
||||
40 | } |
||||
41 | |||||
42 | /** |
||||
43 | * Legt den Primaerschluessel fest. |
||||
44 | * Wenn bei einem Insert der Primaerschluessel mitgegeben wird, dann wird dieser statt der LastInsertId |
||||
45 | * zurueckgegeben |
||||
46 | * |
||||
47 | * @param string $field |
||||
48 | * @return $this |
||||
49 | */ |
||||
50 | public function setKey(string $field) { |
||||
51 | $this->keyField = $field; |
||||
52 | return $this; |
||||
53 | } |
||||
54 | |||||
55 | /** |
||||
56 | * @param string $field |
||||
57 | * @param DBParameterValueType $value |
||||
0 ignored issues
–
show
|
|||||
58 | * @return $this |
||||
59 | */ |
||||
60 | public function add(string $field, $value) { |
||||
61 | $this->fields = $this->addTo($this->fields, $field, $value); |
||||
62 | return $this; |
||||
63 | } |
||||
64 | |||||
65 | /** |
||||
66 | * @param string $field |
||||
67 | * @param DBParameterValueType $value |
||||
68 | * @return $this |
||||
69 | */ |
||||
70 | public function update(string $field, $value) { |
||||
71 | $this->update = $this->addTo($this->update, $field, $value); |
||||
72 | return $this; |
||||
73 | } |
||||
74 | |||||
75 | /** |
||||
76 | * @param string $field |
||||
77 | * @param DBParameterValueType $value |
||||
78 | * @return $this |
||||
79 | */ |
||||
80 | public function addOrUpdate(string $field, $value) { |
||||
81 | $this->add($field, $value); |
||||
82 | $this->update($field, $value); |
||||
83 | return $this; |
||||
84 | } |
||||
85 | |||||
86 | /** |
||||
87 | * @param string $str |
||||
88 | * @param DBParameterValueType ...$args |
||||
89 | * @return $this |
||||
90 | */ |
||||
91 | public function addExpr(string $str, ...$args) { |
||||
92 | if(count($args) > 0) { |
||||
93 | $this->fields[] = func_get_args(); |
||||
94 | } else { |
||||
95 | $this->fields[] = $str; |
||||
96 | } |
||||
97 | return $this; |
||||
98 | } |
||||
99 | |||||
100 | /** |
||||
101 | * @param string $str |
||||
102 | * @param DBParameterValueType ...$args |
||||
103 | * @return $this |
||||
104 | */ |
||||
105 | public function updateExpr(string $str, ...$args) { |
||||
106 | if(count($args) > 0) { |
||||
107 | $this->update[] = func_get_args(); |
||||
108 | } else { |
||||
109 | $this->update[] = $str; |
||||
110 | } |
||||
111 | return $this; |
||||
112 | } |
||||
113 | |||||
114 | /** |
||||
115 | * @param string $expr |
||||
116 | * @param DBParameterValueType ...$args |
||||
117 | * @return $this |
||||
118 | */ |
||||
119 | public function addOrUpdateExpr(string $expr, ...$args) { |
||||
120 | if(count($args) > 0) { |
||||
121 | $this->fields[] = func_get_args(); |
||||
122 | $this->update[] = func_get_args(); |
||||
123 | } else { |
||||
124 | $this->fields[] = $expr; |
||||
125 | $this->update[] = $expr; |
||||
126 | } |
||||
127 | return $this; |
||||
128 | } |
||||
129 | |||||
130 | /** |
||||
131 | * @param array<string, DBParameterValueType> $data |
||||
132 | * @param null|string[] $mask |
||||
133 | * @param null|string[] $excludeFields |
||||
134 | * @return $this |
||||
135 | */ |
||||
136 | public function addAll(array $data, ?array $mask = null, ?array $excludeFields = null) { |
||||
137 | $this->addAllTo($data, $mask, $excludeFields, function ($field, $value) { |
||||
138 | $this->add($field, $value); |
||||
139 | }); |
||||
140 | return $this; |
||||
141 | } |
||||
142 | |||||
143 | /** |
||||
144 | * @param array<string, DBParameterValueType> $data |
||||
145 | * @param null|string[] $mask |
||||
146 | * @param null|string[] $excludeFields |
||||
147 | * @return $this |
||||
148 | */ |
||||
149 | public function updateAll(array $data, ?array $mask = null, ?array $excludeFields = null) { |
||||
150 | $this->addAllTo($data, $mask, $excludeFields, function ($field, $value) { |
||||
151 | if($field !== $this->keyField) { |
||||
152 | $this->update($field, $value); |
||||
153 | } |
||||
154 | }); |
||||
155 | return $this; |
||||
156 | } |
||||
157 | |||||
158 | /** |
||||
159 | * @param array<string, DBParameterValueType> $data |
||||
160 | * @param null|string[] $mask |
||||
161 | * @param array<int, string>|null $excludeFields |
||||
162 | * @return $this |
||||
163 | */ |
||||
164 | public function addOrUpdateAll(array $data, ?array $mask = null, ?array $excludeFields = null) { |
||||
165 | $this->addAll($data, $mask, $excludeFields); |
||||
166 | $this->updateAll($data, $mask, $excludeFields); |
||||
167 | return $this; |
||||
168 | } |
||||
169 | |||||
170 | /** |
||||
171 | * @param Select $select |
||||
172 | * @return $this |
||||
173 | */ |
||||
174 | public function from(Select $select) { |
||||
175 | $this->from = $select; |
||||
176 | return $this; |
||||
177 | } |
||||
178 | |||||
179 | /** |
||||
180 | * @param iterable<int, array<string, mixed>>|Traversable<int, array<string, mixed>> $rows |
||||
181 | * @return int[] Insert IDs |
||||
182 | */ |
||||
183 | abstract public function insertRows(iterable $rows); |
||||
184 | |||||
185 | /** |
||||
186 | * @param array<string, mixed> $params |
||||
187 | * @return int |
||||
188 | */ |
||||
189 | abstract public function run(array $params = []): int; |
||||
190 | |||||
191 | /** |
||||
192 | * @return string |
||||
193 | */ |
||||
194 | public function __toString(): string { |
||||
195 | if($this->table === null) { |
||||
196 | throw new RuntimeException('Specify a table-name'); |
||||
197 | } |
||||
198 | |||||
199 | $tableName = (new AliasReplacer($this->db()->getAliasRegistry()))->replace($this->table); |
||||
200 | |||||
201 | $queryArr = []; |
||||
202 | $ignoreStr = $this->ignore ? ' IGNORE' : ''; |
||||
203 | $queryArr[] = "INSERT{$ignoreStr} INTO\n\t{$tableName}\n"; |
||||
204 | |||||
205 | if($this->from !== null) { |
||||
206 | $fields = $this->from->getFields(); |
||||
207 | $queryArr[] = sprintf("\t(%s)\n", implode(', ', array_keys($fields))); |
||||
208 | $queryArr[] = $this->from; |
||||
209 | } else { |
||||
210 | $fields = $this->fields; |
||||
211 | $insertData = $this->buildFieldList($fields); |
||||
212 | if(!count($insertData)) { |
||||
213 | throw new RuntimeException('No field-data found'); |
||||
214 | } |
||||
215 | $queryArr[] = sprintf("SET\n%s\n", implode(",\n", $insertData)); |
||||
216 | } |
||||
217 | |||||
218 | $updateData = $this->buildUpdate(); |
||||
219 | if($updateData) { |
||||
220 | $queryArr[] = "{$updateData}\n"; |
||||
221 | } |
||||
222 | |||||
223 | return implode('', $queryArr); |
||||
224 | } |
||||
225 | |||||
226 | /** |
||||
227 | * @param array<string|int, mixed> $fields |
||||
228 | * @param string $field |
||||
229 | * @param DBParameterValueType $value |
||||
230 | * @return array<string|int, mixed> |
||||
231 | */ |
||||
232 | private function addTo(array $fields, string $field, $value): array { |
||||
233 | if(!$this->isFieldNameValid($field)) { |
||||
234 | throw new UnexpectedValueException('Field name is invalid'); |
||||
235 | } |
||||
236 | $sqlField = $field; |
||||
237 | $sqlValue = $this->db()->quote($value); |
||||
238 | $fields[$sqlField] = $sqlValue; |
||||
239 | return $fields; |
||||
240 | } |
||||
241 | |||||
242 | /** |
||||
243 | * @param array<string, mixed> $data |
||||
244 | * @param string[]|null $mask |
||||
245 | * @param string[]|null $excludeFields |
||||
246 | * @param callable(string, mixed): void $fn |
||||
247 | */ |
||||
248 | private function addAllTo(array $data, ?array $mask, ?array $excludeFields, $fn): void { |
||||
249 | if($mask !== null) { |
||||
0 ignored issues
–
show
|
|||||
250 | $data = array_intersect_key($data, array_combine($mask, $mask)); |
||||
251 | } |
||||
252 | if($excludeFields !== null) { |
||||
0 ignored issues
–
show
|
|||||
253 | foreach($excludeFields as $excludeField) { |
||||
254 | if(array_key_exists($excludeField, $data)) { |
||||
255 | unset($data[$excludeField]); |
||||
256 | } |
||||
257 | } |
||||
258 | } |
||||
259 | $data = $this->clearValues($data); |
||||
260 | foreach($data as $field => $value) { |
||||
261 | $fn($field, $value); |
||||
262 | } |
||||
263 | } |
||||
264 | |||||
265 | /** |
||||
266 | * @return string |
||||
267 | */ |
||||
268 | private function buildUpdate(): string { |
||||
269 | $queryArr = []; |
||||
270 | if(!empty($this->update)) { |
||||
271 | $queryArr[] = "ON DUPLICATE KEY UPDATE\n"; |
||||
272 | $updateArr = []; |
||||
273 | if($this->keyField !== null) { |
||||
274 | $updateArr[] = "\t`{$this->keyField}` = LAST_INSERT_ID({$this->keyField})"; |
||||
275 | } |
||||
276 | $updateArr = $this->buildFieldList($this->update, $updateArr); |
||||
277 | |||||
278 | $queryArr[] = implode(",\n", $updateArr); |
||||
279 | } |
||||
280 | return implode('', $queryArr); |
||||
281 | } |
||||
282 | |||||
283 | /** |
||||
284 | * @param string $fieldName |
||||
285 | * @return bool |
||||
286 | */ |
||||
287 | private function isFieldNameValid(string $fieldName): bool { |
||||
288 | return !(is_numeric($fieldName) || !is_scalar($fieldName)); |
||||
289 | } |
||||
290 | |||||
291 | /** |
||||
292 | * @param array<string, mixed> $values |
||||
293 | * @return array<string, mixed> |
||||
294 | */ |
||||
295 | private function clearValues(array $values): array { |
||||
296 | if(!count($values)) { |
||||
297 | return []; |
||||
298 | } |
||||
299 | |||||
300 | $tableName = (new AliasReplacer($this->db()->getAliasRegistry()))->replace($this->table); |
||||
0 ignored issues
–
show
It seems like
$this->table can also be of type null ; however, parameter $name of Kir\MySQL\Tools\AliasReplacer::replace() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
301 | $fields = $this->db()->getTableFields($tableName); |
||||
302 | $result = []; |
||||
303 | |||||
304 | foreach($values as $fieldName => $fieldValue) { |
||||
305 | if(in_array($fieldName, $fields)) { |
||||
306 | $result[$fieldName] = $fieldValue; |
||||
307 | } |
||||
308 | } |
||||
309 | |||||
310 | return $result; |
||||
311 | } |
||||
312 | } |
||||
313 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths