From 6f1b1b814da26ecef2bcb5c1ab45a547e0574c9f Mon Sep 17 00:00:00 2001 From: Abel Hoogeveen Date: Fri, 1 Mar 2019 11:15:30 +0100 Subject: [PATCH] Implemented changes requested by FuzeWorks\Application. - TracyBridge is now fully functional. --- .../DatabaseEngine/DatabaseDriver.php | 4 +- src/FuzeWorks/DatabaseEngine/PDOEngine.php | 32 ++++++-- .../DatabaseEngine/PDOStatementWrapper.php | 11 +-- .../DatabaseEngine/iDatabaseEngine.php | 1 + src/FuzeWorks/DatabaseTracyBridge.php | 73 +++++++++++-------- src/FuzeWorks/Model/PDOTableModel.php | 11 ++- src/Layout/layout.tracydatabasepanel.php | 52 +++++++++++++ src/Layout/layout.tracydatabasetab.php | 19 +++++ test/database/DatabaseTest.php | 7 -- 9 files changed, 157 insertions(+), 53 deletions(-) create mode 100644 src/Layout/layout.tracydatabasepanel.php create mode 100644 src/Layout/layout.tracydatabasetab.php diff --git a/src/FuzeWorks/DatabaseEngine/DatabaseDriver.php b/src/FuzeWorks/DatabaseEngine/DatabaseDriver.php index 5cc1047..689f5e5 100644 --- a/src/FuzeWorks/DatabaseEngine/DatabaseDriver.php +++ b/src/FuzeWorks/DatabaseEngine/DatabaseDriver.php @@ -55,11 +55,11 @@ abstract class DatabaseDriver * Log information about a query. Used for debugging issues * * @param string $queryString - * @param array $queryData + * @param int $queryData * @param float $queryTimings * @param array $queryError */ - protected function logQuery(string $queryString, array $queryData, float $queryTimings, array $queryError = []) + protected function logQuery(string $queryString, int $queryData, float $queryTimings, array $queryError = []) { $this->queries[] = [ 'queryString' => $queryString, diff --git a/src/FuzeWorks/DatabaseEngine/PDOEngine.php b/src/FuzeWorks/DatabaseEngine/PDOEngine.php index 17a2fbb..2e75a03 100644 --- a/src/FuzeWorks/DatabaseEngine/PDOEngine.php +++ b/src/FuzeWorks/DatabaseEngine/PDOEngine.php @@ -68,6 +68,13 @@ class PDOEngine extends DatabaseDriver implements iDatabaseEngine */ protected $pdoConnection; + /** + * Connection string with the database + * + * @var string + */ + protected $dsn; + /** * Whether a transaction has failed and should be reverted in the future * @@ -95,6 +102,11 @@ class PDOEngine extends DatabaseDriver implements iDatabaseEngine return 'pdo'; } + public function getConnectionDescription(): string + { + return is_null($this->dsn) ? 'none' : $this->dsn; + } + /** * Whether the database connection has been set up yet * @@ -115,17 +127,20 @@ class PDOEngine extends DatabaseDriver implements iDatabaseEngine public function setUp(array $parameters): bool { // Prepare variables for connection - $dsn = isset($parameters['dsn']) ? $parameters['dsn'] : null; + $this->dsn = isset($parameters['dsn']) ? $parameters['dsn'] : null; $username = isset($parameters['username']) ? $parameters['username'] : ''; $password = isset($parameters['password']) ? $parameters['password'] : ''; // Don't attempt connection without DSN - if (is_null($dsn)) + if (is_null($this->dsn)) throw new DatabaseException("Could not setUp PDOEngine. No DSN provided"); + // Set some base parameters which are required for FuzeWorks + $parameters[PDO::ATTR_ERRMODE] = PDO::ERRMODE_SILENT; + // Attempt to connect. Throw exception on failure try { - $this->pdoConnection = new PDO($dsn, $username, $password, $parameters); + $this->pdoConnection = new PDO($this->dsn, $username, $password, $parameters); } catch (PDOException $e) { throw new DatabaseException("Could not setUp PDOEngine. PDO threw PDOException: '" . $e->getMessage() . "'"); } @@ -192,12 +207,13 @@ class PDOEngine extends DatabaseDriver implements iDatabaseEngine * * @internal * @param string $queryString - * @param array $queryData + * @param int $queryData * @param float $queryTimings */ - public function logPDOQuery(string $queryString, array $queryData, float $queryTimings) + public function logPDOQuery(string $queryString, int $queryData, float $queryTimings, $errorInfo = []) { - $this->logQuery($queryString, $queryData, $queryTimings, $this->error()); + $errorInfo = empty($errorInfo) ? $this->error() : $errorInfo; + $this->logQuery($queryString, $queryData, $queryTimings, $errorInfo); } /** @@ -257,9 +273,9 @@ class PDOEngine extends DatabaseDriver implements iDatabaseEngine */ protected function error(): array { - $error = ['code' => '00000', 'message' => '']; + $error = []; $pdoError = $this->pdoConnection->errorInfo(); - if (empty($pdoError[0])) + if (empty($pdoError[0]) || $pdoError[0] == '00000') return $error; $error['code'] = isset($pdoError[1]) ? $pdoError[0] . '/' . $pdoError[1] : $pdoError[0]; diff --git a/src/FuzeWorks/DatabaseEngine/PDOStatementWrapper.php b/src/FuzeWorks/DatabaseEngine/PDOStatementWrapper.php index 25827aa..7141340 100644 --- a/src/FuzeWorks/DatabaseEngine/PDOStatementWrapper.php +++ b/src/FuzeWorks/DatabaseEngine/PDOStatementWrapper.php @@ -60,19 +60,20 @@ class PDOStatementWrapper $this->logQueryCallable = $logQueryCallable; } - public function execute(array $input_parameters = null) + public function execute(array $input_parameters = []) { // Run the query and benchmark the time $benchmarkStart = microtime(true); $result = $this->statement->execute($input_parameters); $benchmarkEnd = microtime(true) - $benchmarkStart; - call_user_func_array($this->logQueryCallable, [$this->statement->queryString, $input_parameters, $benchmarkEnd]); + $errInfo = $this->error(); + call_user_func_array($this->logQueryCallable, [$this->statement->queryString, $this->statement->rowCount(), $benchmarkEnd, $errInfo]); // If the query failed, throw an error if ($result === false) { // And throw an exception - throw new DatabaseException("Could not run query. Database returned an error. Error code: " . $this->error()['code']); + throw new DatabaseException("Could not run query. Database returned an error. Error code: " . $errInfo['code']); } return $result; @@ -85,9 +86,9 @@ class PDOStatementWrapper */ private function error(): array { - $error = ['code' => '00000', 'message' => '']; + $error = []; $pdoError = $this->statement->errorInfo(); - if (empty($pdoError[0])) + if (empty($pdoError[0]) || $pdoError[0] == '00000') return $error; $error['code'] = isset($pdoError[1]) ? $pdoError[0] . '/' . $pdoError[1] : $pdoError[0]; diff --git a/src/FuzeWorks/DatabaseEngine/iDatabaseEngine.php b/src/FuzeWorks/DatabaseEngine/iDatabaseEngine.php index 48fd110..438db06 100644 --- a/src/FuzeWorks/DatabaseEngine/iDatabaseEngine.php +++ b/src/FuzeWorks/DatabaseEngine/iDatabaseEngine.php @@ -40,6 +40,7 @@ namespace FuzeWorks\DatabaseEngine; interface iDatabaseEngine { public function getName(): string; + public function getConnectionDescription(): string; public function isSetup(): bool; public function setUp(array $parameters): bool; public function tearDown(): bool; diff --git a/src/FuzeWorks/DatabaseTracyBridge.php b/src/FuzeWorks/DatabaseTracyBridge.php index 382f411..1a44674 100644 --- a/src/FuzeWorks/DatabaseTracyBridge.php +++ b/src/FuzeWorks/DatabaseTracyBridge.php @@ -35,8 +35,10 @@ */ namespace FuzeWorks; +use FuzeWorks\DatabaseEngine\iDatabaseEngine; use Tracy\IBarPanel; use Tracy\Debugger; +use Tracy\Dumper; /** * DatabaseTracyBridge Class. @@ -47,12 +49,16 @@ use Tracy\Debugger; * It hooks into database usage and provides the information on the Tracy Bar panel. * * @author Abel Hoogeveen - * @copyright Copyright (c) 2013 - 2017, TechFuze. (http://techfuze.net) + * @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net) */ class DatabaseTracyBridge implements IBarPanel { + /** + * @var iDatabaseEngine[] + */ public static $databases = array(); + protected $results = array(); public static function register() @@ -62,7 +68,7 @@ class DatabaseTracyBridge implements IBarPanel $bar->addPanel($class); } - public static function registerDatabase($database) + public static function registerDatabase(iDatabaseEngine $database) { self::$databases[] = $database; } @@ -86,41 +92,48 @@ class DatabaseTracyBridge implements IBarPanel // Increase total databases $results['dbCount']++; - // First determine the ID - if (!empty($database->dsn)) - { - $databaseId = $database->dsn; - } - elseif (!empty($database->username) && !empty($database->database) && !empty($database->hostname)) - { - $databaseId = $database->username . '@' . $database->hostname . '/' . $database->database; - } - else - { - $databaseId = spl_object_hash($database); - } + // First determine the ID + $databaseId = $database->getConnectionDescription(); // Go through all queries - foreach ($database->queries as $key => $query) { - $results['queryCount']++; - $results['queryTimings'] += $database->query_times[$key]; - $results['queries'][$databaseId][$key]['query'] = $query; - $results['queries'][$databaseId][$key]['timings'] = $database->query_times[$key]; - $results['queries'][$databaseId][$key]['data'] = $database->query_data[$key]; + if (!method_exists($database, 'getQueries')) + { + continue; + } - // If errors are found, set this at the top of the array - if ($database->query_data[$key]['error']['code'] != 0) - $results['errorsFound'] = true; - } + foreach ($database->getQueries() as $query) + { + $results['queryTimings'] += $query['queryTimings']; + $key = $query['queryString']; + if (!isset($results['queries'][$databaseId][$key])) + $results['queryCount']++; + + $results['queries'][$databaseId][$key]['query'] = $query['queryString']; + $results['queries'][$databaseId][$key]['timings'] = $query['queryTimings']; + $results['queries'][$databaseId][$key]['errors'] = $query['queryError']; + + if (!isset($results['queries'][$databaseId][$key]['data'])) + $results['queries'][$databaseId][$key]['data'] = $query['queryData']; + else + $results['queries'][$databaseId][$key]['data'] += $query['queryData']; + + if (!empty($query['queryError'])) + $results['errorsFound'] = true; + } } // Limit the amount in order to keep things readable $results['queryCountProvided'] = 0; - foreach ($results['queries'] as $id => $database) { - $results['queries'][$id] = array_reverse(array_slice($database, -10)); - $results['queryCountProvided'] += count($results['queries'][$id]); - } - $results = array_slice($results, -10); + if (isset($results['queries'])) + { + foreach ($results['queries'] as $id => $database) { + $results['queries'][$id] = array_reverse(array_slice($database, -10)); + $results['queryCountProvided'] += count($results['queries'][$id]); + } + $results = array_slice($results, -10); + } + + //dump($results['queries']['mysql:host=localhost;dbname=hello']); return $this->results = $results; } diff --git a/src/FuzeWorks/Model/PDOTableModel.php b/src/FuzeWorks/Model/PDOTableModel.php index 2a7b9dc..c8ea837 100644 --- a/src/FuzeWorks/Model/PDOTableModel.php +++ b/src/FuzeWorks/Model/PDOTableModel.php @@ -140,6 +140,14 @@ class PDOTableModel implements iDatabaseTableModel return true; } + /** + * @todo: WRITE ABOUT FETCHMODE + * + * @param array $filter + * @param array $options + * @return array + * @throws DatabaseException + */ public function read(array $filter = [], array $options = []): array { // Determine which fields to select. If none provided, select all @@ -158,7 +166,8 @@ class PDOTableModel implements iDatabaseTableModel $this->lastStatement->execute($filter); // And return the result - return $this->lastStatement->fetchAll(PDO::FETCH_ASSOC); + $fetchMode = (isset($options['fetchMode']) ? $options['fetchMode'] : PDO::FETCH_ASSOC); + return $this->lastStatement->fetchAll($fetchMode); } public function update(array $data, array $filter, array $options = []): bool diff --git a/src/Layout/layout.tracydatabasepanel.php b/src/Layout/layout.tracydatabasepanel.php new file mode 100644 index 0000000..4b6b1f0 --- /dev/null +++ b/src/Layout/layout.tracydatabasepanel.php @@ -0,0 +1,52 @@ + + +

Queries:

+ +
+ + $queries): ?> + + + + + + + + + + + + + + +
Database:#
Time msSQL QueryRows
+ + ERROR +
explain + +
+ + + + + + + + + + +
CodeMessage
+ +
+

...and more

+ +
\ No newline at end of file diff --git a/src/Layout/layout.tracydatabasetab.php b/src/Layout/layout.tracydatabasetab.php new file mode 100644 index 0000000..ed42a4b --- /dev/null +++ b/src/Layout/layout.tracydatabasetab.php @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/test/database/DatabaseTest.php b/test/database/DatabaseTest.php index f025a27..9a51db8 100644 --- a/test/database/DatabaseTest.php +++ b/test/database/DatabaseTest.php @@ -56,12 +56,5 @@ class DatabaseTest extends DatabaseTestAbstract $this->database = Factory::getInstance()->database; } - /** - * @expectedException \FuzeWorks\Exception\DatabaseException - */ - public function testInvalidDb() - { - $this->database->get('unknown://unknown:password@unknown/database'); - } }