Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
90.96% |
161 / 177 |
|
73.68% |
28 / 38 |
CRAP | |
0.00% |
0 / 1 |
DatabaseContext | |
90.96% |
161 / 177 |
|
73.68% |
28 / 38 |
84.73 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
getErrorMessage | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setParameters | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
4.25 | |||
executeStatement | |
92.86% |
13 / 14 |
|
0.00% |
0 / 1 |
5.01 | |||
getColumns | |
90.91% |
10 / 11 |
|
0.00% |
0 / 1 |
4.01 | |||
convertRowResult | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
convertTableResult | |
85.71% |
6 / 7 |
|
0.00% |
0 / 1 |
2.01 | |||
convertSequenceResult | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
disposeImpl | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
inTransaction | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
beginTransaction | |
71.43% |
5 / 7 |
|
0.00% |
0 / 1 |
4.37 | |||
commit | |
71.43% |
5 / 7 |
|
0.00% |
0 / 1 |
4.37 | |||
rollback | |
66.67% |
4 / 6 |
|
0.00% |
0 / 1 |
4.59 | |||
transaction | |
63.64% |
7 / 11 |
|
0.00% |
0 / 1 |
3.43 | |||
escapeLike | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
escapeValue | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
fetch | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
query | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
queryFirst | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
queryFirstOrNull | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
querySingle | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
querySingleOrNull | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
throwIfInvalidOrdered | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
selectOrdered | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
throwIfInvalidSingleCount | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
selectSingleCount | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
2.02 | |||
execute | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
throwIfInvalidInsert | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
insert | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
insertSingle | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
throwIfInvalidUpdate | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
update | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
updateByKey | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
updateByKeyOrNothing | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
2.03 | |||
throwIfInvalidDelete | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
delete | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
deleteByKey | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
deleteByKeyOrNothing | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace PeServer\Core\Database; |
6 | |
7 | use Exception; |
8 | use PDO; |
9 | use PDOException; |
10 | use PDOStatement; |
11 | use Throwable; |
12 | use PeServer\Core\Collection\Arr; |
13 | use PeServer\Core\Database\ConnectionSetting; |
14 | use PeServer\Core\Database\DatabaseColumn; |
15 | use PeServer\Core\Database\DatabaseSequenceResult; |
16 | use PeServer\Core\Database\DatabaseTableResult; |
17 | use PeServer\Core\Database\IDatabaseTransactionContext; |
18 | use PeServer\Core\DisposerBase; |
19 | use PeServer\Core\Log\ILogger; |
20 | use PeServer\Core\Regex; |
21 | use PeServer\Core\Text; |
22 | use PeServer\Core\Throws\DatabaseException; |
23 | use PeServer\Core\Throws\NotImplementedException; |
24 | use PeServer\Core\Throws\SqlException; |
25 | use PeServer\Core\Throws\Throws; |
26 | use PeServer\Core\Throws\TransactionException; |
27 | use PeServer\Core\TypeUtility; |
28 | |
29 | /** |
30 | * DB処理。 |
31 | */ |
32 | class DatabaseContext extends DisposerBase implements IDatabaseTransactionContext |
33 | { |
34 | #region variable |
35 | |
36 | /** |
37 | * 接続処理。 |
38 | */ |
39 | protected readonly PDO $pdo; |
40 | |
41 | /** |
42 | * ロガー |
43 | */ |
44 | protected readonly ILogger $logger; |
45 | |
46 | protected Regex $regex; |
47 | |
48 | #endregion |
49 | |
50 | /** |
51 | * 生成。 |
52 | * |
53 | * @param ConnectionSetting $setting |
54 | * @param ILogger $logger |
55 | * @throws DatabaseException |
56 | */ |
57 | public function __construct(ConnectionSetting $setting, ILogger $logger) |
58 | { |
59 | $this->logger = $logger; |
60 | $this->regex = new Regex(); |
61 | |
62 | $this->pdo = Throws::wrap(PDOException::class, DatabaseException::class, fn () => new PDO($setting->dsn, $setting->user, $setting->password, $setting->options)); |
63 | $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //cspell:disable-line |
64 | $this->pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); //cspell:disable-line |
65 | } |
66 | |
67 | #region function |
68 | |
69 | /** |
70 | * 直近のエラーメッセージを取得。 |
71 | * |
72 | * @return string |
73 | */ |
74 | private function getErrorMessage(): string |
75 | { |
76 | return Text::dump($this->pdo->errorInfo()); |
77 | } |
78 | |
79 | /** |
80 | * バインド実行。 |
81 | * |
82 | * @param PDOStatement $statement |
83 | * @param array<string|int,string|int|bool>|null $parameters |
84 | * @phpstan-param array<array-key,DatabaseBindValueAlias>|null $parameters |
85 | * @return void |
86 | */ |
87 | private function setParameters(PDOStatement $statement, ?array $parameters): void |
88 | { |
89 | if ($parameters !== null) { |
90 | foreach ($parameters as $key => $value) { |
91 | if (!$statement->bindValue($key, $value)) { |
92 | throw new SqlException('$key: ' . $key . ' -> ' . TypeUtility::getType($value)); |
93 | } |
94 | } |
95 | } |
96 | } |
97 | |
98 | /** |
99 | * 文を実行。 |
100 | * |
101 | * @param string $statement |
102 | * @phpstan-param literal-string $statement |
103 | * @phpstan-param array<array-key,DatabaseBindValueAlias>|null $parameters |
104 | * @return PDOStatement |
105 | * @throws SqlException 実行失敗。 |
106 | */ |
107 | private function executeStatement(string $statement, ?array $parameters): PDOStatement |
108 | { |
109 | $this->throwIfDisposed(); |
110 | |
111 | /** @var PDOStatement|false|null */ |
112 | $query = null; |
113 | |
114 | try { |
115 | $query = $this->pdo->prepare($statement); |
116 | if ($query === false) { |
117 | throw new SqlException($this->getErrorMessage()); |
118 | } |
119 | |
120 | $this->setParameters($query, $parameters); |
121 | } catch (PDOException $ex) { |
122 | Throws::reThrow(SqlException::class, $ex); |
123 | } |
124 | |
125 | $this->logger->trace($query, $parameters); |
126 | |
127 | try { |
128 | if (!$query->execute()) { |
129 | throw new DatabaseException($this->getErrorMessage()); |
130 | } |
131 | } catch (PDOException $ex) { |
132 | Throws::reThrow(DatabaseException::class, $ex); |
133 | } |
134 | |
135 | return $query; |
136 | } |
137 | |
138 | /** |
139 | * カラム情報一覧の取得。 |
140 | * |
141 | * カラム情報は取得できたものだけを返す。 |
142 | * |
143 | * @param PDOStatement $pdoStatement |
144 | * @return DatabaseColumn[] 取得できたカラム一覧。 |
145 | */ |
146 | private function getColumns(PDOStatement $pdoStatement): array |
147 | { |
148 | $count = $pdoStatement->columnCount(); |
149 | if ($count === 0) { |
150 | return []; |
151 | } |
152 | |
153 | $columns = []; |
154 | |
155 | for ($i = 0; $i < $count; $i++) { |
156 | $meta = $pdoStatement->getColumnMeta($i); |
157 | if ($meta === false) { |
158 | continue; |
159 | } |
160 | |
161 | $column = DatabaseColumn::create($meta); |
162 | $columns[] = $column; |
163 | } |
164 | |
165 | return $columns; |
166 | } |
167 | |
168 | /** |
169 | * 単一行データに変換。 |
170 | * |
171 | * データが存在しない場合、`DatabaseRowResult->field` はから配列となるが、あくまで `Database` 内限定のデータ状態となる。 |
172 | * |
173 | * @template TFieldArray of FieldArrayAlias |
174 | * @param PDOStatement $pdoStatement |
175 | * @return DatabaseRowResult |
176 | * @phpstan-return DatabaseRowResult<TFieldArray> |
177 | */ |
178 | private function convertRowResult(PDOStatement $pdoStatement): DatabaseRowResult |
179 | { |
180 | $columns = $this->getColumns($pdoStatement); |
181 | |
182 | $resultCount = $pdoStatement->rowCount(); |
183 | |
184 | /** @phpstan-var TFieldArray|false */ |
185 | $row = $pdoStatement->fetch(); |
186 | if ($row === false) { |
187 | return new DatabaseRowResult($columns, $resultCount, []); //@phpstan-ignore-line 空データを本クラス内のみ許容 |
188 | } |
189 | |
190 | return new DatabaseRowResult($columns, $resultCount, $row); //@phpstan-ignore-line 空じゃないでしょ・・・ |
191 | } |
192 | |
193 | /** |
194 | * データセットに変換。 |
195 | * |
196 | * @template TFieldArray of FieldArrayAlias |
197 | * @param PDOStatement $pdoStatement |
198 | * @return DatabaseTableResult |
199 | * @phpstan-return DatabaseTableResult<TFieldArray> |
200 | */ |
201 | private function convertTableResult(PDOStatement $pdoStatement): DatabaseTableResult |
202 | { |
203 | $columns = $this->getColumns($pdoStatement); |
204 | |
205 | $resultCount = $pdoStatement->rowCount(); |
206 | |
207 | $rows = $pdoStatement->fetchAll(); |
208 | // @phpstan-ignore-next-line: [PHP_VERSION] |
209 | if ($rows === false) { |
210 | throw new DatabaseException($this->getErrorMessage()); |
211 | } |
212 | |
213 | $result = new DatabaseTableResult($columns, $resultCount, $rows); |
214 | |
215 | return $result; //@phpstan-ignore-line 空フィールドはない |
216 | } |
217 | |
218 | /** |
219 | * 逐次データセットに変換。 |
220 | * |
221 | * @template TFieldArray of FieldArrayAlias |
222 | * @param PDOStatement $pdoStatement |
223 | * @return DatabaseSequenceResult |
224 | * @phpstan-return DatabaseSequenceResult<TFieldArray> |
225 | */ |
226 | private function convertSequenceResult(PDOStatement $pdoStatement): DatabaseSequenceResult |
227 | { |
228 | $columns = $this->getColumns($pdoStatement); |
229 | |
230 | /** @var DatabaseSequenceResult<TFieldArray> */ |
231 | $result = new DatabaseSequenceResult($columns, $pdoStatement); |
232 | |
233 | return $result; |
234 | } |
235 | |
236 | #endregion |
237 | |
238 | #region DisposerBase |
239 | |
240 | protected function disposeImpl(): void |
241 | { |
242 | if ($this->inTransaction()) { |
243 | $this->rollback(); |
244 | } |
245 | |
246 | parent::disposeImpl(); |
247 | } |
248 | |
249 | #endregion |
250 | |
251 | #region IDatabaseTransactionContext |
252 | |
253 | public function inTransaction(): bool |
254 | { |
255 | $this->throwIfDisposed(); |
256 | |
257 | return $this->pdo->inTransaction(); |
258 | } |
259 | |
260 | public function beginTransaction(): void |
261 | { |
262 | $this->throwIfDisposed(); |
263 | |
264 | if ($this->inTransaction()) { |
265 | throw new TransactionException(); |
266 | } |
267 | |
268 | try { |
269 | if (!$this->pdo->beginTransaction()) { |
270 | throw new TransactionException($this->getErrorMessage()); |
271 | } |
272 | } catch (PDOException $ex) { |
273 | Throws::reThrow(TransactionException::class, $ex, $this->getErrorMessage()); |
274 | } |
275 | } |
276 | |
277 | public function commit(): void |
278 | { |
279 | $this->throwIfDisposed(); |
280 | |
281 | if (!$this->inTransaction()) { |
282 | throw new TransactionException(); |
283 | } |
284 | |
285 | try { |
286 | if (!$this->pdo->commit()) { |
287 | throw new TransactionException($this->getErrorMessage()); |
288 | } |
289 | } catch (PDOException $ex) { |
290 | Throws::reThrow(TransactionException::class, $ex, $this->getErrorMessage()); |
291 | } |
292 | } |
293 | |
294 | public function rollback(): void |
295 | { |
296 | if (!$this->inTransaction()) { |
297 | throw new TransactionException(); |
298 | } |
299 | |
300 | try { |
301 | if (!$this->pdo->rollBack()) { |
302 | throw new TransactionException($this->getErrorMessage()); |
303 | } |
304 | } catch (PDOException $ex) { |
305 | Throws::reThrow(TransactionException::class, $ex, $this->getErrorMessage()); |
306 | } |
307 | } |
308 | |
309 | public function transaction(callable $callback): bool |
310 | { |
311 | try { |
312 | $this->beginTransaction(); |
313 | |
314 | $result = $callback($this); |
315 | if ($result) { |
316 | $this->commit(); |
317 | return true; |
318 | } else { |
319 | $this->rollback(); |
320 | } |
321 | } catch (Throwable $ex) { |
322 | $this->logger->error($ex); |
323 | $this->rollback(); |
324 | Throws::reThrow(DatabaseException::class, $ex); |
325 | } |
326 | |
327 | return false; |
328 | } |
329 | |
330 | public function escapeLike(string $value): string |
331 | { |
332 | return Text::replace( |
333 | $value, |
334 | ["\\", '%', '_'], |
335 | ["\\\\", '\\%', '\\_'], |
336 | ); |
337 | } |
338 | |
339 | public function escapeValue(mixed $value): string |
340 | { |
341 | if ($value === null) { |
342 | return 'null'; |
343 | } |
344 | |
345 | return $this->pdo->quote($value); |
346 | } |
347 | |
348 | /** |
349 | * @template TFieldArray of FieldArrayAlias |
350 | * @phpstan-return DatabaseSequenceResult<TFieldArray> |
351 | */ |
352 | public function fetch(string $statement, ?array $parameters = null): DatabaseSequenceResult |
353 | { |
354 | $query = $this->executeStatement($statement, $parameters); |
355 | |
356 | /** @phpstan-var DatabaseSequenceResult<TFieldArray> */ |
357 | $result = $this->convertSequenceResult($query); |
358 | |
359 | return $result; |
360 | } |
361 | |
362 | /** |
363 | * @template TFieldArray of FieldArrayAlias |
364 | * @phpstan-return DatabaseTableResult<TFieldArray> |
365 | */ |
366 | public function query(string $statement, ?array $parameters = null): DatabaseTableResult |
367 | { |
368 | $query = $this->executeStatement($statement, $parameters); |
369 | |
370 | /** @phpstan-var DatabaseTableResult<TFieldArray> */ |
371 | $result = $this->convertTableResult($query); |
372 | |
373 | return $result; |
374 | } |
375 | |
376 | /** |
377 | * @template TFieldArray of FieldArrayAlias |
378 | * @phpstan-return DatabaseRowResult<TFieldArray> |
379 | */ |
380 | public function queryFirst(string $statement, ?array $parameters = null): DatabaseRowResult |
381 | { |
382 | $query = $this->executeStatement($statement, $parameters); |
383 | |
384 | /** @phpstan-var DatabaseRowResult<TFieldArray> */ |
385 | $result = $this->convertRowResult($query); |
386 | if (Arr::isNullOrEmpty($result->fields)) { |
387 | throw new DatabaseException($this->getErrorMessage()); |
388 | } |
389 | |
390 | return $result; |
391 | } |
392 | |
393 | /** |
394 | * @template TFieldArray of FieldArrayAlias |
395 | * @phpstan-return DatabaseRowResult<TFieldArray> |
396 | */ |
397 | public function queryFirstOrNull(string $statement, ?array $parameters = null): ?DatabaseRowResult |
398 | { |
399 | $query = $this->executeStatement($statement, $parameters); |
400 | |
401 | /** @phpstan-var DatabaseRowResult<TFieldArray> */ |
402 | $result = $this->convertRowResult($query); |
403 | if (Arr::isNullOrEmpty($result->fields)) { |
404 | return null; |
405 | } |
406 | |
407 | return $result; |
408 | } |
409 | |
410 | /** |
411 | * @template TFieldArray of FieldArrayAlias |
412 | * @phpstan-return DatabaseRowResult<TFieldArray> |
413 | */ |
414 | public function querySingle(string $statement, ?array $parameters = null): DatabaseRowResult |
415 | { |
416 | $query = $this->executeStatement($statement, $parameters); |
417 | |
418 | /** @phpstan-var DatabaseRowResult<TFieldArray> */ |
419 | $result = $this->convertRowResult($query); |
420 | if (Arr::isNullOrEmpty($result->fields)) { |
421 | throw new DatabaseException($this->getErrorMessage()); |
422 | } |
423 | |
424 | $next = $query->fetch(); |
425 | if ($next !== false) { |
426 | throw new DatabaseException($this->getErrorMessage()); |
427 | } |
428 | |
429 | return $result; |
430 | } |
431 | |
432 | /** |
433 | * @template TFieldArray of FieldArrayAlias |
434 | * @phpstan-return DatabaseRowResult<TFieldArray> |
435 | */ |
436 | public function querySingleOrNull(string $statement, ?array $parameters = null): ?DatabaseRowResult |
437 | { |
438 | $query = $this->executeStatement($statement, $parameters); |
439 | |
440 | /** @phpstan-var DatabaseRowResult<TFieldArray> */ |
441 | $result = $this->convertRowResult($query); |
442 | if (Arr::isNullOrEmpty($result->fields)) { |
443 | return null; |
444 | } |
445 | |
446 | $next = $query->fetch(); |
447 | if ($next !== false) { |
448 | return null; |
449 | } |
450 | |
451 | return $result; |
452 | } |
453 | |
454 | /** |
455 | * ソートを強制。 |
456 | * |
457 | * 単純な文字列処理のため無理な時は無理。 |
458 | * |
459 | * @param string $statement |
460 | * @return void |
461 | */ |
462 | protected function throwIfInvalidOrdered(string $statement): void |
463 | { |
464 | if (!$this->regex->isMatch($statement, '/\\border\\s+by\\b/i')) { |
465 | throw new SqlException('order by'); |
466 | } |
467 | } |
468 | |
469 | /** |
470 | * @template TFieldArray of FieldArrayAlias |
471 | * @phpstan-return DatabaseTableResult<TFieldArray> |
472 | */ |
473 | public function selectOrdered(string $statement, ?array $parameters = null): DatabaseTableResult |
474 | { |
475 | $this->throwIfInvalidOrdered($statement); |
476 | |
477 | /** @phpstan-var DatabaseTableResult<TFieldArray> */ |
478 | $result = $this->query($statement, $parameters); |
479 | |
480 | return $result; |
481 | } |
482 | |
483 | /** |
484 | * 単独件数取得を強制。 |
485 | * |
486 | * 単純な文字列処理のため無理な時は無理。 |
487 | * |
488 | * @param string $statement |
489 | * @return void |
490 | */ |
491 | protected function throwIfInvalidSingleCount(string $statement): void |
492 | { |
493 | if (!$this->regex->isMatch($statement, '/\\bselect\\s+count\\s*\\(/i')) { |
494 | throw new SqlException('select count'); |
495 | } |
496 | } |
497 | |
498 | public function selectSingleCount(string $statement, ?array $parameters = null): int |
499 | { |
500 | $this->throwIfInvalidSingleCount($statement); |
501 | |
502 | /** @-var array<string,mixed> */ |
503 | $result = $this->queryFirst($statement, $parameters); |
504 | $val = strval(current($result->fields)); |
505 | if (TypeUtility::tryParseInteger($val, $count)) { |
506 | /** @var non-negative-int */ |
507 | return $count; |
508 | } |
509 | |
510 | throw new DatabaseException(); |
511 | } |
512 | |
513 | /** |
514 | * @template TFieldArray of FieldArrayAlias |
515 | * @phpstan-return DatabaseTableResult<TFieldArray> |
516 | */ |
517 | public function execute(string $statement, ?array $parameters = null): DatabaseTableResult |
518 | { |
519 | $query = $this->executeStatement($statement, $parameters); |
520 | |
521 | /** @phpstan-var DatabaseTableResult<TFieldArray> */ |
522 | $result = $this->convertTableResult($query); |
523 | |
524 | return $result; |
525 | } |
526 | |
527 | /** |
528 | * INSERT文を強制。 |
529 | * |
530 | * 単純な文字列処理のため無理な時は無理。 |
531 | * |
532 | * @param string $statement |
533 | * @return void |
534 | */ |
535 | protected function throwIfInvalidInsert(string $statement): void |
536 | { |
537 | if (!$this->regex->isMatch($statement, '/\\binsert\\b/i')) { |
538 | throw new SqlException('insert'); |
539 | } |
540 | } |
541 | |
542 | public function insert(string $statement, ?array $parameters = null): int |
543 | { |
544 | $this->throwIfInvalidInsert($statement); |
545 | return $this->execute($statement, $parameters)->getResultCount(); |
546 | } |
547 | |
548 | public function insertSingle(string $statement, ?array $parameters = null): void |
549 | { |
550 | $this->throwIfInvalidInsert($statement); |
551 | $result = $this->execute($statement, $parameters); |
552 | if ($result->getResultCount() !== 1) { |
553 | throw new DatabaseException(); |
554 | } |
555 | } |
556 | |
557 | /** |
558 | * UPDATE文を強制。 |
559 | * |
560 | * 単純な文字列処理のため無理な時は無理。 |
561 | * |
562 | * @param string $statement |
563 | * @return void |
564 | */ |
565 | protected function throwIfInvalidUpdate(string $statement): void |
566 | { |
567 | if (!$this->regex->isMatch($statement, '/\\bupdate\\b/i')) { |
568 | throw new SqlException('update'); |
569 | } |
570 | } |
571 | |
572 | public function update(string $statement, ?array $parameters = null): int |
573 | { |
574 | $this->throwIfInvalidUpdate($statement); |
575 | return $this->execute($statement, $parameters)->getResultCount(); |
576 | } |
577 | |
578 | public function updateByKey(string $statement, ?array $parameters = null): void |
579 | { |
580 | $this->throwIfInvalidUpdate($statement); |
581 | $result = $this->execute($statement, $parameters); |
582 | if ($result->getResultCount() !== 1) { |
583 | throw new DatabaseException(); |
584 | } |
585 | } |
586 | |
587 | public function updateByKeyOrNothing(string $statement, ?array $parameters = null): bool |
588 | { |
589 | $this->throwIfInvalidUpdate($statement); |
590 | $result = $this->execute($statement, $parameters); |
591 | if (1 < $result->getResultCount()) { |
592 | throw new DatabaseException(); |
593 | } |
594 | |
595 | return $result->getResultCount() === 1; |
596 | } |
597 | |
598 | /** |
599 | * DELETE文を強制。 |
600 | * |
601 | * 単純な文字列処理のため無理な時は無理。 |
602 | * |
603 | * @param string $statement |
604 | * @return void |
605 | */ |
606 | protected function throwIfInvalidDelete(string $statement): void |
607 | { |
608 | if (!$this->regex->isMatch($statement, '/\\bdelete\\b/i')) { |
609 | throw new SqlException('delete'); |
610 | } |
611 | } |
612 | |
613 | public function delete(string $statement, ?array $parameters = null): int |
614 | { |
615 | $this->throwIfInvalidDelete($statement); |
616 | return $this->execute($statement, $parameters)->getResultCount(); |
617 | } |
618 | |
619 | public function deleteByKey(string $statement, ?array $parameters = null): void |
620 | { |
621 | $this->throwIfInvalidDelete($statement); |
622 | $result = $this->execute($statement, $parameters); |
623 | if ($result->getResultCount() !== 1) { |
624 | throw new DatabaseException(); |
625 | } |
626 | } |
627 | |
628 | public function deleteByKeyOrNothing(string $statement, ?array $parameters = null): bool |
629 | { |
630 | $this->throwIfInvalidDelete($statement); |
631 | $result = $this->execute($statement, $parameters); |
632 | if (1 < $result->getResultCount()) { |
633 | throw new DatabaseException(); |
634 | } |
635 | |
636 | return $result->getResultCount() === 1; |
637 | } |
638 | |
639 | #endregion |
640 | } |