From 89ccdb24ba3d910976151879b5e67270cd6b864b Mon Sep 17 00:00:00 2001 From: Abel Hoogeveen Date: Fri, 14 Jul 2017 16:05:52 +0200 Subject: [PATCH] Fixed multiple bugs relating the database system. Debugging now actually works in the Database Driver, --- .gitignore | 1 + .travis.yml | 1 + src/Database/DB_driver.php | 49 +++++----- src/Database/DB_utility.php | 1 + src/Database/drivers/pdo/pdo_driver.php | 1 - src/Database/drivers/pdo/pdo_result.php | 2 - src/FuzeWorks/Database.php | 121 +++++++++++++++++++----- 7 files changed, 127 insertions(+), 49 deletions(-) diff --git a/.gitignore b/.gitignore index 9f24f98..5316808 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ vendor/ build/ doc nbproject +._* diff --git a/.travis.yml b/.travis.yml index 2c8dedd..e8627ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: php php: + - 7.1 - 7 - 5.6 diff --git a/src/Database/DB_driver.php b/src/Database/DB_driver.php index 96f7a02..ab15243 100644 --- a/src/Database/DB_driver.php +++ b/src/Database/DB_driver.php @@ -30,12 +30,12 @@ * @version Version 1.0.0 */ -use FuzeWorks\Factory; +use FuzeWorks\Core; use FuzeWorks\Logger; -use FuzeWorks\Exception\DatabaseException; use FuzeWorks\Utf8; use FuzeWorks\Language; -use FuzeWorks\Core; +use FuzeWorks\DatabaseTracyBridge; +use FuzeWorks\Exception\DatabaseException; /** * Database Driver Class @@ -175,7 +175,7 @@ abstract class FW_DB_driver { * * @var bool */ - public $db_debug = FALSE; + public $db_debug = TRUE; /** * Benchmark time @@ -217,6 +217,14 @@ abstract class FW_DB_driver { */ public $queries = array(); + /** + * Data of performed queries + * + * @see FW_DB_driver::$save_queries + * @var array + */ + public $query_data = array(); + /** * Query times * @@ -375,8 +383,6 @@ abstract class FW_DB_driver { } } - $this->factory = Factory::getInstance(); - Logger::log('Database Driver ' . get_class($this) . ' Initialized'); } @@ -614,7 +620,6 @@ abstract class FW_DB_driver { { if ($sql === '') { - Logger::logError('Invalid query: '.$sql); return ($this->db_debug) ? $this->display_error('db_invalid_query') : FALSE; } elseif ( ! is_bool($return_object)) @@ -661,6 +666,7 @@ abstract class FW_DB_driver { if ($this->save_queries === TRUE) { $this->query_times[] = 0; + $this->query_data[] = array('error' => $this->error(), 'rows' => 0); } // This will trigger a rollback if transactions are being used @@ -672,9 +678,6 @@ abstract class FW_DB_driver { // Grab the error now, as we might run some additional queries before displaying the error $error = $this->error(); - // Log errors - Logger::logError('Query error: '.$error['message'].' - Invalid query: '.$sql); - if ($this->db_debug) { // We call this function in order to roll-back queries @@ -703,11 +706,6 @@ abstract class FW_DB_driver { $time_end = microtime(TRUE); $this->benchmark += $time_end - $time_start; - if ($this->save_queries === TRUE) - { - $this->query_times[] = $time_end - $time_start; - } - // Increment the query counter $this->query_count++; @@ -727,6 +725,12 @@ abstract class FW_DB_driver { $driver = $this->load_rdriver(); $RES = new $driver($this); + if ($this->save_queries === TRUE) + { + $this->query_times[] = $time_end - $time_start; + $this->query_data[] = array('error' => $this->error(), 'rows' => $RES->num_rows()); + } + // Is query caching enabled? If so, we'll serialize the // result object and save it to a cache file. if ($this->cache_on === TRUE && $this->_cache_init()) @@ -1762,6 +1766,8 @@ abstract class FW_DB_driver { // the backtrace until the source file is no longer in the // database folder. $trace = debug_backtrace(); + $file = ''; + $line = ''; foreach ($trace as $call) { if (isset($call['file'], $call['class'])) @@ -1775,19 +1781,16 @@ abstract class FW_DB_driver { if (strpos($call['file'], Core::$coreDir . DS . 'Database') === FALSE && strpos($call['class'], 'Loader') === FALSE) { // Found it - use a relative path for safety - $message[] = 'Filename: '.str_replace(array('Application', 'Core'), '', $call['file']); - $message[] = 'Line Number: '.$call['line']; + $file = str_replace(array('Application', 'Core'), '', $call['file']); + $line = $call['line']; break; } } } - Logger::logError($heading); - foreach ($message as $message) { - Logger::logError($message); - } - Logger::http_error(500); - exit(8); // EXIT_DATABASE + Logger::logError($heading . " | " . implode(' | ', $message), null, $file, $line); + throw new DatabaseException($heading . ": " . implode(' ', $this->error()), 1); + } // -------------------------------------------------------------------- diff --git a/src/Database/DB_utility.php b/src/Database/DB_utility.php index 29e5965..321c83a 100644 --- a/src/Database/DB_utility.php +++ b/src/Database/DB_utility.php @@ -33,6 +33,7 @@ use FuzeWorks\Logger; use FuzeWorks\Helpers; use FuzeWorks\Libraries; +use Fuzeworks\Factory; use FuzeWorks\Exception\DatabaseException; /** diff --git a/src/Database/drivers/pdo/pdo_driver.php b/src/Database/drivers/pdo/pdo_driver.php index 12f3b39..10d5325 100644 --- a/src/Database/drivers/pdo/pdo_driver.php +++ b/src/Database/drivers/pdo/pdo_driver.php @@ -32,7 +32,6 @@ use FuzeWorks\Logger; use FuzeWorks\Exception\DatabaseException; -use PDOException; /** * PDO Database Adapter Class diff --git a/src/Database/drivers/pdo/pdo_result.php b/src/Database/drivers/pdo/pdo_result.php index c8b885f..4bac608 100644 --- a/src/Database/drivers/pdo/pdo_result.php +++ b/src/Database/drivers/pdo/pdo_result.php @@ -30,8 +30,6 @@ * @version Version 1.0.0 */ -use \Exception; - /** * PDO Result Class * diff --git a/src/FuzeWorks/Database.php b/src/FuzeWorks/Database.php index 9f9e597..d729a12 100644 --- a/src/FuzeWorks/Database.php +++ b/src/FuzeWorks/Database.php @@ -31,6 +31,7 @@ */ namespace FuzeWorks; +use FuzeWorks\Exception\DatabaseException; use FW_DB; /** @@ -50,6 +51,12 @@ class Database * @var type FW_DB|null */ protected static $defaultDB = null; + + /** + * Array of all the non-default databases + * @var array FW_DB|null + */ + protected static $databases = array(); /** * The default database forge. @@ -57,12 +64,29 @@ class Database */ protected static $defaultForge = null; + /** + * Array of all the non-default databases forges. + * @var array FW_DB_forge|null + */ + protected static $forges = array(); + /** * The default database utility. * @var type FW_DB_utility|null */ protected static $defaultUtil = null; + /** + * Register with the TracyBridge upon startup + */ + public function __construct() + { + if (class_exists('Tracy\Debugger', true)) + { + DatabaseTracyBridge::register(); + } + } + /** * Retrieve a database using a DSN or the default configuration. * @@ -76,7 +100,6 @@ class Database * default one. $newInstance will also make sure that the loaded database is not default one. * This behaviour will be changed in the future. * - * @todo Change $newInstance behaviour related to self::$defaultDB * * If $queryBuilder = false is provided, the database will load without a queryBuilder. * By default the queryBuilder will load. @@ -84,48 +107,88 @@ class Database * @param string $parameters * @param bool $newInstance * @param bool $queryBuilder - * @return FW_DB + * @return FW_DB|bool */ public static function get($parameters = '', $newInstance = false, $queryBuilder = null) { - if (!$newInstance && is_object(self::$defaultDB) && ! empty(self::$defaultDB->conn_id)) + // Fire the event to allow settings to be changed + $event = Events::fireEvent('databaseLoadDriverEvent', $parameters, $newInstance, $queryBuilder); + if ($event->isCancelled()) + { + return false; + } + + // If an instance already exists and is requested, return it + if (isset($event->database) && empty($event->parameters)) + { + return self::$defaultDB = $event->database; + } + elseif (isset($event->database) && !empty($event->parameters)) + { + return self::$databases[$event->parameters] = $event->database; + } + elseif (empty($event->parameters) && !$event->newInstance && is_object(self::$defaultDB) && ! empty(self::$defaultDB->conn_id)) { return $reference = self::$defaultDB; } + elseif (!empty($event->parameters) && !$event->newInstance && isset(self::$databases[$event->parameters])) + { + return $reference = self::$databases[$event->parameters]; + } + // If a new instance is required, load it require_once (Core::$coreDir . DS . 'Database'.DS.'DB.php'); - if ($newInstance) + if ($event->newInstance === TRUE) { - return DB($parameters, $queryBuilder); + $database = DB($event->parameters, $event->queryBuilder); + } + elseif (empty($event->parameters) && $event->newInstance === FALSE) + { + $database = self::$defaultDB = DB($event->parameters, $event->queryBuilder); } else { - return self::$defaultDB = DB($parameters, $queryBuilder); + $database = self::$databases[$event->parameters] = DB($event->parameters, $event->queryBuilder); } + + // Tie it into the Tracy Bar if available + if (class_exists('\Tracy\Debugger', true)) + { + DatabaseTracyBridge::registerDatabase($database); + } + + return $database; } /** * Retrieves a database forge from the provided or default database. * * If no database is provided, the default database will be used. - * @todo Change $newInstance behaviour with default instances. * - * - * @param FW_DB $database - * @param bool $newInstance + * @param FW_DB|null $database + * @param bool $newInstance * @return FW_DB_forge */ public static function getForge($database = null, $newInstance = false) { + // Fire the event to allow settings to be changed + $event = Events::fireEvent('databaseLoadForgeEvent', $database, $newInstance); + if ($event->isCancelled()) + { + return false; + } + // First check if we're talking about the default forge and that one is already set - if (is_object($database) && $database === self::$defaultDB && is_object(self::$defaultForge)) + if (is_object($event->forge) && ($event->forge instanceof FW_DB_forge) ) + { + return $event->forge; + } + elseif (is_object($event->database) && $event->database === self::$defaultDB && is_object(self::$defaultForge)) { return $reference = self::$defaultForge; } - - - if ( ! is_object($database) OR ! ($database instanceof FW_DB)) + elseif ( ! is_object($event->database) OR ! ($event->database instanceof FW_DB)) { isset(self::$defaultDB) OR self::get('', false); $database =& self::$defaultDB; @@ -142,6 +205,10 @@ class Database require_once($driver_path); $class = 'FW_DB_'.$database->dbdriver.'_'.$database->subdriver.'_forge'; } + else + { + throw new DatabaseException("Could not load forge. Driver file does not exist.", 1); + } } else { @@ -149,7 +216,7 @@ class Database } // Create a new instance of set the default database - if ($newInstance) + if ($event->newInstance) { return new $class($database); } @@ -163,23 +230,31 @@ class Database * Retrieves a database utility from the provided or default database. * * If no database is provided, the default database will be used. - * @todo Change $newInstance behaviour with default instances. * - * - * @param FW_DB $database + * @param FW_DB|null $database * @param bool $newInstance * @return FW_DB_utility */ public static function getUtil($database = null, $newInstance = false) { - // First check if we're talking about the default util and that one is already set - if (is_object($database) && $database === self::$defaultDB && is_object(self::$defaultUtil)) + // Fire the event to allow settings to be changed + $event = Events::fireEvent('databaseLoadUtilEvent', $database, $newInstance); + if ($event->isCancelled()) + { + return false; + } + + // First check if we're talking about the default util and that one is already set + if (is_object($event->util) && ($event->util instanceof FW_DB_utility)) + { + return $event->util; + } + elseif (is_object($event->database) && $event->database === self::$defaultDB && is_object(self::$defaultUtil)) { - echo "CALLED"; return $reference = self::$defaultUtil; } - if ( ! is_object($database) OR ! ($database instanceof FW_DB)) + if ( ! is_object($event->database) OR ! ($event->database instanceof FW_DB)) { isset(self::$defaultDB) OR self::get('', false); $database = & self::$defaultDB; @@ -189,7 +264,7 @@ class Database require_once(Core::$coreDir . DS . 'Database'.DS.'drivers'.DS.$database->dbdriver.DS.$database->dbdriver.'_utility.php'); $class = 'FW_DB_'.$database->dbdriver.'_utility'; - if ($newInstance) + if ($event->newInstance) { return new $class($database); }