Implemented changes requested by FuzeWorks\Application.
- TracyBridge is now fully functional.
This commit is contained in:
parent
330d521f98
commit
6f1b1b814d
|
@ -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,
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 <abel@techfuze.net>
|
||||
* @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;
|
||||
}
|
||||
|
@ -87,40 +93,47 @@ class DatabaseTracyBridge implements IBarPanel
|
|||
$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);
|
||||
}
|
||||
$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)
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
<style class="tracy-debug">
|
||||
#tracy-debug td.nette-DbConnectionPanel-sql { background: white !important }
|
||||
#tracy-debug .nette-DbConnectionPanel-source { color: #BBB !important }
|
||||
#tracy-debug .nette-DbConnectionPanel-explain td { white-space: pre }
|
||||
#tracy-debug .fuzeworks-DbDescriptor th { background: #FDF5CE !important }
|
||||
</style>
|
||||
|
||||
<h1 title="Database">Queries: <?php
|
||||
echo $results['queryCount'], ($results['queryTimings'] ? sprintf(', time: %0.3f ms', $results['queryTimings'] * 1000) : ''); ?></h1>
|
||||
|
||||
<div class="tracy-inner">
|
||||
<table>
|
||||
<?php
|
||||
if (isset($results['queries'])):
|
||||
foreach ($results['queries'] as $database => $queries): ?>
|
||||
<tr class='fuzeworks-DbDescriptor'>
|
||||
<th>Database:</th>
|
||||
<th><?= htmlSpecialChars($database, ENT_QUOTES, 'UTF-8') ?></th>
|
||||
<th>#</th>
|
||||
</tr>
|
||||
|
||||
<tr><th>Time ms</th><th>SQL Query</th><th>Rows</th></tr>
|
||||
<?php foreach ($queries as $query): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<?php if (!empty($query['errors'])): ?>
|
||||
<span title="<?= htmlSpecialChars((isset($query['errors']['message']) ? $query['errors']['message'] : ''), ENT_IGNORE | ENT_QUOTES, 'UTF-8') ?>">ERROR</span>
|
||||
<br /><a class="tracy-toggle tracy-collapsed" data-tracy-ref="^tr .nette-DbConnectionPanel-explain">explain</a>
|
||||
<?php elseif ($query['timings'] !== 0): echo sprintf('%0.3f', $query['timings'] * 1000); endif ?>
|
||||
</td>
|
||||
<td class="nette-DbConnectionPanel-sql"><?= htmlSpecialChars($query['query'], ENT_QUOTES, 'UTF-8') ?>
|
||||
<?php if (!empty($query['errors'])): ?>
|
||||
<table class="tracy-collapsed nette-DbConnectionPanel-explain">
|
||||
<tr>
|
||||
<th>Code</th>
|
||||
<th>Message</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?= htmlSpecialChars($query['errors']['code'], ENT_NOQUOTES, 'UTF-8') ?></td>
|
||||
<td><?= htmlSpecialChars((isset($query['errors']['message']) ? $query['errors']['message'] : ''), ENT_NOQUOTES, 'UTF-8') ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
<td> <?= htmlSpecialChars(var_export($query['data'], true), ENT_QUOTES, 'UTF-8') ?> </td>
|
||||
</tr>
|
||||
<?php endforeach;
|
||||
endforeach; endif; ?>
|
||||
</table>
|
||||
<?php if ($results['queryCountProvided'] < $results['queryCount']): ?><p>...and more</p><?php endif ?>
|
||||
|
||||
</div>
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
if ($results['queryCount'] && !$results['errorsFound'])
|
||||
{
|
||||
$color = "#6ba9e6";
|
||||
}
|
||||
elseif ($results['queryCount'] && $results['errorsFound'])
|
||||
{
|
||||
$color = "#990000";
|
||||
}
|
||||
else
|
||||
{
|
||||
$color = "#aaa";
|
||||
}
|
||||
?>
|
||||
|
||||
<span title="Database">
|
||||
<svg viewBox="0 0 2048 2048"><path fill="<?= $color ?>" d="M1024 896q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0 768q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-384q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-1152q208 0 385 34.5t280 93.5 103 128v128q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-128q0-69 103-128t280-93.5 385-34.5z"/>
|
||||
</svg><span class="tracy-label"><?= ($results['queryTimings'] ? sprintf('%0.1f ms / ', $results['queryTimings'] * 1000) : '') . $results['queryCount'] ?></span>
|
||||
</span>
|
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue