array('priority' => Zend_Log::ERR, 'type' => 'Fatal error'), // 1 E_WARNING => array('priority' => Zend_Log::WARN, 'type' => 'Warning'), // 2 E_PARSE => array('priority' => Zend_Log::ERR, 'type' => 'Parse Error'), // 4 E_NOTICE => array('priority' => Zend_Log::NOTICE, 'type' => 'Notice'), // 8 E_CORE_ERROR => array('priority' => Zend_Log::ERR, 'type' => 'Core Error'), // 16 E_CORE_WARNING => array('priority' => Zend_Log::WARN, 'type' => 'Core Warning'), // 32 E_COMPILE_ERROR => array('priority' => Zend_Log::ERR, 'type' => 'Compile Error'), // 64 E_COMPILE_WARNING => array('priority' => Zend_Log::WARN, 'type' => 'Compile Warning'), // 128 E_USER_ERROR => array('priority' => Zend_Log::ERR, 'type' => 'User Error'), // 256 E_USER_WARNING => array('priority' => Zend_Log::WARN, 'type' => 'User Warning'), // 512 E_USER_NOTICE => array('priority' => Zend_Log::NOTICE, 'type' => 'User Notice'), // 1024 E_STRICT => array('priority' => Zend_Log::NOTICE, 'type' => 'Strict'), // 2048 E_RECOVERABLE_ERROR => array('priority' => Zend_Log::ERR, 'type' => 'Recoverable Error'), // 4096 E_DEPRECATED => array('priority' => Zend_Log::NOTICE, 'type' => 'Deprecated'), // 8192 E_USER_DEPRECATED => array('priority' => Zend_Log::NOTICE, 'type' => 'User Deprecated'), // 16384 ); protected function _init() { $columnMapping = array( 'priority' => 'priorityName', 'message' => 'message', 'backtrace' => 'backtrace', 'ip' => 'ip', 'idSession' => 'idSession', 'serverName' => 'serverName', 'url' => 'url', 'added' => 'timestamp' ); $writer = new Zend_Log_Writer_Db(Qs_Db::getInstance(), Qs_Db::getTableName('Log'), $columnMapping); $this->_log = new Zend_Log($writer); set_exception_handler(array($this, 'exceptionHandler')); if ($this->_writeLog) { $select = $this->_db->select(); $select->from($this->_getPair(), array('maxId' => 'MAX(id)', 'count' => 'COUNT(*)')); $data = $this->_db->fetchRow($select); if (!empty($data)) { $this->_startId = $data['maxId']; $this->_count = $data['count']; } } $this->_idSession = Zend_Session::getId(); $this->_clearOldHistory(); return $this; } public function getOption($name) { if (isset($this->_options[$name])) { return $this->_options[$name]; } return null; } public function setLogBacktrace($flag) { $this->_logBacktrace = (bool) $flag; } public function setWriteLog($flag) { $this->_writeLog = (bool) $flag; } /** * @return Qs_Debug * @throws Qs_Exception */ public static function getInstance() { if (null === Qs_Debug::$_instance) { if (!Zend_Registry::isRegistered('config')) { throw new Qs_Exception('Config is not registered in Zend_Registry'); } Qs_Debug::$_instance = new Qs_Debug(Zend_Registry::get('config')->debug); } return Qs_Debug::$_instance; } public function setErrorReporting($errorReporting) { $this->_previousErrorReporting = $this->_errorReporting; $this->_errorReporting = $errorReporting; error_reporting($errorReporting); set_error_handler(array($this, 'errorHandler'), $this->_errorReporting); } public function getErrorReporting() { return $this->_errorReporting; } public function restoreErrorReporting() { $this->_errorReporting = $this->_previousErrorReporting; set_error_handler(array($this, 'errorHandler'), $this->_errorReporting); } protected function _clearOldHistory() { if ($this->_writeLog) { if ($this->_count > $this->getOption('clearOverflow')) { $select = $this->_db->select(); $select->from($this->_tableName, 'id') ->order('id DESC') ->limit(1, $this->getOption('clearLimit') - 1); if (false !== ($maxId = $this->_db->fetchOne($select))) { $this->_db->delete($this->_tableName, 'id < ' . $this->_db->quote($maxId, Qs_Db::INT_TYPE)); } } } } public function errorHandler($errNo, $errMsg, $filename, $linenNum, $errContext) { $debugMessage = '[' . $errNo . '] ' . $errMsg . '
'; if (null !== $filename && null !== $linenNum) { $debugMessage .= 'In line ' . $linenNum . ' of ' . $filename . ".\n"; } if (isset(self::$_errorTypes[$errNo])) { $info = self::$_errorTypes[$errNo]; $priority = $info['priority']; $debugMessage = "{$info['type']}: {$debugMessage}"; } else { $priority = Zend_Log::DEBUG; $debugMessage = "Unknown Error ({$errNo}): {$debugMessage}"; } // Ignore warnings and notices from lib/Zend if (in_array($errNo, array(E_WARNING, E_NOTICE)) && false !== strpos($filename, '/Zend/')) { return true; } $this->_writeLog($debugMessage, $priority); return true; } public static function log($message, $priority = E_WARNING) { Qs_Debug::getInstance()->errorHandler($priority, $message, null, null, null); } public static function logStdError($message) { if (false !== ($handle = fopen('php://stderr', 'w'))) { fwrite($handle, $message); fclose($handle); } } public function writeLog($message, $priority) { return $this->_writeLog($message, $priority); } protected function _writeLog($message, $priority) { if (!$this->_writeLog) { return false; } $this->_log->setEventItem('serverName', $_SERVER['SERVER_NAME']); $this->_log->setEventItem('ip', $_SERVER['REMOTE_ADDR']); $this->_log->setEventItem('idSession', $this->_idSession); $this->_log->setEventItem('url', (string)Qs_Request::getUrl()); if ($this->_logBacktrace) { $this->_log->setEventItem('backtrace', gzcompress(serialize(Qs_Debug::_getBackTrace()), 9)); } else { $this->_log->setEventItem('backtrace', null); } $this->_log->log($message, $priority); $this->_writed = true; return true; } /** * Remove objects instances from array * (раніше приймав занчення по посиланню, в РНР 5.4 вилізла проблема, debug_backtrace повертає масив з посиланнями * на об'єкти, тому зміни в $array моломи сайт) * @param array $array * @return array */ protected static function _removeObjects(array $array) { $result = array(); foreach ($array as $key => $value) { if (is_array($value)) { $result[$key] = Qs_Debug::_removeObjects($value); } else if (is_object($value)) { $result[$key] = get_class($value) . ' Object'; } else { $result[$key] = $value; } } return $result; } protected static function _getBackTrace() { $backtrace = debug_backtrace(); array_shift($backtrace); array_shift($backtrace); array_shift($backtrace); $backtrace = Qs_Debug::_removeObjects($backtrace); return $backtrace; } public function getDebugInformation() { if (!$this->_writeLog || !$this->_writed) { return array(); } $select = $this->_db->select(); $select->from($this->_getPair(), array('added', 'priority', 'message')); $select->where("`{$this->_tableAlias}`.`id` > " . $this->_db->quote($this->_startId, Qs_Db::INT_TYPE)); $select->where("`{$this->_tableAlias}`.`idSession` = " . $this->_db->quote($this->_idSession)); $select->order($this->_tableAlias . '.id'); $list = $this->_db->fetchAll($select); return $list; } public static function getExceptionMessage(Exception $exception) { $firstTracePoint = current($exception->getTrace()); $html = '
' . '' . $firstTracePoint['class'] . $firstTracePoint['type'] . $firstTracePoint['function'] . '' . ' uncaught exception ' . 'in ' . $exception->getFile() . ' on line ' . $exception->getLine() . '' . '
' . '' . nl2br(strip_tags($exception->getMessage())) . '' . '
'; if ($exception instanceof Zend_Db_Statement_Exception && false !== ($lastQueryProfile = Qs_Db::getInstance()->getProfiler()->getLastQueryProfile()) ) { if (Qs_Request::isXmlHttpRequest()) { $html .= "\nLast Query:\n" . Qs_SqlFormatter::format($lastQueryProfile->getQuery(), false); } else { $html .= '
Last query:
' . Qs_SqlFormatter::format($lastQueryProfile->getQuery()); } } return $html; } public static function getExceptionPlainMessage(Exception $exception) { $firstTracePoint = current($exception->getTrace()); $text = $firstTracePoint['class'] . $firstTracePoint['type'] . $firstTracePoint['function'] . ' ' . 'uncaught exception in ' . $exception->getFile() . ' on line ' . $exception->getLine() . ' - ' . strip_tags($exception->getMessage()); if ($exception instanceof Zend_Db_Statement_Exception && false !== ($lastQueryProfile = Qs_Db::getInstance()->getProfiler()->getLastQueryProfile()) ) { $text .= '. Last query: ' . $lastQueryProfile->getQuery(); } return $text; } /** * @param Exception $exception * @return string */ public static function getExceptionBacktrace($exception) { $debugBacktrace = $exception->getTrace(); $debugBacktrace = Qs_Debug::_removeObjects($debugBacktrace); return 'BACKTRACE
' . print_r($debugBacktrace, true) . '
'; } protected function _sendExceptionNotification($exception) { $message = Qs_Debug::getExceptionMessage($exception); $backtrace = Qs_Debug::getExceptionBacktrace($exception); $html = $message . '
' . 'URL: ' . Qs_Request::getUrl() . ''; foreach (array('_GET', '_POST', '_COOKIE', '_SERVER', '_ENV') as $name) { $html .= '
$' . $name . '
' . htmlspecialchars(print_r($GLOBALS[$name], true)) . '
'; } $html .= '
' . $backtrace; $notifyEmails = $this->getOption('notifyEmails'); if (is_array($notifyEmails) && !empty($notifyEmails)) { $headers = 'From: debug@' . $_SERVER['SERVER_NAME'] . "\n" . 'Reply-To: no-reply@' . $_SERVER['SERVER_NAME'] . "\n" . 'Content-Type: text/html' . "\n" . 'X-Mailer: PHP/' . phpversion(); foreach ($notifyEmails as $email) { mail($email, 'Error Dump from ' . $_SERVER['SERVER_NAME'], $html, $headers); } } return $this; } /** * @param Exception $exception */ public function exceptionHandler($exception) { self::logStdError('QSF Exception: ' . self::getExceptionPlainMessage($exception)); $message = Qs_Debug::getExceptionMessage($exception); $backtrace = Qs_Debug::getExceptionBacktrace($exception); $this->_sendExceptionNotification($exception); $this->_writeLog($exception->getMessage(), 0); if (Qs_Constant::get('DEBUG')) { die($message . '
'. $backtrace); } die('Internal error'); } public static function processException($exception) { Qs_Debug::getInstance()->exceptionHandler($exception); } }