@@ -52,6 +52,7 @@ class ErrorHandler
5252 );
5353 protected $ registered = false ;
5454 protected $ oldErrorHandler = null ;
55+ protected $ throttleData = array ();
5556
5657 /**
5758 * Constructor
@@ -110,57 +111,53 @@ protected function emailErr($errType, $errMsg, $file, $line, $vars = array())
110111 $ data = &$ this ->data ;
111112 $ email = false ;
112113 if ($ cfg ['emailMin ' ] > 0 && $ cfg ['emailThrottleFile ' ]) {
113- $ ts_now = time ();
114- $ ts_cutoff = $ ts_now - $ cfg ['emailMin ' ] * 60 ;
115- $ data_str = is_readable ($ cfg ['emailThrottleFile ' ])
116- ? file_get_contents ($ cfg ['emailThrottleFile ' ])
117- : '' ;
118- $ throttleData = unserialize ($ data_str );
119- if (!is_array ($ throttleData )) {
120- $ throttleData = array (
121- 'ts_trash_collection ' => $ ts_now ,
122- 'errors ' => array (),
123- );
114+ $ tsNow = time ();
115+ $ tsCutoff = $ tsNow - $ cfg ['emailMin ' ] * 60 ;
116+ $ throttleData = &$ this ->throttleData ;
117+ if (!$ throttleData ) {
118+ $ data_str = is_readable ($ cfg ['emailThrottleFile ' ])
119+ ? file_get_contents ($ cfg ['emailThrottleFile ' ])
120+ : '' ;
121+ $ throttleData = unserialize ($ data_str );
122+ if (!is_array ($ throttleData )) {
123+ $ throttleData = array (
124+ 'tsTrashCollection ' => $ tsNow ,
125+ 'errors ' => array (),
126+ );
127+ }
124128 }
125- // for key creation:
126- // use true error location
127- // remove "numbers" from error message
128- $ errMsgTmp = $ errMsg ;
129- $ errMsgTmp = preg_replace ('/(\(.*?)\d+(.*?\))/ ' , '\1x\2 ' , $ errMsgTmp );
130- $ errMsgTmp = preg_replace ('/\b([a-z]+\d+)+\b/ ' , 'xxx ' , $ errMsgTmp );
131- $ errMsgTmp = preg_replace ('/\b[\d.-]{4,}\b/ ' , 'xxx ' , $ errMsgTmp );
132- $ key = md5 ($ file .$ line .$ errType .$ errMsgTmp );
129+ $ hash = $ this ->getErrorHash ($ errType , $ errMsg , $ file , $ line );
133130 if ($ data ['errorCaller ' ]) {
134131 $ file = $ data ['errorCaller ' ]['file ' ];
135132 $ line = $ data ['errorCaller ' ]['file ' ];
136133 }
137- if (!isset ($ throttleData ['errors ' ][$ key ]) || $ throttleData ['errors ' ][$ key ]['tsEmailed ' ] < $ ts_cutoff ) {
134+ if (!isset ($ throttleData ['errors ' ][$ hash ]) || $ throttleData ['errors ' ][$ hash ]['tsEmailed ' ] < $ tsCutoff ) {
138135 // this error has not occurred recently -> email it
139136 $ email = true ;
140137 if ($ this ->debug ->get ('collect ' ) && in_array ($ this ->debug ->get ('emailLog ' ), array ('always ' ,'onError ' ))) {
141138 // Don't email error. debug's shutdownFunction will email
142139 $ email = false ;
143140 }
144- $ throttleData ['errors ' ][$ key ] = array (
141+ $ throttleData ['errors ' ][$ hash ] = array (
145142 'file ' => $ file ,
146143 'line ' => $ line ,
147144 'errType ' => $ errType ,
148145 'errMsg ' => $ errMsg ,
149- 'tsEmailed ' => $ ts_now ,
146+ 'tsEmailed ' => $ tsNow ,
150147 'emailTo ' => $ this ->debug ->get ('emailTo ' ),
151148 'countSince ' => 0 ,
152149 );
153150 } else {
154151 // Don't email error. Was recently emailed.
155- $ throttleData ['errors ' ][$ key ]['countSince ' ]++;
152+ $ throttleData ['errors ' ][$ hash ]['countSince ' ]++;
156153 }
157154 $ throttleData = $ this ->emailTrashCollection ($ throttleData );
158155 $ wrote = $ this ->fileWrite ($ cfg ['emailThrottleFile ' ], serialize ($ throttleData ));
159156 }
160157 if ($ email ) {
161158 // send error email!
162159 $ errMsg = preg_replace ('/ \[<a.*?\/a>\]/i ' , '' , $ errMsg ); // remove links from errMsg
163- $ cs = $ throttleData ['errors ' ][$ key ]['countSince ' ];
160+ $ cs = $ throttleData ['errors ' ][$ hash ]['countSince ' ];
164161 $ subject = 'Website Error: ' .$ _SERVER ['SERVER_NAME ' ].': ' .$ errMsg .( $ cs ? ' ( ' .$ cs .'x) ' : '' );
165162 $ emailBody = '' ;
166163 if (!empty ($ cs )) {
@@ -216,19 +213,19 @@ protected function emailErr($errType, $errMsg, $file, $line, $vars = array())
216213 */
217214 protected function emailTrashCollection ($ data )
218215 {
219- $ ts_now = time ();
220- $ ts_cutoff = $ ts_now - $ this ->cfg ['emailMin ' ] * 60 ;
221- if ($ data ['ts_trash_collection ' ] < $ ts_cutoff ) {
216+ $ tsNow = time ();
217+ $ tsCutoff = $ tsNow - $ this ->cfg ['emailMin ' ] * 60 ;
218+ if ($ data ['tsTrashCollection ' ] < $ tsCutoff ) {
222219 // trash collection time
223- $ data ['ts_trash_collection ' ] = $ ts_now ;
220+ $ data ['tsTrashCollection ' ] = $ tsNow ;
224221 $ emailBody = '' ;
225222 foreach ($ data ['errors ' ] as $ k => $ err ) {
226- if ($ err ['tsEmailed ' ] > $ ts_cutoff ) {
223+ if ($ err ['tsEmailed ' ] > $ tsCutoff ) {
227224 continue ;
228225 }
229226 // it's been a while since this error was emailed
230227 if ($ err ['emailTo ' ] != $ this ->cfg ['emailTo ' ]) {
231- if ($ err ['countSince ' ] < 1 || $ err ['tsEmailed ' ] < $ ts_now - 60 *60 *24 ) {
228+ if ($ err ['countSince ' ] < 1 || $ err ['tsEmailed ' ] < $ tsNow - 60 *60 *24 ) {
232229 unset($ data ['errors ' ][$ k ]);
233230 }
234231 continue ;
@@ -295,6 +292,25 @@ public function get($k)
295292 return $ ret ;
296293 }
297294
295+ /**
296+ * generate hash
297+ *
298+ * @param integer $errType the level of the error
299+ * @param string $errMsg the error message
300+ * @param string $file filepath the error was raised in
301+ * @param string $line the line the error was raised in
302+ *
303+ * @return string hash
304+ */
305+ protected function getErrorHash ($ errType , $ errMsg , $ file , $ line )
306+ {
307+ $ errMsg = preg_replace ('/(\(.*?)\d+(.*?\))/ ' , '\1x\2 ' , $ errMsg );
308+ $ errMsg = preg_replace ('/\b([a-z]+\d+)+\b/ ' , 'xxx ' , $ errMsg );
309+ $ errMsg = preg_replace ('/\b[\d.-]{4,}\b/ ' , 'xxx ' , $ errMsg );
310+ $ hash = md5 ($ file .$ line .$ errType .$ errMsg );
311+ return $ hash ;
312+ }
313+
298314 /**
299315 * Error handler
300316 *
@@ -313,30 +329,31 @@ public function handler($errType, $errMsg, $file, $line, $vars = array())
313329 $ data = &$ this ->data ;
314330 $ isFatal = $ errType & $ cfg ['fatalMask ' ];
315331 $ isSuppressed = !$ isFatal && error_reporting () === 0 ;
316- $ errMd5 = md5 ( $ file. $ line. $ errMsg ); // use true source for tracking
317- $ first_occur = !isset ($ data ['errors ' ][$ errMd5 ]);
332+ $ hash = $ this -> getErrorHash ( $ errType , $ errMsg , $ file, $ line);
333+ $ firstOccur = !isset ($ data ['errors ' ][$ hash ]);
318334 if (!empty ($ data ['errorCaller ' ])) {
319335 $ file = $ data ['errorCaller ' ]['file ' ];
320336 $ line = $ data ['errorCaller ' ]['line ' ];
321337 }
322- $ err_string = $ this ->errTypes [$ errType ].': ' .$ file .' : ' .$ errMsg .' on line ' .$ line ;
338+ $ errStr = $ this ->errTypes [$ errType ].': ' .$ file .' (line ' .$ line .'): ' .$ errMsg ;
339+ $ errStrLog = $ this ->errTypes [$ errType ].': ' .$ file .' : ' .$ errMsg .' on line ' .$ line ;
323340 $ error = array (
324341 'type ' => $ errType ,
325342 'typeStr ' => $ this ->errTypes [$ errType ],
326343 // if any instance of this error was not supprseed, reflect that
327- 'suppressed ' => !$ first_occur && !$ data ['errors ' ][$ errMd5 ]['suppressed ' ]
344+ 'suppressed ' => !$ firstOccur && !$ data ['errors ' ][$ hash ]['suppressed ' ]
328345 ? false
329346 : $ isSuppressed ,
330347 // likewise if any any instance was logged in console
331- 'inConsole ' => !$ first_occur && $ data ['errors ' ][$ errMd5 ]['inConsole ' ]
348+ 'inConsole ' => !$ firstOccur && $ data ['errors ' ][$ hash ]['inConsole ' ]
332349 ? true
333350 : !$ isSuppressed && $ this ->debug ->get ('collect ' ),
334351 'message ' => $ errMsg ,
335352 'file ' => $ file ,
336353 'line ' => $ line ,
337354 );
338355 $ data ['lastError ' ] = $ error ;
339- $ data ['errors ' ][$ errMd5 ] = $ error ;
356+ $ data ['errors ' ][$ hash ] = $ error ;
340357 if ($ isSuppressed ) {
341358 // @suppressed error
342359 } elseif ($ this ->debug ->get ('collect ' )) {
@@ -347,20 +364,20 @@ public function handler($errType, $errMsg, $file, $line, $vars = array())
347364 */
348365 $ errors = array (E_ERROR ,E_WARNING ,E_PARSE ,E_CORE_ERROR ,E_COMPILE_ERROR ,E_USER_ERROR ,E_RECOVERABLE_ERROR );
349366 if (in_array ($ errType , $ errors )) {
350- $ this ->debug ->error ($ err_string );
367+ $ this ->debug ->error ($ errStr );
351368 } else {
352- $ this ->debug ->warn ($ err_string );
369+ $ this ->debug ->warn ($ errStr );
353370 }
354371 if (!$ this ->debug ->get ('output ' )) {
355372 // not currently outputing... send to error log
356- error_log ('PHP ' .$ err_string );
373+ error_log ('PHP ' .$ errStrLog );
357374 }
358- } elseif ($ first_occur ) {
375+ } elseif ($ firstOccur ) {
359376 if ($ this ->debug ->get ('emailTo ' ) && ( $ errType & $ cfg ['emailMask ' ] )) {
360377 $ args = func_get_args ();
361378 call_user_func_array (array ($ this ,'emailErr ' ), $ args );
362379 }
363- error_log ('PHP ' .$ err_string );
380+ error_log ('PHP ' .$ errStrLog );
364381 }
365382 if (!$ isSuppressed ) {
366383 $ data ['errorCaller ' ] = array ();
0 commit comments