1: <?php /* vim: set expandtab : */
2: /**
3: * MySQL exception class
4: *
5: * A list of error codes can be found below. Anything in ($#), where # is a number which
6: * represents the index the substring is captured in in the array $this->getParams()
7: * returns.
8: *
9: * CODE_DUPLICATE_ENTRY (1062):
10: * Duplicate entry '($0)' for key '($1)'
11: * CODE_PARENT_FOREIGN_KEY_CONSTRAINT_FAILS (1451):
12: * Cannot delete or update a parent row: a foreign key constraint fails ($0)
13: * CODE_CHILD_FOREIGN_KEY_CONSTRAINT_FAILS (1452):
14: * Cannot add or update a child row: a foreign key constraint fails (%0)
15: * CODE_SERVER_GONE_AWAY (2006):
16: * Mysql server has gone away
17: *
18: * List of official MySQL error codes can be found on these links:
19: * @link http://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
20: * @link http://dev.mysql.com/doc/refman/5.7/en/error-messages-client.html
21: *
22: * Visit https://github.com/amcsi/amysql
23: * @author Szerémi Attila
24: * @license MIT License; http://www.opensource.org/licenses/mit-license.php
25: **/
26: class AMysql_Exception extends RuntimeException
27: {
28:
29: /**
30: * @var string The query string mysql had a problem with (if there is one).
31: **/
32: public $query;
33: protected $errorTriggered = 0;
34: protected $params;
35:
36: const CODE_DUPLICATE_ENTRY = 1062;
37: const CODE_PARENT_FOREIGN_KEY_CONSTRAINT_FAILS = 1451;
38: const CODE_CHILD_FOREIGN_KEY_CONSTRAINT_FAILS = 1452;
39: const CODE_SERVER_GONE_AWAY = 2006;
40:
41: /**
42: * __construct
43: *
44: * @param string $msg The mysql error message or custom
45: * message
46: * @param int $errno The mysql error code or custom
47: * error code
48: * @param string $query The mysql query string used or a
49: * message hinting the action taken
50: * @param Exception $previous The previous exception
51: * @access public
52: */
53: public function __construct($msg, $errno, $query, $previous = null)
54: {
55: $this->query = $query;
56: if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
57: parent::__construct($msg, $errno, $previous);
58: } else {
59: parent::__construct($msg, $errno);
60: }
61: }
62:
63: public function getDetails()
64: {
65: return $this->__toString();
66: }
67:
68: public function getLogMessage()
69: {
70: return $this->__toString();
71: }
72:
73: /**
74: * Parses the mysql error message for variables based on the error code.
75: * Only a subset of error codes/messages are supported.
76: * It is recommended that you always check the exception's error code before
77: * calling this method.
78: *
79: * For requests for including support for specific error codes/messages
80: * in the codebase, please visit @link https://github.com/amcsi/amysql/issues
81: *
82: * @param int $index (Optional) Returns the $index element of the returning
83: * array instead
84: * @access public
85: * @return array|string An array of the parsed variables.
86: * Empty array if the error code's message doesn't have
87: * variables, or if the error code/message is not supported.
88: * If $index was set, a string will be returned if found,
89: * otherwise NULL is returned and an E_NOTICE is raised.
90: */
91: public function getParams($index = null)
92: {
93: if (!isset($this->params)) {
94: switch ($this->getCode()) {
95: case self::CODE_DUPLICATE_ENTRY:
96: $pattern = "@Duplicate entry '(.*)' for key '(.*)'@";
97: $message = $this->getMessage();
98: preg_match($pattern, $message, $params);
99: array_shift($params);
100: break;
101: case self::CODE_PARENT_FOREIGN_KEY_CONSTRAINT_FAILS:
102: /**
103: * @todo Grab more specific parameters here.
104: * Would anyone help me find a guaranteed-to-work regex solution?
105: * I couldn't find any reliable documentation on the exact format
106: * of this among the MySQL docs.
107: **/
108: $pattern = "@Cannot delete or update a parent row: a foreign key constraint fails \((.*)\)$@";
109: $message = $this->getMessage();
110: preg_match($pattern, $message, $params);
111: array_shift($params);
112: break;
113: case self::CODE_CHILD_FOREIGN_KEY_CONSTRAINT_FAILS:
114: $pattern = "@Cannot add or update a child row: a foreign key constraint fails \((.*)\)$@";
115: /*
116: $pattern = '@Cannot add or update a child row: ' .
117: 'a foreign key constraint fails \((.*), CONSTRAINT ' .
118: '[`"]?(.*?)[`"]? FOREIGN KEY \([`"](.*)[`"]\) REFERENCES '
119: .'[`"]?(.*?)[`"]? \([`"]?(.*?)[`"]?\)(?: (.*))?\)$@';
120: */
121: $message = $this->getMessage();
122: preg_match($pattern, $message, $params);
123: array_shift($params);
124: break;
125: default:
126: $params = array();
127: break;
128: }
129: $this->params = $params;
130: }
131: $ret = $this->params;
132: if (isset($index)) {
133: $ret = $ret[$index];
134: }
135: return $ret;
136: }
137:
138: /**
139: * Performs a trigger_error on this exception.
140: * Does nothing if this method has already been called on this object.
141: *
142: * @access public
143: * @return void
144: */
145: public function triggerErrorOnce()
146: {
147: if (!$this->errorTriggered) {
148: trigger_error($this, E_USER_WARNING);
149: $this->errorTriggered++;
150: }
151: }
152:
153: /**
154: * The details of the exception described in a string.
155: *
156: * @todo Include the previous exception here.
157: *
158: * @access public
159: * @return void
160: */
161: public function __toString()
162: {
163: return "AMysqlException: `$this->message`\n" .
164: "Error code `$this->code` in $this->file:$this->line\n" .
165: "Query: $this->query\n";
166: }
167: }
168: ?>
169: