1: <?php
2: /**
3: * This is the main exception to be thrown by any
4: * method to indicate that the CALLER is responsible for
5: * causing the exception. This works in conjunction with QCubed's
6: * error handling/reporting, so that the correct file/line-number is
7: * displayed to the user.
8: *
9: * So for example, for a class that contains the method GetItemAtIndex($intIndex),
10: * it is conceivable that the caller could call GetItemAtIndex(15), where 15 does not exist.
11: * GetItemAtIndex would then thrown an IndexOutOfRangeException (which extends CallerException).
12: * If the CallerException is not caught, then the Exception will be reported to the user. The CALLER
13: * (the script who CALLED GetItemAtIndex) would have that line highlighted as being responsible
14: * for calling the error.
15: *
16: * The PHP default for exeception reporting would normally say that the "throw Exception" line in GetItemAtIndex
17: * is responsible for throwing the exception. While this is technically true, in reality, it was the line that
18: * CALLED GetItemAtIndex which is responsible. In short, this allows for much cleaner exception reporting.
19: *
20: * On a more in-depth note, in general, suppose a method OuterMethod takes in parameters, and ends up passing those
21: * paremeters into ANOTHER method InnerMethod which could throw a CallerException. OuterMethod is responsible
22: * for catching and rethrowing the caller exception. And when this is done, IncrementOffset() MUST be called on
23: * the exception object, to indicate that OuterMethod's CALLER is responsible for the exception.
24: *
25: * So the code snippet to call InnerMethod by OuterMethod should look like:
26: * <code>
27: * function OuterMethod($mixValue) {
28: * try {
29: * InnerMethod($mixValue);
30: * } catch (CallerException $objExc) {
31: * $objExc->IncrementOffset();
32: * throw $objExc;
33: * }
34: * // Do Other Stuff
35: * }
36: * </code>
37: * Again, this will assure the user that the line of code responsible for the excpetion is properly being reported
38: * by the QCubed error reporting/handler.
39: * @property-read int $Offset The exception offset.
40: * @property-read string $BackTrace The exception backtrace.
41: * @property-read string $TraceArray The exception backtrace in a form of an array.
42: */
43: class QCallerException extends Exception {
44: /**
45: * @var int Exception offset
46: * The element in the stack trace array indicated by this index is marked
47: * as the point which caused the exception
48: */
49: private $intOffset;
50: /** @var array The stack trace array as caputred by debug_backtrace() */
51: private $strTraceArray;
52:
53: /**
54: * Set message for the exception
55: *
56: * @param string $strMessage
57: */
58: public function setMessage($strMessage) {
59: $this->message = $strMessage;
60: }
61:
62: /**
63: * The constructor of CallerExceptions. Takes in a message string
64: * as well as an optional Offset parameter (defaults to 1).
65: * The Offset specifiies how many calls up the call stack is responsible
66: * for the exception. By definition, when a CallerException is called,
67: * at the very least the Caller of the most immediate function, which is
68: * 1 up the call stack, is responsible. So therefore, by default, intOffset
69: * is set to 1.
70: *
71: * It is rare for intOffset to be set to an integer other than 1.
72: *
73: * Normally, the Offset would be altered by calls to IncrementOffset
74: * at every step the CallerException is caught/rethrown up the call stack.
75: *
76: * @param string $strMessage the Message of the exception
77: * @param integer $intOffset the optional Offset value (currently defaulted to 1)
78: */
79: public function __construct($strMessage, $intOffset = 1) {
80: parent::__construct($strMessage);
81: $this->intOffset = $intOffset;
82: $this->strTraceArray = debug_backtrace();
83:
84: if (isset($this->strTraceArray[$this->intOffset]['file'])) {
85: $this->file = $this->strTraceArray[$this->intOffset]['file'];
86: $this->line = $this->strTraceArray[$this->intOffset]['line'];
87: }
88: }
89:
90: public function IncrementOffset() {
91: $this->intOffset++;
92: if (array_key_exists('file', $this->strTraceArray[$this->intOffset]))
93: $this->file = $this->strTraceArray[$this->intOffset]['file'];
94: else
95: $this->file = '';
96: if (array_key_exists('line', $this->strTraceArray[$this->intOffset]))
97: $this->line = $this->strTraceArray[$this->intOffset]['line'];
98: else
99: $this->line = '';
100: }
101:
102: public function DecrementOffset() {
103: $this->intOffset--;
104: if (array_key_exists('file', $this->strTraceArray[$this->intOffset]))
105: $this->file = $this->strTraceArray[$this->intOffset]['file'];
106: else
107: $this->file = '';
108: if (array_key_exists('line', $this->strTraceArray[$this->intOffset]))
109: $this->line = $this->strTraceArray[$this->intOffset]['line'];
110: else
111: $this->line = '';
112: }
113:
114: /**
115: * PHP magic method
116: * @param $strName
117: *
118: * @return array|int|mixed
119: */
120: public function __get($strName) {
121: if ($strName == "Offset")
122: return $this->intOffset;
123: else if ($strName == "BackTrace") {
124: $objTraceArray = debug_backtrace();
125: return (var_export($objTraceArray, true));
126: } else if ($strName == "TraceArray") {
127: return $this->strTraceArray;
128: }
129: }
130: }
131:
132: /**
133: * Used when trying to access a table object which does not have a primary key defined on it
134: */
135: class QUndefinedPrimaryKeyException extends QCallerException {
136: /**
137: * Constructor method
138: * @param string $strMessage
139: */
140: public function __construct($strMessage) {
141: parent::__construct($strMessage, 2);
142: }
143: }
144:
145: /**
146: * Thrown when trying to access an element in an array whose index does not exist
147: * NOTE: this exception will not fire automatically for you unless you use it with the try-catch block
148: */
149: class QIndexOutOfRangeException extends QCallerException {
150: /**
151: * Constructor method
152: * @param string $intIndex
153: * @param int $strMessage
154: */
155: public function __construct($intIndex, $strMessage) {
156: if ($strMessage)
157: $strMessage = ": " . $strMessage;
158: parent::__construct(sprintf(QApplication::Translate("Index (%s) is out of range%s"), $intIndex, $strMessage), 2);
159: }
160: }
161:
162: /**
163: * Thrown when a particular property of class is not defined and we try to access it
164: */
165: class QUndefinedPropertyException extends QCallerException {
166: /**
167: * Constructor method
168: * @param string $strType
169: * @param int $strClass
170: * @param string $strProperty
171: */
172: public function __construct($strType, $strClass, $strProperty) {
173: parent::__construct(sprintf(QApplication::Translate("Undefined %s property or variable in '%s' class: %s"), $strType, $strClass, $strProperty), 2);
174: }
175: }
176:
177: /**
178: * Thrown when we try to call an undefined method. Helpful for codegen.
179: */
180: class QUndefinedMethodException extends QCallerException {
181: public function __construct($strClass, $strMethod) {
182: parent::__construct(sprintf(QApplication::Translate("Undefined method in '%s' class: %s"), $strClass, $strMethod), 2);
183: }
184: }
185:
186: /**
187: * Thrown when optimistic locking (in ORM Save() method) detects that DB data was updated
188: */
189: class QOptimisticLockingException extends QCallerException {
190: /**
191: * Constructor method
192: * @param string $strClass
193: */
194: public function __construct($strClass) {
195: parent::__construct(sprintf(QApplication::Translate('Optimistic Locking constraint when trying to update %s object. To update anyway, call ->Save() with $blnForceUpdate set to true'), $strClass, 2));
196: }
197: }
198:
199: /**
200: * Thrown when the desired page is protected by ALLOW REMOTE ADMIN feature and the request does not qualify
201: */
202: class QRemoteAdminDeniedException extends QCallerException {
203: /**
204: * Constructor method
205: */
206: public function __construct() {
207: parent::__construct(
208: sprintf(
209: QApplication::Translate('Remote access to "%s" has been disabled.' . "\n" .
210: 'To allow remote access to this script, set the ALLOW_REMOTE_ADMIN constant to TRUE' . "\n" .
211: 'or to "%s" in "configuration.inc.php".')
212: , QApplication::$RequestUri, $_SERVER['REMOTE_ADDR'])
213: , 2);
214: }
215: }
216:
217: /**
218: * Thrown when formstate is not found
219: */
220: class QInvalidFormStateException extends QCallerException {
221: /**
222: * Constructor method
223: * @param string $strFormId Form ID for which the state was not found
224: */
225: public function __construct($strFormId) {
226: parent::__construct(sprintf(QApplication::Translate('Invalid Form State Data for "%s" object (session may have been lost)'), $strFormId), 2);
227: }
228: }
229:
230: /**
231: * @property-read integer $Offset
232: * @property-read mixed $BackTrace
233: * @property-read string $Query
234: */
235: class QDataBindException extends Exception {
236: private $intOffset;
237: private $strTraceArray;
238: private $strQuery;
239:
240: public function __construct(QCallerException $objExc) {
241: parent::__construct($objExc->getMessage(), $objExc->getCode());
242: $this->intOffset = $objExc->Offset;
243: $this->strTraceArray = $objExc->TraceArray;
244:
245: if ($objExc instanceof QDatabaseExceptionBase)
246: $this->strQuery = $objExc->Query;
247:
248: $this->file = $this->strTraceArray[$this->intOffset]['file'];
249: $this->line = $this->strTraceArray[$this->intOffset]['line'];
250: }
251:
252: public function __get($strName) {
253: switch($strName) {
254: case "Offset":
255: return $this->intOffset;
256:
257: case "BackTrace":
258: $objTraceArray = debug_backtrace();
259: return (var_export($objTraceArray, true));
260:
261: case "Query":
262: return $this->strQuery;
263: }
264: }
265: }