Update DatabaseException to make proper use of the new Exception handling
authorTim Düsterhus <duesterhus@woltlab.com>
Tue, 29 Sep 2015 19:34:36 +0000 (21:34 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Tue, 1 Dec 2015 21:15:48 +0000 (22:15 +0100)
wcfsetup/install/files/lib/core.functions.php
wcfsetup/install/files/lib/system/database/Database.class.php
wcfsetup/install/files/lib/system/database/DatabaseException.class.php
wcfsetup/install/files/lib/system/database/MySQLDatabase.class.php
wcfsetup/install/files/lib/system/database/PostgreSQLDatabase.class.php
wcfsetup/install/files/lib/system/database/exception/DatabaseException.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/database/exception/DatabaseQueryException.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/database/exception/DatabaseQueryExecutionException.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/database/exception/DatabaseTransactionException.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/database/statement/PreparedStatement.class.php
wcfsetup/install/files/lib/system/exception/IExtraInformationException.class.php [new file with mode: 0644]

index 35b2121c3cb6a6325c9d9329160330bceeef122e..5d586439fbfd7902b75a953f06871366ad2011ac 100644 (file)
@@ -42,6 +42,7 @@ namespace {
 }
 
 namespace wcf\functions\exception {
+       use wcf\system\exception\IExtraInformationException;
        use wcf\system\exception\SystemException;
        use wcf\system\WCF;
        use wcf\util\FileUtil;
@@ -148,15 +149,15 @@ namespace wcf\functions\exception {
                                </div>
                                <?php if (WCF::debugModeIsEnabled()) { ?>
                                        <div>
-                                               <h2>System Information:</h2>
+                                               <h2>System Information</h2>
                                                <dl>
-                                                       <dt>PHP Version:</dt> <dd><?php echo StringUtil::encodeHTML(phpversion()); ?></dd>
-                                                       <dt>WCF Version:</dt> <dd><?php echo StringUtil::encodeHTML(WCF_VERSION); ?></dd>
-                                                       <dt>Date:</dt> <dd><?php echo gmdate('r'); ?></dd>
-                                                       <dt>Request URI:</dt> <dd><?php if (isset($_SERVER['REQUEST_URI'])) echo StringUtil::encodeHTML($_SERVER['REQUEST_URI']); ?></dd>
-                                                       <dt>Referrer:</dt> <dd><?php if (isset($_SERVER['HTTP_REFERER'])) echo StringUtil::encodeHTML($_SERVER['HTTP_REFERER']); ?></dd>
-                                                       <dt>User Agent:</dt> <dd><?php if (isset($_SERVER['HTTP_USER_AGENT'])) echo StringUtil::encodeHTML($_SERVER['HTTP_USER_AGENT']); ?></dd>
-                                                       <dt>Peak Memory Usage:</dt> <dd><?php echo $peakMemory = memory_get_peak_usage(); ?>/<?php echo $memoryLimit = FileUtil::getMemoryLimit(); ?> Byte (<?php echo round($peakMemory / 1024 / 1024, 3); ?>/<?php echo round($memoryLimit / 1024 / 1024, 3); ?> MiB)</dd>
+                                                       <dt>PHP Version</dt> <dd><?php echo StringUtil::encodeHTML(phpversion()); ?></dd>
+                                                       <dt>WCF Version</dt> <dd><?php echo StringUtil::encodeHTML(WCF_VERSION); ?></dd>
+                                                       <dt>Date</dt> <dd><?php echo gmdate('r'); ?></dd>
+                                                       <dt>Request URI</dt> <dd><?php if (isset($_SERVER['REQUEST_URI'])) echo StringUtil::encodeHTML($_SERVER['REQUEST_URI']); ?></dd>
+                                                       <dt>Referrer</dt> <dd><?php if (isset($_SERVER['HTTP_REFERER'])) echo StringUtil::encodeHTML($_SERVER['HTTP_REFERER']); ?></dd>
+                                                       <dt>User Agent</dt> <dd><?php if (isset($_SERVER['HTTP_USER_AGENT'])) echo StringUtil::encodeHTML($_SERVER['HTTP_USER_AGENT']); ?></dd>
+                                                       <dt>Peak Memory Usage</dt> <dd><?php echo $peakMemory = memory_get_peak_usage(); ?>/<?php echo $memoryLimit = FileUtil::getMemoryLimit(); ?> Byte (<?php echo round($peakMemory / 1024 / 1024, 3); ?>/<?php echo round($memoryLimit / 1024 / 1024, 3); ?> MiB)</dd>
                                                </dl>
                                        </div>
                                        <?php
@@ -164,15 +165,15 @@ namespace wcf\functions\exception {
                                        do {
                                        ?>
                                        <div>
-                                               <h2><?php if (!$e->getPrevious() && !$first) { echo "Original "; } else if ($e->getPrevious() && $first) { echo "Final "; } ?>Error:</h2>
+                                               <h2><?php if (!$e->getPrevious() && !$first) { echo "Original "; } else if ($e->getPrevious() && $first) { echo "Final "; } ?>Error</h2>
                                                <?php if ($e instanceof SystemException && $e->getDescription()) { ?>
                                                        <p><?php echo $e->getDescription(); ?></p>
                                                <?php } ?>
                                                <dl>
-                                                       <dt>Error Class:</dt> <dd><?php echo get_class($e); ?></dd>
-                                                       <dt>Error Message:</dt> <dd><?php echo StringUtil::encodeHTML($e->getMessage()); ?></dd>
-                                                       <dt>Error Code:</dt> <dd><?php echo intval($e->getCode()); ?></dd>
-                                                       <dt>File:</dt> <dd><?php echo StringUtil::encodeHTML(sanitizePath($e->getFile())); ?> (<?php echo $e->getLine(); ?>)</dd>
+                                                       <dt>Error Class</dt> <dd><?php echo get_class($e); ?></dd>
+                                                       <dt>Error Message</dt> <dd><?php echo StringUtil::encodeHTML($e->getMessage()); ?></dd>
+                                                       <?php if ($e->getCode()) { ?><dt>Error Code</dt> <dd><?php echo intval($e->getCode()); ?></dd><?php } ?>
+                                                       <dt>File</dt> <dd><?php echo StringUtil::encodeHTML(sanitizePath($e->getFile())); ?> (<?php echo $e->getLine(); ?>)</dd>
                                                        <?php
                                                        if ($e instanceof SystemException) {
                                                                ob_start();
@@ -186,8 +187,13 @@ namespace wcf\functions\exception {
                                                                        throw new \Exception("Using the 'information' property of SystemException is not supported any more.");
                                                                }
                                                        }
+                                                       if ($e instanceof IExtraInformationException) {
+                                                               foreach ($e->getExtraInformation() as list($key, $value)) {
+                                                                       echo "<dt>".StringUtil::encodeHTML($key)."</dt> <dd>".StringUtil::encodeHTML($value)."</dd>";
+                                                               }
+                                                       }
                                                        ?>
-                                                       <dt>Stack Trace:</dt>
+                                                       <dt>Stack Trace</dt>
                                                        <dd class="pre"><?php
                                                                $trace = array_map(function ($item) {
                                                                        if (!isset($item['file'])) $item['file'] = '[internal function]';
@@ -216,7 +222,7 @@ namespace wcf\functions\exception {
                                                                                                return $item ? 'true' : 'false';
                                                                                        case 'array':
                                                                                                $keys = array_keys($item);
-                                                                                               if (count($keys) > 5) return "[ ".StringUtil::HELLIP." ]";
+                                                                                               if (count($keys) > 5) return "[ ".count($keys)." items ]";
                                                                                                return '[ '.implode(', ', array_map(function ($item) {
                                                                                                        return $item.' => ';
                                                                                                }, $keys)).']';
@@ -257,6 +263,8 @@ namespace wcf\functions\exception {
                        
                        if (!$ignorePaths) {
                                $item['args'] = array_map(function ($item) {
+                                       if (!is_string($item)) return $item;
+                                       
                                        if (preg_match('~^'.preg_quote($_SERVER['DOCUMENT_ROOT'], '~').'~', $item)) {
                                                $item = sanitizePath($item);
                                        }
index fba3294d3b7ed85107f22743110b78fad57c7660..91eb80586458776bf6cae17b054661d7358728ce 100644 (file)
@@ -1,6 +1,9 @@
 <?php
 namespace wcf\system\database;
 use wcf\system\benchmark\Benchmark;
+use wcf\system\database\exception\DatabaseException as GenericDatabaseException;
+use wcf\system\database\exception\DatabaseQueryException;
+use wcf\system\database\exception\DatabaseTransactionException;
 use wcf\system\WCF;
 
 /**
@@ -124,7 +127,7 @@ abstract class Database {
                        return $this->pdo->lastInsertId();
                }
                catch (\PDOException $e) {
-                       throw new DatabaseException("Cannot fetch last insert id: " . $e->getMessage(), $this);
+                       throw new GenericDatabaseException("Cannot fetch last insert id", $e);
                }
        }
        
@@ -150,7 +153,7 @@ abstract class Database {
                        return $result;
                }
                catch (\PDOException $e) {
-                       throw new DatabaseException("Cannot begin transaction: " . $e->getMessage(), $this);
+                       throw new DatabaseTransactionException("Could not begin transaction", $e);
                }
        }
        
@@ -179,7 +182,7 @@ abstract class Database {
                        return $result;
                }
                catch (\PDOException $e) {
-                       throw new DatabaseException("Cannot commit transaction: " . $e->getMessage(), $this);
+                       throw new DatabaseTransactionException("Could not commit transaction", $e);
                }
        }
        
@@ -207,7 +210,7 @@ abstract class Database {
                        return $result;
                }
                catch (\PDOException $e) {
-                       throw new DatabaseException("Cannot rollback transaction: " . $e->getMessage(), $this);
+                       throw new DatabaseTransactionException("Could not roll back transaction", $e);
                }
        }
        
@@ -228,7 +231,7 @@ abstract class Database {
                        return new $this->preparedStatementClassName($this, $pdoStatement, $statement);
                }
                catch (\PDOException $e) {
-                       throw new DatabaseException($e->getMessage(), $this, null, $statement);
+                       throw new DatabaseQueryException("Could not prepare statement '".$statement."'", $e);
                }
        }
        
index 377531e35a7fcc50dd3c46a45d83916e5ed05916..d50a312dc8ba23d2bcbe7818111a711d0572966a 100644 (file)
@@ -13,6 +13,7 @@ use wcf\util\StringUtil;
  * @package    com.woltlab.wcf
  * @subpackage system.database
  * @category   Community Framework
+ * @deprecated 2.2 - Use \wcf\system\database\exception\DatabaseException
  */
 class DatabaseException extends SystemException {
        /**
@@ -128,28 +129,4 @@ class DatabaseException extends SystemException {
        public function getDBType() {
                return $this->DBType;
        }
-       
-       /**
-        * Prints the error page.
-        */
-       public function show() {
-               $this->information .= '<b>sql type:</b> ' . StringUtil::encodeHTML($this->getDBType()) . '<br />';
-               $this->information .= '<b>sql error:</b> ' . StringUtil::encodeHTML($this->getErrorDesc()) . '<br />';
-               $this->information .= '<b>sql error number:</b> ' . StringUtil::encodeHTML($this->getErrorNumber()) . '<br />';
-               $this->information .= '<b>sql version:</b> ' . StringUtil::encodeHTML($this->getSQLVersion()) . '<br />';
-               if ($this->preparedStatement !== null) {
-                       $this->information .= '<b>sql query:</b> ' . StringUtil::encodeHTML($this->preparedStatement->getSQLQuery()) . '<br />';
-                       $parameters = $this->preparedStatement->getSQLParameters();
-                       if (!empty($parameters)) {
-                               foreach ($parameters as $index => $parameter) {
-                                       $this->information .= '<b>sql query parameter ' . $index . ':</b>' . StringUtil::encodeHTML($parameter) . '<br />';
-                               }
-                       }
-               }
-               else if ($this->sqlQuery !== null) {
-                       $this->information .= '<b>sql query:</b> ' . StringUtil::encodeHTML($this->sqlQuery) . '<br />';
-               }
-               
-               parent::show();
-       }
 }
index 3fce425a53ee89091e80b0067bf3e26eb354e310..ea28c2b85a00e47f8de0974e797b7897121eb966 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 namespace wcf\system\database;
+use wcf\system\database\exception\DatabaseException as GenericDatabaseException;
 
 /**
  * This is the database implementation for MySQL 5.1 or higher using PDO.
@@ -43,7 +44,7 @@ class MySQLDatabase extends Database {
                        $this->setAttributes();
                }
                catch (\PDOException $e) {
-                       throw new DatabaseException("Connecting to MySQL server '".$this->host."' failed:\n".$e->getMessage(), $this);
+                       throw new GenericDatabaseException("Connecting to MySQL server '".$this->host."' failed", $e);
                }
        }
        
index 035cab49a3ca77579c959581434802a0a54ad23c..6351cce293b7aaf30f89aa433e345ad60078078d 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 namespace wcf\system\database;
+use wcf\system\database\exception\DatabaseException as GenericDatabaseException;
 use wcf\util\StringStack;
 
 /**
@@ -29,7 +30,7 @@ class PostgreSQLDatabase extends Database {
                        $this->setAttributes();
                }
                catch (\PDOException $e) {
-                       throw new DatabaseException("Connecting to PostgreSQL server '".$this->host."' failed:\n".$e->getMessage(), $this);
+                       throw new GenericDatabaseException("Connecting to PostgreSQL server '".$this->host."' failed", $e);
                }
                
                // set connection character set
@@ -72,7 +73,7 @@ class PostgreSQLDatabase extends Database {
                        return $this->pdo->lastInsertId('"' . $table . '_' . $field . '_seq"');
                }
                catch (\PDOException $e) {
-                       throw new DatabaseException("Can not fetch last insert id", $this);
+                       throw new GenericDatabaseException("Can not fetch last insert id", $this);
                }
        }
        
diff --git a/wcfsetup/install/files/lib/system/database/exception/DatabaseException.class.php b/wcfsetup/install/files/lib/system/database/exception/DatabaseException.class.php
new file mode 100644 (file)
index 0000000..565b3fc
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\system\database\exception;
+
+/**
+ * Denotes an database related error.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.database.exception
+ * @category   Community Framework
+ */
+class DatabaseException extends \wcf\system\database\DatabaseException {
+       /**
+        * @see \Exception::__construct()
+        */
+       public function __construct($message, \PDOException $previous = null) {
+               \Exception::__construct($message, 0, $previous);
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/database/exception/DatabaseQueryException.class.php b/wcfsetup/install/files/lib/system/database/exception/DatabaseQueryException.class.php
new file mode 100644 (file)
index 0000000..9e6c568
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+namespace wcf\system\database\exception;
+
+/**
+ * Denotes an error that is related to a specific database query.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.database.exception
+ * @category   Community Framework
+ */
+class DatabaseQueryException extends DatabaseException {
+
+}
diff --git a/wcfsetup/install/files/lib/system/database/exception/DatabaseQueryExecutionException.class.php b/wcfsetup/install/files/lib/system/database/exception/DatabaseQueryExecutionException.class.php
new file mode 100644 (file)
index 0000000..091222c
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+namespace wcf\system\database\exception;
+use wcf\system\exception\IExtraInformationException;
+
+/**
+ * Denotes an error that is related to a specific database query.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.database.exception
+ * @category   Community Framework
+ */
+class DatabaseQueryExecutionException extends DatabaseQueryException implements IExtraInformationException {
+       /**
+        * Parameters that were passed to execute().
+        * @var array
+        */
+       protected $parameters = [];
+       
+       /**
+        * @see \Exception::__construct()
+        */
+       public function __construct($message, $parameters, \PDOException $previous = null) {
+               \Exception::__construct($message, 0, $previous);
+               
+               $this->parameters = $parameters;
+       }
+       
+       /**
+        * Returns the parameters that were passed to execute().
+        *
+        * @return      array
+        */
+       public function getParameters() {
+               return $this->parameters;
+       }
+       
+       /**
+        * @see \wcf\system\exception\IExtraInformationException::getExtraInformation()
+        */
+       public function getExtraInformation() {
+               return array_map(function ($val) {
+                       static $i = 0;
+                       return [ 'Query Parameter '.(++$i), $val ];
+               }, $this->getParameters());
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/database/exception/DatabaseTransactionException.class.php b/wcfsetup/install/files/lib/system/database/exception/DatabaseTransactionException.class.php
new file mode 100644 (file)
index 0000000..4d5ea38
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+namespace wcf\system\database\exception;
+
+/**
+ * Denotes an error that is related to a database transaction.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.database.exception
+ * @category   Community Framework
+ */
+class DatabaseTransactionException extends DatabaseException {
+
+}
index 6fe37efd642f0e5242dbeca465dd8859b53b33ad..1f0ff086a3ba02d3e1249f2a8195fe4aec513c95 100644 (file)
@@ -2,7 +2,8 @@
 namespace wcf\system\database\statement;
 use wcf\system\benchmark\Benchmark;
 use wcf\system\database\Database;
-use wcf\system\database\DatabaseException;
+use wcf\system\database\exception\DatabaseQueryException;
+use wcf\system\database\exception\DatabaseQueryExecutionException;
 use wcf\system\exception\SystemException;
 use wcf\system\WCF;
 
@@ -70,7 +71,7 @@ class PreparedStatement {
                        return call_user_func_array(array($this->pdoStatement, $name), $arguments);
                }
                catch (\PDOException $e) {
-                       throw new DatabaseException('Could not handle prepared statement: '.$e->getMessage(), $this->database, $this);
+                       throw new DatabaseQueryException("Could call '".$name."' on '".$this->query."'", $e);
                }
        }
        
@@ -86,12 +87,11 @@ class PreparedStatement {
                try {
                        if (WCF::benchmarkIsEnabled()) Benchmark::getInstance()->start($this->query, Benchmark::TYPE_SQL_QUERY);
                        
-                       if (empty($parameters)) $result = $this->pdoStatement->execute();
-                       else $result = $this->pdoStatement->execute($parameters);
+                       $result = $this->pdoStatement->execute($parameters);
                        
                        if (!$result) {
                                $errorInfo = $this->pdoStatement->errorInfo();
-                               throw new DatabaseException('Could not execute prepared statement: '.$errorInfo[0].' '.$errorInfo[2], $this->database, $this);
+                               throw new DatabaseQueryExecutionException("Could not execute statement '".$this->query."': ".$errorInfo[0].' '.$errorInfo[2], $parameters);
                        }
 
                        if (WCF::benchmarkIsEnabled()) Benchmark::getInstance()->stop();
@@ -99,7 +99,7 @@ class PreparedStatement {
                catch (\PDOException $e) {
                        if (WCF::benchmarkIsEnabled()) Benchmark::getInstance()->stop();
                        
-                       throw new DatabaseException('Could not execute prepared statement: '.$e->getMessage(), $this->database, $this);
+                       throw new DatabaseQueryExecutionException("Could not execute statement '".$this->query."'", $parameters, $e);
                }
        }
        
@@ -192,7 +192,7 @@ class PreparedStatement {
                        return $this->pdoStatement->rowCount();
                }
                catch (\PDOException $e) {
-                       throw new DatabaseException("Can not fetch affected rows: ".$e->getMessage(), $this);
+                       throw new DatabaseQueryException("Could fetch affected rows for '".$this->query."'", $e);
                }
        }
        
diff --git a/wcfsetup/install/files/lib/system/exception/IExtraInformationException.class.php b/wcfsetup/install/files/lib/system/exception/IExtraInformationException.class.php
new file mode 100644 (file)
index 0000000..882cd41
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+namespace wcf\system\exception;
+
+/**
+ * Denotes an Exception with extra information for the human reader.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.exception
+ * @category   Community Framework
+ */
+interface IExtraInformationException {
+       /**
+        * Returns an array of (key, value) tuples with extra information to show
+        * in the human readable error log.
+        * Avoid including sensitive information (such as private keys or passwords).
+        * 
+        * @return      array<array>
+        */
+       public function getExtraInformation();
+}