1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace HexMakina\Crudites; |
4
|
|
|
|
5
|
|
|
use HexMakina\BlackBox\Database\QueryInterface; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* The CruditesExceptionFactory class make CruditesExcepton, a custom exception class that extends the built-in \Exception class in PHP. |
9
|
|
|
* The purpose of this class is to provide a more descriptive and meaningful error message when an exception is thrown in the CRUD operations. |
10
|
|
|
* It takes a message, an optional error code, and an optional previous exception as its parameters. |
11
|
|
|
* It also provides a method called transcript() which extracts error information from a QueryInterface object |
12
|
|
|
* and sets the error message to a more readable and understandable format. |
13
|
|
|
* |
14
|
|
|
* This is useful when dealing with database errors that may not be very clear or descriptive on their own. |
15
|
|
|
*/ |
16
|
|
|
|
17
|
|
|
class CruditesExceptionFactory |
18
|
|
|
{ |
19
|
|
|
public static function make(QueryInterface $query, \PDOException $exception = null): CruditesException |
20
|
|
|
{ |
21
|
|
|
$errorInfo = null; |
22
|
|
|
|
23
|
|
|
if (!$query->isSuccess()) |
24
|
|
|
$errorInfo = $query->errorInfo(); |
|
|
|
|
25
|
|
|
elseif ($exception !== null) |
26
|
|
|
$errorInfo = $exception->errorInfo; |
27
|
|
|
|
28
|
|
|
if (!is_array($errorInfo)) |
29
|
|
|
return new CruditesException('ERROR_INFO_UNAVAILABLE', 0, $exception); |
30
|
|
|
|
31
|
|
|
list($message, $code) = self::transcript($errorInfo); |
32
|
|
|
|
33
|
|
|
// TODO: losing the parsing work from transcript. IMPROVE |
34
|
|
|
if (is_array($message)) |
35
|
|
|
$message = array_shift($message); |
36
|
|
|
|
37
|
|
|
return new CruditesException($message, $code, null); // exception could reveal database credentials |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Attempts to humanize database errors |
42
|
|
|
* @param ?array errorInfo, consists of at least the following fields: |
|
|
|
|
43
|
|
|
* 0 SQLSTATE error code (a five characters alphanumeric identifier defined in the ANSI SQL standard). |
44
|
|
|
* 1 Driver-specific error code. |
45
|
|
|
* 2 Driver-specific error message. |
46
|
|
|
* |
47
|
|
|
* |
48
|
|
|
* @return array consists of the following fields: |
49
|
|
|
* 0 the transcripted error message. |
50
|
|
|
* 1 the error code |
51
|
|
|
* |
52
|
|
|
*/ |
53
|
|
|
private static function transcript(array $errorInfo): array |
54
|
|
|
{ |
55
|
|
|
list($state, $code, $message) = $errorInfo; |
56
|
|
|
$functs = [ |
57
|
|
|
// violation: column cannot be null |
58
|
|
|
1048 => function ($message) { |
59
|
|
|
preg_match("#Column '(.+)' cannot be null#", $message, $m); |
60
|
|
|
return ['FIELD_REQUIRED', $m[1]]; |
61
|
|
|
}, |
62
|
|
|
|
63
|
|
|
1054 => function ($message) { |
64
|
|
|
return ['COLUMN_DOES_NOT_EXIST', $message]; |
65
|
|
|
}, |
66
|
|
|
|
67
|
|
|
// violation: duplicate key |
68
|
|
|
1062 => function ($message) { |
69
|
|
|
if (preg_match("#'([^']+)' for key '([^']+)'#", $message, $m)) { |
70
|
|
|
$entry = $m[1]; |
71
|
|
|
$key = $m[2]; |
72
|
|
|
return ["DUPLICATE_KEY:$key:$entry"]; |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
if (preg_match("#for key '[a-z]+\.(.+)'$#", $message, $m) !== 1) { |
76
|
|
|
preg_match("#for key '(.+)'$#", $message, $m); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
return ["DUPLICATE_KEY:".$m[1]]; |
80
|
|
|
}, |
81
|
|
|
|
82
|
|
|
1064 => function ($message) { |
83
|
|
|
preg_match("#right syntax to use near '(.+)'#", $message, $m); |
84
|
|
|
return ['SYNTAX_ERROR', $m[1]]; |
85
|
|
|
}, |
86
|
|
|
|
87
|
|
|
1146 => function ($message) { |
88
|
|
|
return ['TABLE_DOES_NOT_EXIST', $message]; |
89
|
|
|
}, |
90
|
|
|
|
91
|
|
|
1264 => function ($message) { |
92
|
|
|
preg_match("#for column '(.+)'#", $message, $m); |
93
|
|
|
return ['VALUE_OUT_OF_RANGE', $m[1]]; |
94
|
|
|
}, |
95
|
|
|
|
96
|
|
|
1364 => function ($message) { |
97
|
|
|
return ['FIELD_REQUIRED', $message]; |
98
|
|
|
}, |
99
|
|
|
|
100
|
|
|
1451 => function ($message) { |
101
|
|
|
preg_match("#CONSTRAINT `(.+)` FOREIGN#", $message, $m); |
102
|
|
|
return ['RELATIONAL_INTEGRITY', $m[1]]; |
103
|
|
|
}, |
104
|
|
|
|
105
|
|
|
]; |
106
|
|
|
if (isset($functs[$code])) |
107
|
|
|
return [call_user_func($functs[$code], $message), $code]; |
108
|
|
|
|
109
|
|
|
return ['FUBAR #' . $state . '-' . $code . '-' . $message, $code]; |
110
|
|
|
} |
111
|
|
|
} |
112
|
|
|
|
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.