_url = (string) Qs_Request::getUrl(); $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', true), $columnMapping); $this->_log = new Zend_Log($writer); $this->_log->setTimestampFormat('Y-m-d H:i:s'); set_exception_handler(array($this, 'exceptionHandler')); if ($this->_writeLog) { $select = $this->db->select(); $select->from($this->pair, 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; } 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, $linenum, $vars) { $debugMessage = '[' . $errno . '] ' . $errmsg . '
'; if (null !== $filename && null !== $linenum) { $debugMessage .= 'In line ' . $linenum . ' of ' . $filename . ".\n"; } switch ($errno) { case E_ERROR: $priority = 3; $debugMessage = "Fatal error: $debugMessage"; break; case E_WARNING: // Ignore warnings from lib/Zend if (false !== strpos($filename, BASE_PATH.'/lib/Zend')) { return true; } $priority = 4; $debugMessage = "Warning: $debugMessage"; break; case E_NOTICE: // Ignore notices from lib/Zend if (false !== strpos($filename, BASE_PATH.'/lib/Zend')) { return true; } $priority = 5; $debugMessage = "Notice: $debugMessage"; break; case E_USER_ERROR: $priority = 3; $debugMessage = "User error: $debugMessage"; break; case E_USER_WARNING: $priority = 4; $debugMessage = "User warning: $debugMessage"; break; case E_USER_NOTICE: $priority = 5; $debugMessage = "User notice: $debugMessage"; break; default: $priority = 6; $debugMessage = "Unknown Error ({$errno}): $debugMessage"; } $this->_writeLog($debugMessage, $priority); } public static function log($message, $priority = E_WARNING) { $backtrace = debug_backtrace(); Qs_Debug::getInstance()->errorHandler($priority, $message, null, null, null); } public function writeLog($message, $priority) { return $this->_writeLog($message, $priority); } protected function _writeLog($message, $priority) { if (!$this->_writeLog) { return false; } $this->_log->setEventItem('serverName', $this->_truncate(isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost', 255)); $this->_log->setEventItem('ip', $this->_truncate(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR']: '127.0.0.1', 15)); $this->_log->setEventItem('idSession', $this->_truncate($this->_idSession, 32)); $this->_log->setEventItem('url', $this->_truncate($this->_url, 255)); if ($this->_logBacktrace) { $backtrace = serialize(Qs_Debug::_getBackTrace()); $backtrace = gzcompress($backtrace, 9); $backtrace = '0x' . bin2hex($backtrace); $this->_log->setEventItem( 'backtrace', new Zend_Db_Expr( 'CAST(' . $backtrace . ' AS varbinary(8000))' ) ); } else { $this->_log->setEventItem('backtrace', null); } $this->_log->log($message, $priority); $this->_writed = true; return true; } protected function _truncate($text, $length) { $text = (string) $text; $length = abs($length); if (strlen($text) > $length) { $text = substr($text, 0, $length); } return $text; } public static function removeObjects(&$array) { return Qs_Debug::_removeObjects($array); } protected static function _removeObjects(&$array) { foreach ($array as $key => &$value) { if (is_array($value)) { Qs_Debug::_removeObjects($value); } elseif (is_object($value)) { unset($array[$key]); $array[$key] = get_class($value) . ' Object'; } } } protected static function _getBackTrace() { $backtrace = debug_backtrace(); array_shift($backtrace); array_shift($backtrace); array_shift($backtrace); Qs_Debug::_removeObjects($backtrace); return $backtrace; } public function getDebugInformation() { if (!$this->_writeLog || !$this->_writed) { return array(); } $select = $this->db->select(); $select->from($this->pair, 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()); $message = $exception->getMessage(); if ($exception instanceof Zend_Db_Exception && (false !== ($pos = strpos($message, 'query was:')))) { $query = substr($message, $pos + 11); $message = substr($message, 0, $pos + 11); $message = nl2br($message); $query = strip_tags($query); $message = $message . Qs_Debug::formatSql($query); } else { $message = nl2br(strip_tags($message)); } $html = '
' . '' . $firstTracePoint['class'] . $firstTracePoint['type'] . $firstTracePoint['function'] . '' . ' uncaught exception ' . 'in ' . $exception->getFile() . ' on line ' . $exception->getLine() . '' . '
' . '' . $message . '' . '
'; if ($exception instanceof Zend_Db_Exception && Qs_Db::getInstance()->getProfiler()) { $html .= self::renderBox('DB Queries', self::renderQueries()); } return $html; } public static function renderBox($title, $body, $expanded = false) { $id = preg_replace('/[^A-Za-z]/', '', $title); $html = ''; $html .= '
' . htmlspecialchars($title) . '
'; $html .= '
' . $body . '
'; return $html; } public static function getExceptionBacktraceArray($exception) { $debugBacktrace = $exception->getTrace(); Qs_Debug::_removeObjects($debugBacktrace); return $debugBacktrace; } public static function renderBacktrace($backtrace) { $html = ''; foreach ($backtrace as $index => $item) { $id = 'debug-bt-' . $index; $color = false !== strpos($item['file'], 'site/App') ? '#F5E0B4' : '#d4eaef'; $html .= '
' . "{$item['file']}:{$item['line']} | {$item['class']}{$item['type']}{$item['function']}" . '
'; $html .= ''; } return $html; } public static function getExceptionBacktrace($exception) { $debugBacktrace = self::getExceptionBacktraceArray($exception); return '
' . 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; } public function exceptionHandler($exception) { $message = Qs_Debug::getExceptionMessage($exception); $backtrace = Qs_Debug::getExceptionBacktraceArray($exception); $this->_writeLog($exception->getMessage(), 0); if (Qs_Constant::get('DEBUG')) { $html = ''; $html .= $message; $html .= self::renderBox('BACKTRACE', self::renderBacktrace($backtrace), true); die($html); } $this->_sendExceptionNotification($exception); die('Internal error'); } public static function processException($exception) { Qs_Debug::getInstance()->exceptionHandler($exception); } public static function dumpSql($sql) { echo self::formatSql($sql); } public static function formatSql($sql) { return Qs_SqlFormatter::format('' . $sql, !Qs_Request::isXmlHttpRequest()); } public static function dumpQueries() { echo self::renderQueries(); } public static function renderQueries() { $db = Qs_Db::getInstance(); if (!($profiler = $db->getProfiler())) { return false; } /** @var Zend_Db_Profiler_Query $query */ $html = ''; foreach ($profiler->getQueryProfiles() as $index => $query) { $html .= ''; } $html .= '
' . ($index + 1) . ''; $html .= Qs_Debug::formatSql($query->getQuery()); $html .= '
'; return $html; } }