Skip to content

Commit 935c60a

Browse files
committed
Added QueryLogger to log the execution of queries
1 parent 2a3086e commit 935c60a

File tree

8 files changed

+210
-24
lines changed

8 files changed

+210
-24
lines changed

src/Builder/QueryStatement.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
namespace Kir\MySQL\Builder;
3+
4+
use Kir\MySQL\QueryLogger\QueryLoggers;
5+
use PDO;
6+
use PDOStatement;
7+
8+
class QueryStatement {
9+
/** @var PDOStatement */
10+
private $statement;
11+
/** @var QueryLoggers */
12+
private $queryLoggers;
13+
/** @var string */
14+
private $query;
15+
16+
/**
17+
* @param PDOStatement $stmt
18+
* @param string $query
19+
* @param QueryLoggers $queryLoggers
20+
*/
21+
public function __construct(PDOStatement $stmt, $query, QueryLoggers $queryLoggers) {
22+
$this->statement = $stmt;
23+
$this->queryLoggers = $queryLoggers;
24+
$this->query = $query;
25+
}
26+
27+
/**
28+
* @return PDOStatement
29+
*/
30+
public function getStatement() {
31+
return $this->statement;
32+
}
33+
34+
/**
35+
* @param array $params
36+
* @return bool
37+
*/
38+
public function execute(array $params = []) {
39+
$timer = microtime(true);
40+
$response = $this->statement->execute($params);
41+
$this->queryLoggers->log($this->query, microtime(true) - $timer);
42+
return $response;
43+
}
44+
45+
/**
46+
* @param int $fetchStyle
47+
* @return array
48+
*/
49+
public function fetchAll($fetchStyle = PDO::FETCH_ASSOC) {
50+
$args = func_get_args();
51+
return call_user_func_array([$this->statement, 'fetchAll'], $args);
52+
}
53+
54+
/**
55+
* @param int $fetchStyle
56+
* @param int $cursorOrientation
57+
* @param int $cursorOffset
58+
* @return mixed
59+
*/
60+
public function fetch($fetchStyle = PDO::FETCH_ASSOC, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) {
61+
return $this->statement->fetch($fetchStyle, $cursorOrientation, $cursorOffset);
62+
}
63+
64+
/**
65+
* @param int $columnNo
66+
* @return mixed
67+
*/
68+
public function fetchColumn($columnNo = 0) {
69+
return $this->statement->fetchColumn($columnNo);
70+
}
71+
}

src/Builder/Statement.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,9 @@
55
use Kir\MySQL\Tools\AliasReplacer;
66

77
abstract class Statement {
8-
/**
9-
* @var Database
10-
*/
8+
/** @var Database */
119
private $db;
12-
/**
13-
* @var AliasReplacer
14-
*/
10+
/** @var AliasReplacer */
1511
private $aliasReplacer;
1612

1713
/**

src/Databases/MySQL.php

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,45 @@
33

44
use Kir\MySQL\Builder;
55
use Kir\MySQL\Builder\Exception;
6+
use Kir\MySQL\Builder\QueryStatement;
7+
use Kir\MySQL\Builder\Statement;
68
use Kir\MySQL\Database;
9+
use Kir\MySQL\QueryLogger\QueryLoggers;
710
use Kir\MySQL\Tools\AliasRegistry;
11+
use Kir\MySQL\Tools\PDOStatementInterceptor;
12+
use PDO;
13+
use PDOStatement;
814
use UnexpectedValueException;
915
use Kir\MySQL\Builder\RunnableSelect;
1016

1117
/**
1218
*/
1319
class MySQL implements Database {
14-
/**
15-
* @var array
16-
*/
20+
/** @var array */
1721
private static $tableFields = array();
18-
/**
19-
* @var PDO
20-
*/
22+
/** @var PDO */
2123
private $pdo;
22-
/**
23-
* @var AliasRegistry
24-
*/
24+
/** @var AliasRegistry */
2525
private $aliasRegistry;
26-
/**
27-
* @var int
28-
*/
26+
/** @var int */
2927
private $transactionLevel = 0;
28+
/** @var QueryLoggers */
29+
private $queryLoggers = 0;
3030

3131
/**
32-
* @param \PDO $pdo
32+
* @param PDO $pdo
3333
*/
34-
public function __construct(\PDO $pdo) {
34+
public function __construct(PDO $pdo) {
3535
$this->pdo = $pdo;
3636
$this->aliasRegistry = new AliasRegistry();
37+
$this->queryLoggers = new QueryLoggers();
38+
}
39+
40+
/**
41+
* @return QueryLoggers
42+
*/
43+
public function getQueryLoggers() {
44+
return $this->queryLoggers;
3745
}
3846

3947
/**
@@ -46,27 +54,29 @@ public function getAliasRegistry() {
4654
/**
4755
* @param string $query
4856
* @throws Exception
49-
* @return \PDOStatement
57+
* @return QueryStatement
5058
*/
5159
public function query($query) {
5260
$stmt = $this->pdo->query($query);
5361
if(!$stmt) {
5462
throw new Exception("Could not execute statement:\n{$query}");
5563
}
56-
return $stmt;
64+
$stmtWrapper = new QueryStatement($stmt, $query, $this->queryLoggers);
65+
return $stmtWrapper;
5766
}
5867

5968
/**
6069
* @param string $query
6170
* @throws Exception
62-
* @return \PDOStatement
71+
* @return QueryStatement
6372
*/
6473
public function prepare($query) {
6574
$stmt = $this->pdo->prepare($query);
6675
if(!$stmt) {
6776
throw new Exception("Could not execute statement:\n{$query}");
6877
}
69-
return $stmt;
78+
$stmtWrapper = new QueryStatement($stmt, $query, $this->queryLoggers);
79+
return $stmtWrapper;
7080
}
7181

7282
/**
@@ -76,7 +86,9 @@ public function prepare($query) {
7686
*/
7787
public function exec($query, array $params = array()) {
7888
$stmt = $this->pdo->prepare($query);
89+
$timer = microtime(true);
7990
$stmt->execute($params);
91+
$this->queryLoggers->log($query, microtime(true) - $timer);
8092
$result = $stmt->rowCount();
8193
$stmt->closeCursor();
8294
return $result;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
namespace Kir\MySQL\QueryLogger;
3+
4+
class ClosureQueryLogger implements QueryLogger {
5+
/** @var callable */
6+
private $fn;
7+
8+
/**
9+
* @param callable $fn
10+
*/
11+
function __construct($fn) {
12+
$this->fn = $fn;
13+
}
14+
15+
/**
16+
* @param string $query
17+
* @param float $duration Duration in seconds
18+
* @return void
19+
*/
20+
public function log($query, $duration) {
21+
call_user_func($this->fn, $query, $duration);
22+
}
23+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
namespace Kir\MySQL\QueryLogger;
3+
4+
use Psr\Log\LoggerInterface;
5+
6+
class LoggerInterfaceQueryLogger implements QueryLogger {
7+
/** @var LoggerInterface */
8+
private $logger;
9+
10+
/**
11+
* @param LoggerInterface $logger
12+
*/
13+
function __construct(LoggerInterface $logger) {
14+
$this->logger = $logger;
15+
}
16+
17+
/**
18+
* @param string $query
19+
* @param float $duration Duration in seconds
20+
* @return void
21+
*/
22+
public function log($query, $duration) {
23+
$this->logger->info(sprintf("Query %s took %0.4f seconds", $query, $duration), ['query' => $query, 'duration' => $duration]);
24+
}
25+
}

src/QueryLogger/QueryLogger.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
namespace Kir\MySQL\QueryLogger;
3+
4+
interface QueryLogger {
5+
/**
6+
* @param string $query
7+
* @param float $duration Duration in seconds
8+
* @return void
9+
*/
10+
public function log($query, $duration);
11+
}

src/QueryLogger/QueryLoggers.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
namespace Kir\MySQL\QueryLogger;
3+
4+
class QueryLoggers {
5+
/**
6+
* @var QueryLogger[]
7+
*/
8+
private $queryLoggers = [];
9+
10+
/**
11+
* @param QueryLogger $queryLogger
12+
*/
13+
public function add(QueryLogger $queryLogger) {
14+
$this->queryLoggers[] = $queryLogger;
15+
}
16+
17+
/**
18+
* @param string $query
19+
* @param float $duration
20+
* @return $this
21+
*/
22+
public function log($query, $duration) {
23+
foreach($this->queryLoggers as $queryLogger) {
24+
$queryLogger->log($query, $duration);
25+
}
26+
return $this;
27+
}
28+
}

tests/Builder/InterceptionTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
namespace Kir\MySQL\Builder;
3+
4+
use Kir\MySQL\Databases\TestDB;
5+
use Kir\MySQL\QueryLogger\ClosureQueryLogger;
6+
7+
class InterceptionTest extends \PHPUnit_Framework_TestCase {
8+
public function testQuery() {
9+
$db = new TestDB();
10+
$db->exec('USE mysql');
11+
$queries = [];
12+
$db->getQueryLoggers()->add(new ClosureQueryLogger(function ($query, $duration) use (&$queries) {
13+
$queries[$query] = 1000;
14+
}));
15+
$stmt = $db->query('SHOW TABLES');
16+
$stmt->execute();
17+
$this->assertArrayHasKey('SHOW TABLES', $queries);
18+
$this->assertEquals(1000, $queries['SHOW TABLES']);
19+
}
20+
}

0 commit comments

Comments
 (0)