add error and exception handler
authorStricted <info@stricted.de>
Sun, 10 May 2015 13:15:46 +0000 (15:15 +0200)
committerStricted <info@stricted.de>
Sun, 10 May 2015 13:37:46 +0000 (15:37 +0200)
lib/system/DB.class.php
lib/system/DNS.class.php
lib/system/SystemException.class.php [new file with mode: 0644]

index 7979dc627f4df7638d9f007f3c7c4fc993ae372d..a4950301d0a53c3892623a982e08d977bc98b433 100644 (file)
@@ -13,12 +13,6 @@ class DB {
         */
        private $conn = null;
        
-       /**
-        * error string
-        * @var string
-        */
-       private $error = '';
-
        /**
         * Connects to SQL Server
         *
@@ -34,65 +28,58 @@ class DB {
        public function __construct($driver, $host, $username, $password, $database, $port = 0, $options = array()) {
                if (!extension_loaded("pdo")) {
                        // check if extension loaded
-                       die("Missing <a href=\"http://www.php.net/manual/en/book.pdo.php\">PDO</a> PHP extension.");
+                       throw new SystemException("Missing <a href=\"http://www.php.net/manual/en/book.pdo.php\">PDO</a> PHP extension.");
                }
                
                $driver = strtolower($driver);
-               try {
-                       if ($driver == "mysql") {
-                               if (!extension_loaded("pdo_mysql")) {
-                                       // check if extension loaded
-                                       die("Missing <a href=\"http://php.net/manual/de/ref.pdo-mysql.php\">pdo_mysql</a> PHP extension.");
-                               }
-                               
-                               if (empty($port)) {
-                                       $port=3306;
-                               }
-                               
-                               $this->conn = new \PDO("mysql:host=".$host.";port=".$port.";dbname=".$database, $username, $password, $options);
+               if ($driver == "mysql") {
+                       if (!extension_loaded("pdo_mysql")) {
+                               // check if extension loaded
+                               throw new SystemException("Missing <a href=\"http://php.net/manual/de/ref.pdo-mysql.php\">pdo_mysql</a> PHP extension.");
                        }
-                       else if ($driver == "pgsql") {
-                               if (!extension_loaded("pdo_pgsql")) {
-                                       // check if extension loaded
-                                       die("Missing <a href=\"http://php.net/manual/de/ref.pdo-pgsql.php\">pdo_pgsql</a> PHP extension.");
-                               }
-                               
-                               if (empty($port)) {
-                                       $port=5432;
-                               }
-                               
-                               $this->conn = new \PDO("pgsql:host=".$host.";port=".$port.";dbname=".$database, $username, $password, $options);
+                       
+                       if (empty($port)) {
+                               $port=3306;
                        }
-                       else if ($driver == "sqlite") {
-                               if (!extension_loaded("pdo_sqlite")) {
-                                       // check if extension loaded
-                                       die("Missing <a href=\"http://php.net/manual/de/ref.pdo-sqlite.php\">pdo_sqlite</a> PHP extension.");
-                               }
-                               
-                               if (!file_exists($database)) {
-                                       @touch($database);
-                               }
-                               
-                               if (file_exists($database) && is_readable($database) && is_writable($database)) {
-                                       $this->conn = new \PDO("sqlite:".$database, $username, $password, $options);
-                               }
-                               else {
-                                       $this->error = "cant crate/connect the/to sqlite database";
-                                       return false;
-                               }
+                       
+                       $this->conn = new \PDO("mysql:host=".$host.";port=".$port.";dbname=".$database, $username, $password, $options);
+               }
+               else if ($driver == "pgsql") {
+                       if (!extension_loaded("pdo_pgsql")) {
+                               // check if extension loaded
+                               throw new SystemException("Missing <a href=\"http://php.net/manual/de/ref.pdo-pgsql.php\">pdo_pgsql</a> PHP extension.");
                        }
-                       else{
-                               $this->error = "not supported database type found";
-                               return false;
+                       
+                       if (empty($port)) {
+                               $port=5432;
                        }
                        
-                       return true;
+                       $this->conn = new \PDO("pgsql:host=".$host.";port=".$port.";dbname=".$database, $username, $password, $options);
+               }
+               /*
+               else if ($driver == "sqlite") {
+                       if (!extension_loaded("pdo_sqlite")) {
+                               // check if extension loaded
+                               throw new SystemException("Missing <a href=\"http://php.net/manual/de/ref.pdo-sqlite.php\">pdo_sqlite</a> PHP extension.");
+                       }
+                       
+                       if (!file_exists($database)) {
+                               @touch($database);
+                       }
                        
+                       if (file_exists($database) && is_readable($database) && is_writable($database)) {
+                               $this->conn = new \PDO("sqlite:".$database, $username, $password, $options);
+                       }
+                       else {
+                               throw new SystemException("cant crate/connect the/to sqlite database");
+                       }
                }
-               catch (\PDOException $e) {
-                       $this->error = $e->getMessage();
-                       return false;
+               */
+               else{
+                       throw new SystemException("no supported database type found");
                }
+               
+               return true;
        }
        
        /*
@@ -110,22 +97,18 @@ class DB {
         * @return      integer 
         */
        public function query ($res, $bind = array()) {
-               try {
-                       $query = null;
-                       $query = $this->conn->prepare($res);
-                       
-                       if (is_array($bind) && !empty($bind)) {
-                               $query->execute($bind);
-                       }
-                       else {
-                               $query->execute();
-                       }
-                       
-                       return $query;
+               $query = null;
+               $query = $this->conn->prepare($res);
+               
+               if (is_array($bind) && !empty($bind)) {
+                       $query->execute($bind);
                }
-               catch (\PDOException $e) {
-                       $this->error = $e->getMessage();
+               else {
+                       $query->execute();
                }
+               
+               return $query;
+
        }
        
        /**
@@ -135,12 +118,7 @@ class DB {
         * @return      array
         */
        public function fetch_array ($res) {
-               try {
-                       return $res->fetch(\PDO::FETCH_ASSOC);
-               }
-               catch (\PDOException $e) {
-                       $this->error = $e->getMessage();
-               }
+               return $res->fetch(\PDO::FETCH_ASSOC);
        }
        
        /**
@@ -152,15 +130,6 @@ class DB {
                return $this->conn->lastInsertId();
        }
        
-       /**
-        * Returns SQL last error.
-        *
-        * @return      string
-        */
-       public function error () {
-               return $this->error;
-       }
-       
        /**
         * call PDO methods
         *
index ba1033570f975481977f4474d88dd873b5a92337..976b9fb7a4409b967cda0bebf03198d74779157d 100644 (file)
@@ -41,7 +41,10 @@ class DNS {
         * init main system
         */
        public function __construct() {
-               spl_autoload_register(array('self', 'autoload'));
+               spl_autoload_register(array('dns\system\DNS', 'autoload'));
+               set_exception_handler(array('dns\system\DNS', 'handleException'));
+               set_error_handler(array('dns\system\DNS', 'handleError'), E_ALL);
+               /*register_shutdown_function(array('dns\system\DNS', 'destruct'));*/
                
                $this->initDB();
                self::buildOptions();
@@ -51,6 +54,16 @@ class DNS {
                new RequestHandler();
        }
        
+       /*
+       public static function destruct() {
+               $error = error_get_last();
+               if (!empty($error)) {
+                       self::handleException(new SystemException($error['message'], $error['line']));
+               }
+
+       }
+       */
+       
        /**
         * get database
         */
@@ -96,6 +109,66 @@ class DNS {
                }
        }
        
+       /**
+        * Calls the show method on the given exception.
+        * 
+        * @param       \Exception      $e
+        */
+       public static final function handleException(\Exception $e) {
+               try {
+                       if ($e instanceof SystemException) {
+                               $e->show();
+                               exit;
+                       }
+                       
+                       // repack Exception
+                       self::handleException(new SystemException($e->getMessage(), $e->getCode(), '', $e));
+               }
+               catch (\Exception $exception) {
+                       die("<pre>DNS::handleException() Unhandled exception: ".$exception->getMessage()."\n\n".$exception->getTraceAsString());
+               }
+       }
+       
+       /**
+        * Catches php errors and throws instead a system exception.
+        * 
+        * @param       integer         $errorNo
+        * @param       string          $message
+        * @param       string          $filename
+        * @param       integer         $lineNo
+        */
+       public static final function handleError($errorNo, $message, $filename, $lineNo) {
+               if (error_reporting() != 0) {
+                       $type = 'error';
+                       switch ($errorNo) {
+                               case 2: $type = 'warning';
+                                       break;
+                               case 8: $type = 'notice';
+                                       break;
+                       }
+                       
+                       throw new SystemException('PHP '.$type.' in file '.$filename.' ('.$lineNo.'): '.$message, 0);
+               }
+       }
+       
+       /**
+        * Returns true if the debug mode is enabled, otherwise false.
+        * 
+        * @return      boolean
+        */
+       public static function debugModeIsEnabled() {
+               // try to load constants
+               if (!defined('ENABLE_DEBUG')) {
+                       self::buildOptions();
+               }
+               
+               if (defined('ENABLE_DEBUG') && ENABLE_DEBUG) {
+                       return true;
+               }
+               
+               return false;
+       }
+       
        /**
         * init language system
         */
diff --git a/lib/system/SystemException.class.php b/lib/system/SystemException.class.php
new file mode 100644 (file)
index 0000000..0a181c0
--- /dev/null
@@ -0,0 +1,255 @@
+<?php\r
+namespace dns\system;\r
+\r
+/**\r
+ * A SystemException is thrown when an unexpected error occurs.\r
+ * \r
+ * @author     Marcel Werk\r
+ * @copyright  2001-2015 WoltLab GmbH\r
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>\r
+ * @package    com.woltlab.wcf\r
+ * @subpackage system.exception\r
+ * @category   Community Framework\r
+ */\r
+// @codingStandardsIgnoreFile\r
+class SystemException extends \Exception {\r
+       /**\r
+        * error description\r
+        * @var string\r
+        */\r
+       protected $description = null;\r
+       \r
+       /**\r
+        * additional information\r
+        * @var string\r
+        */\r
+       protected $information = '';\r
+       \r
+       /**\r
+        * additional information\r
+        * @var string\r
+        */\r
+       protected $functions = '';\r
+    \r
+       /**\r
+        * exception id\r
+        * @var string\r
+        */\r
+       protected $exceptionID = '';\r
+       \r
+       /**\r
+        * Creates a new SystemException.\r
+        * \r
+        * @param       string          $message        error message\r
+        * @param       integer         $code           error code\r
+        * @param       string          $description    description of the error\r
+        * @param       \Exception      $previous       repacked Exception\r
+        */\r
+       public function __construct($message = '', $code = 0, $description = '', \Exception $previous = null) {\r
+               parent::__construct((string) $message, (int) $code, $previous);\r
+               $this->description = $description;\r
+       }\r
+       \r
+       /**\r
+        * Removes database password from stack trace.\r
+        * @see \Exception::getTraceAsString()\r
+        */\r
+       public function __getTraceAsString() {\r
+               $e = ($this->getPrevious() ?: $this);\r
+        $string = $e->getTraceAsString();\r
+               $string = preg_replace('/PDO->__construct\(.*\)/', 'PDO->__construct(...)', $string);\r
+               $string = preg_replace('/DB->__construct\(.*\)/', 'DB->__construct(...)', $string);\r
+               return $string;\r
+       }\r
+    \r
+       /**\r
+        * @see \Exception::getMessage()\r
+        */\r
+       public function _getMessage() {\r
+               $e = ($this->getPrevious() ?: $this);\r
+               return $e->getMessage();\r
+       }\r
+    \r
+       /**\r
+        * Returns the description of this exception.\r
+        * \r
+        * @return      string\r
+        */\r
+       public function getDescription() {\r
+               return $this->description;\r
+       }\r
+       \r
+       /**\r
+        * Returns exception id\r
+        * \r
+        * @return      string\r
+        */\r
+       public function getExceptionID() {\r
+               if (empty($this->exceptionID)) {\r
+                       $this->logError();\r
+               }\r
+               \r
+               return $this->exceptionID;\r
+       }\r
+    \r
+       /**\r
+        * Writes an error to log file.\r
+        */\r
+       protected function logError() {\r
+               if (!empty($this->exceptionID)) {\r
+                       return;\r
+               }\r
+               \r
+               $logFile = DNS_DIR . '/log/' . gmdate('Y-m-d', time()) . '.txt';\r
+               \r
+               // try to create file\r
+               @touch($logFile);\r
+               \r
+               // validate if file exists and is accessible for us\r
+               if (!file_exists($logFile) || !is_writable($logFile)) {\r
+                       /*\r
+                               We cannot recover if we reached this point, the server admin\r
+                               is urged to fix his pretty much broken configuration.\r
+                               \r
+                               GLaDOS: Look at you, sailing through the air majestically, like an eagle... piloting a blimp.\r
+                       */\r
+                       return;\r
+               }\r
+               \r
+               $e = ($this->getPrevious() ?: $this);\r
+               \r
+               // don't forget to update ExceptionLogViewPage, when changing the log file format\r
+               $message = gmdate('r', time())."\n".\r
+                       'Message: '.$e->getMessage()."\n".\r
+                       'File: '.$e->getFile().' ('.$e->getLine().")\n".\r
+                       'PHP version: '.phpversion()."\n".\r
+                       'DNS version: '.DNS_VERSION."\n".\r
+                       'Request URI: '.(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '')."\n".\r
+                       'Referrer: '.(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '')."\n".\r
+                       'User-Agent: '.(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '')."\n".\r
+                       'Information: '.json_encode($this->information)."\n".\r
+                       "Stacktrace: \n  ".implode("\n  ", explode("\n", $this->__getTraceAsString()))."\n";\r
+               \r
+               // calculate Exception-ID\r
+               $this->exceptionID = sha1($message);\r
+               $message = "<<<<<<<<".$this->exceptionID."<<<<\n".$message."<<<<\n\n";\r
+               \r
+               // append\r
+               @file_put_contents($logFile, $message, FILE_APPEND);\r
+       }\r
+    \r
+       /**\r
+        * @see \wcf\system\exception\IPrintableException::show()\r
+        */\r
+       public function show() {\r
+               // send status code\r
+               @header('HTTP/1.1 503 Service Unavailable');\r
+               \r
+               // print report\r
+               $e = ($this->getPrevious() ?: $this);\r
+               ?><!DOCTYPE html>\r
+               <html>\r
+                       <head>\r
+                               <title>Fatal error: <?php echo htmlspecialchars($this->_getMessage(), ENT_COMPAT, 'UTF-8'); ?></title>\r
+                               <meta charset="utf-8" />\r
+                               <style>\r
+                                       .systemException {\r
+                                               font-family: 'Trebuchet MS', Arial, sans-serif !important;\r
+                                               font-size: 80% !important;\r
+                                               text-align: left !important;\r
+                                               border: 1px solid #036;\r
+                                               border-radius: 7px;\r
+                                               background-color: #eee !important;\r
+                                               overflow: auto !important;\r
+                                       }\r
+                                       .systemException h1 {\r
+                                               font-size: 130% !important;\r
+                                               font-weight: bold !important;\r
+                                               line-height: 1.1 !important;\r
+                                               text-decoration: none !important;\r
+                                               text-shadow: 0 -1px 0 #003 !important;\r
+                                               color: #fff !important;\r
+                                               word-wrap: break-word !important;\r
+                                               border-bottom: 1px solid #036;\r
+                                               border-top-right-radius: 6px;\r
+                                               border-top-left-radius: 6px;\r
+                                               background-color: #369 !important;\r
+                                               margin: 0 !important;\r
+                                               padding: 5px 10px !important;\r
+                                       }\r
+                                       .systemException div {\r
+                                               border-top: 1px solid #fff;\r
+                                               border-bottom-right-radius: 6px;\r
+                                               border-bottom-left-radius: 6px;\r
+                                               padding: 0 10px !important;\r
+                                       }\r
+                                       .systemException h2 {\r
+                                               font-size: 130% !important;\r
+                                               font-weight: bold !important;\r
+                                               color: #369 !important;\r
+                                               text-shadow: 0 1px 0 #fff !important;\r
+                                               margin: 5px 0 !important;\r
+                                       }\r
+                                       .systemException pre, .systemException p {\r
+                                               text-shadow: none !important;\r
+                                               color: #555 !important;\r
+                                               margin: 0 !important;\r
+                                       }\r
+                                       .systemException pre {\r
+                                               font-size: .85em !important;\r
+                                               font-family: "Courier New" !important;\r
+                                               text-overflow: ellipsis;\r
+                                               padding-bottom: 1px;\r
+                                               overflow: hidden !important;\r
+                                       }\r
+                                       .systemException pre:hover{\r
+                                               text-overflow: clip;\r
+                                               overflow: auto !important;\r
+                                       }\r
+                               </style>\r
+                       </head>\r
+                       <body>\r
+                               <div class="systemException">\r
+                                       <h1>Fatal error: <?php if(!$this->getExceptionID()) { ?>Unable to write log file, please make &quot;<?php echo DNS_DIR; ?>/log/&quot; writable!<?php } else { echo htmlspecialchars($this->_getMessage(), ENT_COMPAT, 'UTF-8'); } ?></h1>\r
+                    \r
+                    <?php if (DNS::debugModeIsEnabled()) { ?>\r
+                                               <div>\r
+                                                       <?php if ($this->getDescription()) { ?><p><br /><?php echo $this->getDescription(); ?></p><?php } ?>\r
+                                                       \r
+                                                       <h2>Information:</h2>\r
+                                                       <p>\r
+                                                               <b>error message:</b> <?php echo htmlspecialchars($this->_getMessage(), ENT_COMPAT, 'UTF-8'); ?><br>\r
+                                                               <b>error code:</b> <?php echo intval($e->getCode()); ?><br>\r
+                                                               <?php echo $this->information; ?>\r
+                                                               <b>file:</b> <?php echo htmlspecialchars($e->getFile(), ENT_COMPAT, 'UTF-8'); ?> (<?php echo $e->getLine(); ?>)<br>\r
+                                                               <b>php version:</b> <?php echo htmlspecialchars(phpversion(), ENT_COMPAT, 'UTF-8'); ?><br>\r
+                                                               <b>dns version:</b> <?php echo DNS_VERSION; ?><br>\r
+                                                               <b>date:</b> <?php echo gmdate('r'); ?><br>\r
+                                                               <b>request:</b> <?php if (isset($_SERVER['REQUEST_URI'])) echo htmlspecialchars($_SERVER['REQUEST_URI'], ENT_COMPAT, 'UTF-8'); ?><br>\r
+                                                               <b>referer:</b> <?php if (isset($_SERVER['HTTP_REFERER'])) echo htmlspecialchars($_SERVER['HTTP_REFERER'], ENT_COMPAT, 'UTF-8'); ?><br>\r
+                                                       </p>\r
+                                                       \r
+                                                       <h2>Stacktrace:</h2>\r
+                                                       <pre><?php echo htmlspecialchars($this->__getTraceAsString(), ENT_COMPAT, 'UTF-8'); ?></pre>\r
+                                               </div>\r
+                    <?php } else { ?>\r
+                                               <div>\r
+                                                       <h2>Information:</h2>\r
+                                                       <p>\r
+                                                               <?php if (!$this->getExceptionID()) { ?>\r
+                                                                       Unable to write log file, please make &quot;<?php echo DNS_DIR; ?>/log/&quot; writable!\r
+                                                               <?php } else { ?>\r
+                                                                       <b>ID:</b> <code><?php echo $this->getExceptionID(); ?></code><br>\r
+                                                                       <?php echo "Please send the ID above to the site administrator."; ?>\r
+                                                               <?php } ?>\r
+                                                       </p>\r
+                                               </div>\r
+                    <?php } ?>\r
+                                       <?php echo $this->functions; ?>\r
+                               </div>\r
+                       </body>\r
+               </html>\r
+               \r
+               <?php\r
+       }\r
+}\r